Как получить таблицу истинности функции (( c + ~d ) * b ) * ~ ( d + a * e )

Итак, у меня есть уравнение (( c + ~d ) * b ) * ~( d + a * e ), из которого я пытаюсь создать таблицу истинности, но я не уверен, как я мог бы даже начать с того, чтобы заставить программу вычислить, должна ли переменная быть равна true или false в соответствии с таблицей истинности. Любые предложения о том, как это сделать? Заранее спасибо.

#include<iostream>

using namespace std;

bool checkBoolTF(int a){
    bool TF;
    if(a == 0){
        TF = false;
    }
    else{
        TF = true;
    }
    return TF;
}

int main()
{
  int a,b,c,d,e;
  bool aBool, bBool, cBool, dBool, eBool;

  string equation = "(( c + ~d ) * b ) * ~( d + a * e )";
  bool equationTF;


  //LOOP TO TRUTH TABLE - FINAL VALUES
  cout << "----------------------------------------------------------------------" << endl;
  cout << "|  a  |  b  |  c  |  d  |  e  |  " << equation << "  |" << endl;
  cout << "----------------------------------------------------------------------" << endl;
    for(a=0;a<=1;a++){
        checkBoolTF(a);
            for(b=0;b<=1;b++){
                checkBoolTF(b);
                    for(c=0;c<=1;c++){
                        checkBoolTF(c);
                            for(d=0;d<=1;d++){
                                checkBoolTF(d);
                                    for(e=0;e<=1;e++){
                                        checkBoolTF(e);
                                        cout << "|  " << a << "  |  " << b <<  "  |  " << c <<  "  |  " << d <<  "  |  " << e << "  |                   "  << equationTF << "                |" << endl;
                                        cout << "----------------------------------------------------------------------" << endl;
                                    }
                            }
                    }
            }
    }
    return 0;
}

person Ron Baker    schedule 24.10.2020    source источник
comment
Вам нужно разобрать строку (( c + ~d ) * b ) * ~( d + a * e )? Или разрешено жестко кодировать формулу с некоторым преобразованием, чтобы использовать ее как выражение C++?   -  person MikeCAT    schedule 24.10.2020
comment
Его нужно разобрать. К сожалению, не могу жестко закодировать это.   -  person Ron Baker    schedule 24.10.2020
comment
Означают ли + и * ИЛИ и И соответственно?   -  person Eugene    schedule 24.10.2020
comment
Да, они делают @Eugene   -  person Ron Baker    schedule 24.10.2020
comment
@Eugene В математике это то, что они делают. ОП: Вы должны построить дерево синтаксического анализа с операциями в узлах, за исключением того, что внизу у вас есть переменные. Попробуйте сделать что-то вручную с разными простыми примерами входных данных, а затем представьте, что вы компьютер, читающий текст слева направо, и попытайтесь выяснить, как бы вы изменили или добавили дерево синтаксического анализа на каждом этапе.   -  person Anonymous1847    schedule 24.10.2020
comment
Я никогда не делал дерево синтаксического анализа, поэтому мне придется изучить его. Как вы думаете, есть ли более простой способ сделать это? Спасибо @Anonymous1847   -  person Ron Baker    schedule 24.10.2020
comment
@RonBaker Да, дерево синтаксического анализа сложнее, чем необходимо для математических выражений. Посмотрите алгоритм маневровой станции. Он предоставляет способ преобразования выражений из инфиксной нотации (то, что мы видим во входной строке) в своего рода постфиксную нотацию, такую ​​как c d ~ + b * d a e * + ~ * (которая совпадает с указанным вами выражением, если я набрал его правильно), где постфиксная форма ввод гораздо легче оценить   -  person Justin    schedule 24.10.2020
comment
@Justin Итак, я вижу, вы разделили его на разные стеки, но я не совсем уверен, как вы даже начнете вычислять его из этого.   -  person Ron Baker    schedule 24.10.2020
comment
Если у вас есть постфиксная форма выражения, например c d ~ + b * d a e * + ~ *, вычисление может быть реализовано с помощью стека. push c, push d, pop and ~, pop two and +, push b, pop two and *, push d, push a, push e, pop two and *, pop two and +, pop and ~, pop two and *   -  person Justin    schedule 24.10.2020
comment
есть ли ограничение на используемый язык? если нет, это можно легко сделать с помощью других языков, таких как python, которые могут оценивать строку. Все, что вам останется сделать, это написать рекурсивную функцию для проверки каждой возможности оценки, например: ideone.com/pgLN3I< /а>   -  person Cherubim    schedule 24.10.2020


Ответы (4)


Шаг 1: Токенизация.

