T. Таблицы истинности 3

Продолжим работу с таблицами истинности.
К сожалению, некоторые из операций Булевой алгебры не реализованы в Python.
Самые частые не стандартные операции это: импликация, строгая дизъюнкция и эквивалентность.

Обозначим их следующим образом:

  • импликация — ->;
  • строгая дизъюнкция — ^;
  • эквивалентность — ~.

Напишите программу, которая для введённого сложного логического высказывания строит таблицу истинности.

Формат ввода

Вводится логическое выражение от нескольких переменных.

Возможное содержание выражения:

  • Заглавная латинская буква — переменная;
  • not — отрицание;
  • and — конъюнкция;
  • or — дизъюнкция;
  • ^ — строгая дизъюнкция;
  • -> — импликация;
  • ~ — эквивалентность;
  • () — логические скобки.

Формат вывода

Выведите таблицу истинности данного выражения.

Примечание

Прежде, чем реализовывать новые операции, обратите внимание на их приоритет.
Рекомендуем воспользоваться знаниями полученными при решении задачи «Польский калькулятор».

Пример

Ввод

A -> B ~ C

Вывод

A B C F
0 0 0 0
0 0 1 1
0 1 0 0
0 1 1 1
1 0 0 1
1 0 1 0
1 1 0 0
1 1 1 1

Ввод

A or C ~ not (A -> B) or C

Вывод

A B C F
0 0 0 1
0 0 1 1
0 1 0 1
0 1 1 1
1 0 0 1
1 0 1 1
1 1 0 0
1 1 1 1

Решение

Развитие предыдущей задачи. Одна из самых сложных задач курса.
Авторы немного лукавят, говоря о том, что в python не реализована часть функций, так например, с точки зрения этой задачи, строгая дизьюнкция (^) – это оператор !=, импликация (->) – оператор <=, эквивалентность (~) – ==.

Кажется, что можно просто заменить все “неправильные” операторы на правильные и пусть python дальше сам разбирается что и как считать. И при таком подходе даже пройдет первый тест, но уже на втором нас ждет разочарование. Функция eval() будет аловаться на неправильный синтаксис записи A or C == not (A <= B) or C. Все дело в том, что правильная интерпретация должна выглядеть как (A or C) == (not (A <= B) or C).

А это значит, что нам или надо научиться расставлять скобки, или “есть слона по частям” и воспользоваться знаниями из задачи T. Польский калькулятор — 2 для разбора выражения на части с учетом приоритетов и вычисления этих частей с помощью функции eval().

Посмотреть код

Решение

Python
from itertools import product

OPERATORS = {
    'not': 'not',
    'and': 'and',
    'or': 'or',
    '^': '!=',
    '->': '<=',
    '~': '==',
}

PRIORITY = {
    'not': 0,
    'and': 1,
    'or': 2,
    '^': 3,
    '->': 4,
    '~': 5,
    '(': 6,
}


def parse_expression(expression, variables):
    stack = []
    result = []

    expression = expression.replace('(', '( ').replace(')', ' )')

    for item in expression.split():
        if item in variables:
            result.append(item)
        elif item == '(':
            stack.append(item)
        elif item == ')':
            while stack[-1] != '(':
                result.append(OPERATORS[stack.pop()])
            stack.pop()
        elif item in OPERATORS:
            while stack and PRIORITY[item] >= PRIORITY[stack[-1]]:
                result.append(OPERATORS[stack.pop()])
            stack.append(item)
    while stack:
        result.append(OPERATORS[stack.pop()])
    return result


def evaluate(expression, variables):
    stack = []
    for item in expression:
        if item in variables:
            stack.append(variables[item])
        else:
            if item == 'not':
                stack.append(not stack.pop())
            else:
                variable2, variable1 = stack.pop(), stack.pop()
                stack.append(eval(f'{variable1} {item} {variable2}'))  # noqa
    return int(stack.pop())


expression = input()
variables = sorted(set([item for item in expression if item.isupper()]))
parsed_expression = parse_expression(expression, variables)
table = product([0, 1], repeat=len(variables))

print(*variables, 'F')
for values in table:
    globals = {key: value for key, value in zip(variables, values)}
    print(*values, evaluate(parsed_expression, globals))
