python - 根据表达式打印真值表

  输入逻辑表达式,输出真值表,支持六个基本逻辑运算

最终效果

    输入合适公式(没有考虑优先级,只根据括号优先),输出时会提取其中的元素(比如这里有A B C),并打印真值表

python - 根据表达式打印真值表_第1张图片

 算法思路

    求值的一般顺序是:带入每个元素的值,求出式子的真值,所以可以分为两块:1.枚举元素值(枚举) 2.根据元素值求式子值(计算

    我认为这个的难点在于枚举,我们既要想办法提取出每一个元素,又要为其赋值(0和1)

    我们先约定好六个运算符

'''
否.......... !(非)
合取........ &(与)
可兼或...... |(或)
不可兼或.... #
由...可得... >
当且仅当.... =
'''

    并封装好这个真值表打印器 

class FindTruth:
    '''构造函数'''
    def __init__(self):

    '''输入'''
    def __In(self):

    '''枚举'''
    def __Count(self, i):

    '''计算公式结果'''
    def __Sum(self, Str):

    '''配对左右括号'''
    def __Pei(self, cur, Str):
   
    '''运算操作符'''
    def __Add(self, w, a, b = -1):

    '''输出'''
    def __Out(self):

    一,首先处理枚举:

    对于提取元素:涉及到字符串的处理以及查重,python提供了便捷的set集合来处理

    对于赋值:比如式子A|B|C,提取出ABC后,一共有从000 到 111,2^3种情况,而N个元素就有2^N种情况,显然是不能用简单的循环是实现的,这里可以用递归来处理

        a.利用set提取元素  

            set(字符串)能把字符串里每个字符提取出来,构成一个集合,而集合是不存在相同字符的,所以这里完成了筛选重复字符的工作。

            如set('A|B&C') = {'A', 'B', 'C', '|', '&'}

            seta.difference(setb)能让seta留下于setb集合中不同的元素。

            所以第二句生成了一个筛选掉运算符的元素集合,完成了元素的提取

def __In(self):
    #得到表达式Str
    self.Str = input("input your expression: \n")
    #筛出字母集合
    self.Set = set(self.Str).difference(set("()!&|>=#"))

        b.利用递归枚举赋值

            如何递归呢,这里假如有三个元素A,B,C

            递归函数有一个参数 i ,用来指定此次因该枚举哪一个元素的值,比如 i = 0, 则枚举A的值。

            首先初始化一个字典用来存储元素的值:self.dict = {'A':0, 'B':0, 'C': 0}

            第一次:{'A':0, 'B':0, 'C': 0},i = 1,所以先分别给A赋值0和赋值1,并把 i 加一,再调用枚举函数(也就是递归调用自己)。因为要调用自己两次,这里就产生了1*2 = 2个分支:

                分支1:{'A':0, 'B':0, 'C': 0},i = 1

                分支2:{'A':1, 'B':0, 'C': 0},i = 1

            第二次,i = 1,分别给B赋值0和1,在第一次的两个分支的基础上产生了2*2 = 4个分支

                分支1:{'A':0, 'B':0, 'C': 0},i = 1 

                    分支11:{'A':0, 'B':0, 'C': 0},i = 2

                    分支12:{'A':0, 'B':1, 'C': 0},i = 2

                分支2:{'A':1, 'B':0, 'C': 0},i = 1

                    分支21:{'A':1, 'B':0, 'C': 0},i = 2

                    分支22:{'A':1, 'B':1, 'C': 0},i = 2

            第三次,同理,给C赋值的时候,会产生总共4*2 = 8个分支,这时候递归枚举也结束了,我们也得到了八个枚举的结果 

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

        实现代码:

def __Count(self, i):
    '''结束条件:若最后一个元素也完成了枚举,则 打印 + 运算 '''
    if i == len(self.Lis):
        S = ''
        for l in self.Lis:
            S = S + str(self.Dic[l]) + ' '
        print(S,self.__Sum(self.Str))#调用self__Sum()函数进行运算
        return
    '''不是结尾,则赋值并调用自己,产生两个分支'''
    self.Dic[self.Lis[i]] = 0
    self.__Count(i+1)
    self.Dic[self.Lis[i]] = 1
    self.__Count(i+1)

    二,再来处理计算 __Sum(self, Str)

    计算的思路很简单,我们分别设置三个值:

        s :用来存储当前的终值,初始值设置为-1

        s0 :用来存储当前式子或元素的值

        operater :用来存储目前的操作符 

    然后一个字符一个字符地循环遍历表达式:

       例:str = 'A | (A & B) & C'   调用 __sum(self, str):

        循环1:字符 = 'A",则s = A的值

        循环2:字符 = '|',则operater = '|'

        循环3:字符 = '(',则s0 = __sum(self, ’括号内的那坨表达式‘),(这样便可利用递归简单地完成括号的处理)

                     s = (s operator s0),s0 = -1(更新s的值)

        .......

        到最后一个字符结束后,式子的值已经存储于s中了。

        实现代码:

#求公式结果
def __Sum(self, Str):
    i = 0 #字符位置
    s = -1#式子真值
    while i < len(Str):
        c = Str[i]
    #单操作符'!'要做特殊的分类处理
        if c == "!":
        #右边是字母
            if Str[i+1] in self.Set:
                c = Str[i+1]
                i = i + 1
                s0 = self.__Add('!',self.Dic[c])   
        #右边是左括号
            else:
                end = self.__Pei(i+1, Str)
                s0 = self.__Add('!', self.__Sum(Str[i+1:end+1]))
                i = end
    #字母
        elif c in self.Set:
            s0 = self.Dic[c]
    #其它运算符
        elif c in set("&|>=#"):
            operat = c
    #左括号
        elif c == '(':
            end = self.__Pei(i, Str)
            s0 = self.__Sum(Str[i+1:end])
            i = end
    #运算结果
        if s == -1:
            s = s0
            s0 = -1
        elif operat != 0 and s0 != -1:
            s1 = s
            s = self.__Add(operat, s, s0)
            operat = 0
            s0 = -1
        i = i + 1
    return s
#配对左右括号
def __Pei(self, cur, Str):
    kflag = 1  # 左括号的数目
    while not kflag == 0:
        cur = cur + 1
        if Str[cur] == '(':
            kflag = kflag + 1
        elif Str[cur] == ')':
            kflag = kflag - 1
    return cur    
#运算操作
def __Add(self, operator, a, b = -1):#b默认为-1时,表示是单操作符号' ! '
    if operator == '!':
        boo = not a
    elif operator == '&':
        boo = a and b
    elif operator == '|':
        boo = a or b
    elif operator == '#':
        boo = ((not a) or (not b)) and (a or b)
    elif operator == '>':
        boo = (not a) or b
    elif operator == '=':
        boo = ((not a) and (not b)) or (a and b)
    else:
        print("there is no such operator")
    if boo:
        return 1
    else:
        return 0

完整代码: 

# -*- coding: utf-8 -*-
'''
否.......... !
合取........ &
可兼或...... |
不可兼或.... #
由...可得... >
当且仅当.... =
'''
class FindTruth:
    def __init__(self):
        #存储字母及其真值
        self.Dic = {}
        self.Lis = []
        #输入表达式
        self.__In()
        #输出真值表
        self.__Out()
    #输入
    def __In(self):
        #得到表达式Str
        self.Str = input("input your expression: \n")
        #筛出字母集合
        self.Set = set(self.Str).difference(set("()!&|>=#"))
    #求公式结果
    def __Sum(self, Str):
        i = 0 #字符位置
        s = -1#式子真值
        while i < len(Str):
            c = Str[i]
        #单操作符'!'要做特殊的分类处理
            if c == "!":
            #右边是字母
                if Str[i+1] in self.Set:
                    c = Str[i+1]
                    i = i + 1
                    s0 = self.__Add('!',self.Dic[c])   
            #右边是左括号
                else:
                    end = self.__Pei(i+1, Str)
                    s0 = self.__Add('!', self.__Sum(Str[i+1:end+1]))
                    i = end
        #字母
            elif c in self.Set:
                s0 = self.Dic[c]
        #其它运算符
            elif c in set("&|>=#"):
                operat = c
        #左括号
            elif c == '(':
                end = self.__Pei(i, Str)
                s0 = self.__Sum(Str[i+1:end])
                i = end
        #运算结果
            if s == -1:
                s = s0
                s0 = -1
            elif operat != 0 and s0 != -1:
                s1 = s
                s = self.__Add(operat, s, s0)
                operat = 0
                s0 = -1
            i = i + 1
        return s
    #配对左右括号
    def __Pei(self, cur, Str):
        kflag = 1  # 左括号的数目
        while not kflag == 0:
            cur = cur + 1
            if Str[cur] == '(':
                kflag = kflag + 1
            elif Str[cur] == ')':
                kflag = kflag - 1
        return cur    
    #运算操作
    def __Add(self, operator, a, b = -1):#b默认为-1时,表示是单操作符号' ! '
        if operator == '!':
            boo = not a
        elif operator == '&':
            boo = a and b
        elif operator == '|':
            boo = a or b
        elif operator == '#':
            boo = ((not a) or (not b)) and (a or b)
        elif operator == '>':
            boo = (not a) or b
        elif operator == '=':
            boo = ((not a) and (not b)) or (a and b)
        else:
            print("there is no such operator")
        if boo:
            return 1
        else:
            return 0
    #输出
    def __Out(self):
        #将字母放入dict和List
        S = ''
        for c in self.Set:
            self.Dic[c] = 0
            self.Lis.append(c)
            S = S + c + ' '
        print(S, self.Str)
        self.__Count(0)
    #构造2^n的序列
    def __Count(self, i):
        #是结尾,打印 + 运算
        if i == len(self.Lis):
            S = ''
            for l in self.Lis:
                S = S + str(self.Dic[l]) + ' '
            print(S,self.__Sum(self.Str))
            return
        #不是结尾,递归赋值
        self.Dic[self.Lis[i]] = 0
        self.__Count(i+1)
        self.Dic[self.Lis[i]] = 1
        self.__Count(i+1)

if __name__ == '__main__':
    F = FindTruth()

 

你可能感兴趣的:(python,算法设计,python,真值表,离散数学,递归,set)