enum class TokenType {
  bracket, binop, binaryop, var, literal
};
struct Token {
  TokenType type;
  char value;
 };

преобразовать строку в вектор token.

Вы еще не используете literal: это значения 0 или 1. Вы будете использовать их позже.

Напишите код, который красиво печатает вектор токенов.

Шаг 2: сделайте простое дерево.

struct Tree {
  bool is_token=true;
  Token token;
  std::vector<Tree> tree;
};

измените свой первый код, чтобы сгенерировать дерево, содержащее вектор токенов.

Напишите код, который красиво печатает его. Контрольная работа.

Шаг 3: Уменьшите дерево

Шаг 3а: Скобки

Теперь сделайте шаг сокращения; обходит вектор деревьев и генерирует одно. Он слепо копирует в вывод все, что не является скобкой. Если он видит (, он копирует все до соответствия ) (количество открытых и закрытых) в поддерево, а затем копирует это поддерево в выходные данные.

Он берет "a ( b c )" и делает его a, а затем b c в поддереве.

Напишите код, который может красиво печатать поддеревья. Контрольная работа.

Шаг 3b: вложенные скобки

Затем выполните рекурсию по созданному поддереву, чтобы его вложенные скобки также попали в поддеревья.

Шаг 3c: операторы

Далее работа над операторами. ~ легко: он поглощает следующее дерево в векторе. Поскольку + связывает проигравшего, чем *, для каждого + создайте два поддерева; один за все до, один за все после. Затем сделайте то же самое для *.

После всего этого ты поворачиваешься

a+(b+c)*~(d+e)

в