Подписаться
Уведомить о
guest
12 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Niccck
30.11.2023 16:04

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

хочу позже самостоятельно написать код без списочных выражений

сложно привыкнуть к
*[v for v in variables]
долго думал как использовать a,b,c да ещё и регулировать количество переменных

не смог понять подсказку с символами верхнего регистра
воспользовался str.lower()

не секрет почему globals??
понять различие думаю проще в рамках более сложного кода??

про eval читал отсюда

https://docs-python.ru/tutorial/opredelenie-funktsij-python/oblast-vidimosti-peremennyh/

nmalkam
Ответить на  Сергей Клочко
05.12.2023 05:12

1)
Можно, например, искать все односимвольные имена, а можно считать переменными любые слова, не совпадающие с разрешенными операторами.

круто))

2)
то есть, если данный код существует внутри большой программы, то верно будет использовать locals.
И использование locals будет по другому?

придется писать пустой словарь

Последний раз редактировалось 1 год назад nmalkam ем
Niccck
04.12.2023 15:24

1)parse_expression относится к какому либо алгоритму??

while stack and PRIORITY[item] >= PRIORITY[stack[-1]]:
2)почему если второго значения нету(значение stack не существует в PRIORITY)
то условие не проходит ???

def evaluate(expression, variables):
stack = []
3) stack может остаться не пустым после parse_expression???

4)можно попросить объяснение правильности вычисления?
я вижу что каким то образом после parse_expression мы получаем упорядоченный список, но не могу понять, как это работает.
это алгоритм??
например такое выражение, код правильно посчитает??
not A or (C ~ not (A -> B) or C)

мой код считает только для одной пары скобочек

мой код
from itertools import product

def truth_tables(tokens):
# tokens = input()
lst_tokens = tokens.split()
variables = [v for v in sorted(set(list(tokens))) if v.isupper()]
print(*[v for v in variables], ‘F’)
length = len(variables)

def br(tokens):
if ‘(‘ in list(tokens):
brackets = tokens[tokens.index(‘(‘):tokens.index(‘)’) + 1].strip(‘()’).split()
brackets = translator(brackets)
res_lst_tokens = tokens[:tokens.index(‘(‘)].split()
res_lst_tokens.append(brackets)
res_lst_tokens.extend(tokens[tokens.index(‘)’) + 1:].split())
lst_tokens = res_lst_tokens

def translator(expression):
operators = [‘not’, ‘and’, ‘or’, ‘^’, ‘->’, ‘~’]
for operation in operators:
if operation in expression:
match operation:
case ‘not’:
for _ in range(expression.count(operation)):
index = expression.index(operation)
bracket = ‘(‘ + expression.pop(index) + ‘ ‘ + expression.pop(index) + ‘)’
expression.insert(index, bracket)
case ‘and’ | ‘or’:
for _ in range(expression.count(operation)):
index = expression.index(operation)
bracket = ‘(‘ + expression.pop(index – 1) + ‘ ‘ + expression.pop(
index – 1) + ‘ ‘ + expression.pop(index – 1) + ‘)’
expression.insert(index – 1, bracket)
case ‘^’:
index = expression.index(operation)
a = expression.pop(index – 1)
expression.pop(index – 1)
b = expression.pop(index – 1)
strict_disjunction = ”.join([‘(not ‘ + a + ‘ and ‘ + b + ‘ or ‘ + a + ‘ and ‘ + ‘ not ‘, b, ‘)’])
expression.insert(index – 1, strict_disjunction)
case ‘->’:
index = expression.index(operation)
a = expression.pop(index – 1)
expression.pop(index – 1)
b = expression.pop(index – 1)
impl = ”.join([‘(‘, ‘not ‘, a, ‘ or ‘, b, ‘)’])
expression.insert(index – 1, impl)
case ‘~’:
index = expression.index(operation)
a = expression.pop(index – 1)
expression.pop(index – 1)
b = expression.pop(index – 1)
equ = ”.join([‘(not ‘ + a + ‘ and ‘ + ‘ not ‘ + b + ‘ or ‘ + a + ‘ and ‘ + b + ‘)’])
expression.insert(index – 1, equ)

res = ”
for _ in expression:
res += _
return res

if ‘(‘ in list(tokens):
brackets = tokens[tokens.index(‘(‘):tokens.index(‘)’) + 1].strip(‘()’).split()
brackets = translator(brackets)
res_lst_tokens = tokens[:tokens.index(‘(‘)].split()
res_lst_tokens.append(brackets)
res_lst_tokens.extend(tokens[tokens.index(‘)’) + 1:].split())
lst_tokens = res_lst_tokens

to_eval = translator(lst_tokens)

for val in product([False, True], repeat=length):
d = {k: int(v) for k, v in zip(variables, val)}
print(*[v for v in d.values()], int(eval(to_eval, d)))

def main():
string = input()
truth_tables(string)

if __name__ == ‘__main__’:
main()
# not A or (C ~ not (A -> B) or C)

nmalkam
Ответить на  Сергей Клочко
05.12.2023 05:51

1, 4)
переночевал с моими вопросами в голове. понял что ответ на мой вопрос будет – стек. но я не это имел в виду. я имею в виду ….. каким образом можно прийти к этому? Опыт?

я вижу что у вас есть условие при котором мы вносим упорядочено значения из скобок
я вижу что у вас есть условие при котором в зависимости от приоритета операции вы вносите упорядочено значения 
я вижу что вы можете полностью все операторы добавить в result 

после этого вы пользуетесь, в своё время нашумевшим, польским калькулятором

просто я не понимаю, как к этому прийти

как написать эти условия??)))
возможно это очевидно, но ведь как-то эту очевидность нужно внести в программу

