Простой код, один класс. Доработка существующего продукта, разработка с нуля. Нужно написать класс, который будет упрощать логические выражения по правилам алгебры логики с учетом разрешения циклом. Изначально задаются зависимости в виде: dependencies = { 'Y15': 'x15', 'Y16': 'x16', 'Y17': 'x17', 'Y18': 'x18', 'Y19': 'x19', 'Y1': 'x1 & (Y17|Y2|Y5)', 'Y2': 'x2 & (Y16|Y1|Y5|Y3|Y4)', 'Y3': 'x3 & (Y15|Y2|Y4)', 'Y4': 'x4 & (Y3|Y5|Y2|Y20|Y21)', 'Y5': 'x5 & (Y2|Y1|Y22|Y4)', 'Y6': 'x6 & (Y19|Y22|Y11|Y13|Y12)', 'Y7': 'x7 & (Y8|Y20|Y9)', 'Y8': 'x8 & (Y18|Y7)', 'Y9': 'x9 & (Y21|Y7|Y10|Y14)', 'Y10': 'x10 & (Y11|Y9)', 'Y11': 'x11 & (Y10|Y6)', 'Y12': 'x12 & (Y6|Y13)', 'Y13': 'x13 & (Y6|Y12|Y14)', 'Y14': 'x14 & (Y9|Y13)', 'Y20': 'x20 &(Y4|Y7)', 'Y21': 'x21 &(Y4|Y9)', 'Y22': 'x22 &(Y5|Y6)' } Также задается критерий относительно которого надо упростить выражение до ДНФ. 1.Все нераскрытые Yi выражения, полученного на предыдущем шаге, раскрываются и результаты записываются квадратных скобках, следующих сразу за обозначением раскрываемого условия., то есть Yi[…].Обозначения раскрываемых условий слева от соответствующие квадратных скобок сохраняются на всех последующих шагах до завершения преобразований. Возникающие в процессе указанного раскрытия дизъюнктивные группы условий обеспечения заключаются в круглые скобки. 2. После указанного раскрытия на данном шаге выполняются преобразования имеющихся циклических ситуаций по правилам: - Если встречаем переменную с таким же отрицанием - это цикл (заменяем на True, если циклы отрицательные то есть ~Yi и ~Yi и на False, если Yi и Yi), то есть Yi[…Yi…]= Yi[…False…], а если ~Yi[…~Yi…]= ~Yi[…True…] - Если встречаем переменную с противоположным отрицанием - это не цикл (продолжаем раскрытие) 3. Выполняются возможные упрощающие преобразования полученного выражения по его простым логическим переменным и константам 0 и I. Эти преобразования осуществляются на основе правил алгебры логики путем вынесения за скобки общих членов и удаления из записи тождеств и конструкций, равных логическому нулю или логической единице. Пункты 1-3 в общем случае на каждом шаге выполняются последовательно. Однако в рамках данного и следующего шагов указанные преобразования могут совмещаться в целях сокращения общей записи. Еще одно подробное описание пункта 2: 1. Определение цикла Цикл распознается ТОЛЬКО когда: • Встречается полное совпадение переменной и её знака отрицания на разных уровнях раскрытия: Yi[...Yi...] → цикл (замена на False) ~Yi[...~Yi...] → цикл (замена на True) • Все остальные случаи (с разными знаками) НЕ считаются циклами: Yi[...~Yi...] → не цикл ~Yi[...Yi...] → не цикл 2. Алгоритм обработки 1. Начинаем раскрытие целевой переменной (напр. Y14[...]) 2. Для каждого вхождения Yi: Если встретили тот же Yi с тем же знаком → цикл: Yi[...Yi[...]] → внутренний Yi → False ~Yi[...~Yi[...]] → внутренний ~Yi → True Если встретили Yi с другим знаком → продолжаем раскрытие 3. Для вложенных зависимостей (Yj[...Yk[...]]): Анализируем каждое вхождение рекурсивно Проверяем совпадение знаков на всех уровнях 3. Примеры обработки 1. Прямой цикл: Y1 = x1 | Y1[...] → Y1[ x1 | False ] = x1 2. Отрицательный цикл: ~Y2 = x2 & ~Y2[...] → ~Y2[ x2 & True ] = x2 3. Не цикл (разные знаки): Y3 = x3 & ~Y3[...] → дальнейшее раскрытие (не заменяем) 4. Многоуровневый цикл: Y4[...Y5[...~Y4...]] → не цикл (разные знаки) ~Y4[...Y5[...~Y4...]] → цикл (заменяем внутренний ~Y4 на True) 4. Важные замечания • Всегда сохраняем внешнюю переменную (Yi[...]) до конца преобразований • Упрощаем выражения ПОСЛЕ обработки всех циклов • В финале удаляем все [...] но сохраняем логическую структур.