+  (a , ( * (+ (b, c), ~(+(d,e))))

Шаг 4: замена

Сопоставьте std::map, который сопоставляет переменную со значением. Возьмите копию дерева и пройдитесь по нему, заменив каждую переменную литералом, равным ее значению.

Шаг 5: оценить

Для каждого оператора оцените поддерево(я), затем примените оператор.

Результат должен быть 0 или 1.

4 и 5 можно выполнить независимо, начав с буквального выражения.

person Yakk - Adam Nevraumont    schedule 24.10.2020

Итак, у меня есть личная программа, которая реализует это для строк с формой "Ab|c(d|E)|fa"

мой полный исходный код представляет собой полный беспорядок и содержит множество других вещей, которые я пытаюсь сделать одновременно (не удается упростить выражение, обводя kmaps и прочее)

Однако я могу пройти через то, что я сделал, если это поможет

его настройка упрощает анализ с заглавными буквами, представляющими положительные, и строчными буквами, представляющими отрицание/не (), представляющими подвыражения, и [], представляющими отрицательные подвыражения, поэтому входная строка `ABC (DEF) G (HI (KK) S [ as][ge])S преобразуется в эту структуру .repr()

AND(
  A
  B
  C
  AND( D E F )
  G
  AND(
    H
    I
    AND( K K )
    S
    NAND( !A !S )
    NAND( !G !E )
  )
  S
)

и что-то вроде "Ab|cb" есть

OR(
  AND( A !B )
  AND( !C !B )
)

У меня есть объект (я называю выражение), который содержит информацию о его типе, хранящемся примерно в следующем

namespace xpr { //expression types
enum typ {
    identity_,
    negation_,
    and_,
    or_,
    nand_,
    nor_
};
}


class expression{
...

    xpr::typ type = xpr::identity_;
    char value = ' ';
    std::vector<expression> sub_expressions;
...
};

и либо символ, который является его значением, либо вектор выражений. (и или ни выражения nand)

Преобразование его в эту форму выражения выполняется с помощью вложенных конструкторов, которые продолжают передавать текущую позицию в строке, а также ее уровень.

наконец, чтобы ответить на ваш вопрос

std::vector<char> vars = trackUsed();
    // equivalent to a set but more efficent to do the sort/unique at the end one time.
removeDuplicates(vars);

const auto truth_table_width = vars.size();
const auto truth_table_size = (size_t)std::pow((size_t)2, truth_table_width); // 2^width
expression::test_type test; // abc through !a!b!c
test.reserve(truth_table_width);
for ( const auto &v : vars ) {
    // value_type is value: 6, sign: 2 so character has to fit in 6bits and sign in 2.
    // minus 'A' to make A-Z 0-26
    test.emplace_back(v - 'A', xpr::negative);
}   

for ( size_t i = 0; i < truth_table_size; i++ ) {
    for ( size_t j = 0; j < truth_table_width; j++ ) {
        // converts 0 to negative and 1 to positive
        test[j].sign = (xpr::sign_type)((i >> j) & 0x1);
    }
    bool valid = testValues(test);
    if ( valid ) {
        sum_of_products.push_back(test);
    }
}

Я создал таблицу истинности, извлекая все используемые символы, удаляя дубликаты и сортируя их. создание объекта, определенного реализацией вектора ›› увеличение значения до максимальной ширины таблицы истинности и использование бита знака этого значения для заполнения таблицы истинности - 0 = [0, 0, ... 1 = [1, 0 , ... 2 = [0, 1, ... и т. д., а затем перебирает внешний вектор и отправляет внутренний вектор в функцию-член testValues, специализированную для каждого типа выражения.

// given A true B true C true see if whole expression evaluates to true. 
bool expression::testValues(const potion::test_type& values) const {
    if ( this->is_simple() ) {
        auto val = std::lower_bound(values.begin(), values.end(),
                                    this->value,
                                    [ ](potion::val_type lhs, char rhs) -> bool {return lhs < rhs; }
        );
        if ( type == xpr::identity_ ) return (*val).sign;
        if ( type == xpr::negation_ ) return !(*val).sign;
    }
    if ( type == xpr::and_ || type == xpr::nand_ ) {
        const bool is_and = type == xpr::and_; //used to combine and and nand expressions and return the oposite val for nand
        for ( const auto& e : sub_expressions ) {
            if ( e.testValues(values) == false ) return !is_and; // short circuit- if b is false then abc is false
        }
        return is_and;
    }
    if ( type == xpr::or_ || type == xpr::nor_ ) {
        const bool is_or = type == xpr::or_; //used to combine or and nor and return the oposite val for nor
        for ( const auto& e : sub_expressions ) {
            if ( e.testValues(values) == true ) return is_or; // short circuit- if b is true then a|b|c is true
        }
        return !is_or;
    }
    throw std::runtime_error("Expression can't be simplified. Type not valid"); //should never happen
    return false;
}

Очевидно, есть тонны и тонны стандартного кода/кода синтаксического анализа, который, вероятно, не самый лучший. И если вы хотите анализировать строки, используя пользовательский язык, который вы определяете (( c + ~d ) * b ) * ~( d + a * e ), то код синтаксического анализа, очевидно, будет сильно отличаться.

В любом случае, я надеюсь, что это будет полезно для вашего проекта. TLDR: может быть немного сложнее реализовать, чем вы изначально думали. Хотя все, что я сделал, является функциональным, код не самый чистый, и его тяжелая настройка для моего конкретного случая - только получение положительных записей таблицы истинности и сохранение их в сумме произведений, которые можно обрабатывать дальше.

person BlueLightning42    schedule 24.10.2020

Для начала вы можете упростить функцию checkBoolTF примерно так:

bool checkBoolTF (int a)
{
    return !(a==0);
}

Во-вторых, кажется, что в циклах for значения, возвращаемые этой функцией, ничему не присваиваются и поэтому теряются. Итак, вы, вероятно, захотите определить вспомогательную переменную:

bool auxA = checkBoolTF(a);

и так далее..

person Ric    schedule 24.10.2020

Я хотел бы показать дополнительное, уже существующее решение. Он хорошо структурирован и прокомментирован. Он опубликован на github. См. здесь

Целью этой программы является расчет тестовых пар MCDC. Но это, конечно, также все, что вы хотите. Я не рекомендую копировать и вставлять, но вы можете прочитать и узнать, как сделать свою реализацию.

Этот код считывает именно те строки, которые вы указали. Он также создает таблицу истинности. Но это лишь малая часть всего функционала.

Итак, что вам нужно сделать, так это скомпилировать строку во что-то, что можно было бы оценить как логическое выражение.

Для этого я сначала определил входной алфавит и язык, описываемый грамматикой. Затем я создал компилятор, состоящий из сканера (лексера), парсера и генератора кода. На самом деле я создал 2 компилятора с одним интерфейсом и двумя разными серверами. Итак, 2 разных генератора кода. Один для виртуальной машины, которая может вычислять логическое выражение для любой комбинации входных переменных. И, 2-й бэкенд, создаст абстрактное синтаксическое дерево, с помощью которого я оцениваю все возможные пары тестов MCDC.

Как уже говорилось, первый генератор кода создает код операции для виртуальной машины. С помощью этой машины рассчитывается таблица истинности и с ней все минтермы. Затем используются Quine и McCluskey (две разные реализации) для минимизации логического выражения. И, наконец, оптимизированная версия метода Петрикса используется для решения задачи полного покрытия и определения минимального набора простых импликантов.

Часть MCDC, возможно, слишком сложна, чтобы объяснять ее здесь.

Но главное сообщение в том, что вам сначала нужно проанализировать вашу строку и преобразовать ее во что-то исполняемое.

Тогда вы можете оценить все, что хотите.

person Armin Montigny    schedule 25.10.2020