2) спасибо за ссылку и за знания))

4)

Этот прием в программировании сложных процессов используется очень и очень часто, поэтому стоит изучить его повнимательнее.

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

https://leetcode.com/tag/stack/

полагаю у меня ещё есть возможность потренироваться))
Может быть вы подскажите другие ресурсы??

mka
mka
22.04.2024 16:54

#вариант решения без стека, тесты проходит отлично:
from itertools import product

text = input().replace(‘(‘, ‘( ‘).replace(‘)’, ‘ )’)
param = [word for word in sorted(set(text.split())) if word.isupper()]
count = len(param)
text = text.replace(‘( ‘, ‘((((‘).replace(‘ )’, ‘))))’)
text = “(((” + “))) == (((“.join(text.split(‘~’)) + “)))”
text = “((” + “)) <= ((“.join(text.split(‘->’)) + “))”
text = “(” + “) != (“.join(text.split(‘^’)) + “)”
print(” “.join(param), ‘F’)
for vars in product([0, 1], repeat=count):
dic = {k: v for (k, v) in zip(param, vars)}
print(” “.join([str(var) for var in vars]), int(eval(text, dic)))

mka
mka
22.04.2024 17:02

from itertools import product

text = input().replace(‘(‘, ‘( ‘).replace(‘)’, ‘ )’)
param = [word for word in sorted(set(text.split())) if word.isupper()]
count = len(param)
text = text.replace(‘( ‘, ‘((((‘).replace(‘ )’, ‘))))’)
text = “(((” + text.replace(‘~’, “))) == (((“) + “)))”
text = “((” + text.replace(‘->’, “)) <= ((“) + “))”
text = “(” + text.replace(‘^’, “) != (“) + “)”
print(” “.join(param), ‘F’)
for vars in product([0, 1], repeat=count):
dic = {k: v for (k, v) in zip(param, vars)}
print(” “.join([str(var) for var in vars]), int(eval(text, dic)))

mka
mka
Ответить на  Сергей Клочко
22.04.2024 21:51

согласен читабельность пока у меня хромает, борюсь)

Хотел просто показать принцип, что сделать замену скобок возле операторов в зависимости от приоритета, причем начальные скобки заменить на самое большое число скобок, можно этот момент закодить не прописывая скобки самому, а беря приоритет из словаря как у вас в решении + еще по хорошему сделать пробежку и почистить лишние скобки(но решил что для этой задачи избыточное украшательство).

В любом случае на это решение меня натолкнуло ваше объяснение проблематики приоритетов, за что опять таки сердечно благодарю!)