輸入邏輯表達式,輸出真值表,支持六個基本邏輯運算
最終效果:
輸入合適公式(沒有考慮優先級,只根據括號優先),輸出時會提取其中的元素(比如這裏有A B C),並打印真值表。
算法思路:
求值的一般順序是:帶入每個元素的值,求出式子的真值,所以可以分爲兩塊: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()