在上離散數學課程時,老師給了這么一個問題:
1. 對給出的任意一個命題公式(不超過四個命題變元),使學生會利用編程軟體表示出來,并且能夠計算它在各組真值指派下所應有的真值,畫出其真值表,
的真值表;
的真值表;
2. 編程解決下列問題:
求公式
的主析取范式與主合取范式,并求公式的成真賦值和成假賦值,
這對于是個菜狗的我簡直是降維打擊,但是認認真真的學習完相關程序后還是覺得很有趣的,下面我總結一下我個人的學習程序:
1.首先要搞懂什么是主析取范式和主合取范式,成真賦值等一系列概念,
2.了解了概念后要找出實驗的突破口:求真值表,只要搞定了真值表,其他的都能夠水到渠成,
3.要解決計算的問題:對于一般帶有符號和括號的運算,我們一般將算式轉化為逆波蘭運算式的順序進行一些列運算,
什么是逆波蘭排序?
看完這兩個視頻,三分鐘搞定:
逆波蘭 - 上(中綴運算式 轉 后綴運算式)_嗶哩嗶哩_bilibili
逆波蘭 - 下(后綴運算式計算結果)_嗶哩嗶哩_bilibili
在轉化的程序中會遇到一系列的問題,例如在離散數學里有一個按位運算子“?(非)”,這處理起來很麻煩,稍后我會在文章中給出解決辦法,
4.要有簡潔大方的輸入輸出界面,這里將用到python里的easygui的庫來設計一個簡易的gui界面
pip install easygui
下面直接上代碼
1) 提示用戶輸入公式并獲取命題變元的數量
import easygui as eg
def GetGongshi(): # 通過簡易Gui讓用戶輸入命題變元個數和公式
eg.textbox(msg="您可以選擇復制下列標準邏輯運算子號進行輸入:",text="非:﹁\n合:∧\n析取:∨\n蘊含:→\n等價:?\n為了保證識別正確請按照標準符號格式輸入")
msg="請輸入命題變元的數量(1-4):"
while 1:
num = eg.enterbox(msg,title="求命題公式的真值表")
if num == "1":
Gongshi=eg.enterbox(msg="請輸入公式\n(命題變元用p表示)\n(非為'﹁',合取為'∧',析取為'∨',蘊含為'→',等價為'?')",title="求命題公式的真值表")
break
elif num == "2":
Gongshi = eg.enterbox(msg="請輸入公式\n(命題變元用p、q表示)\n(非為'﹁',合取為'∧',析取為'∨',蘊含為'→',等價為'?')", title="求命題公式的真值表")
break
elif num == "3":
Gongshi = eg.enterbox(msg="請輸入公式\n(命題變元用p、q、r表示)\n(非為'﹁',合取為'∧',析取為'∨',蘊含為'→',等價為'?')", title="求命題公式的真值表")
break
elif num == "4":
Gongshi = eg.enterbox(msg="請輸入公式\n(命題變元用p、q、r、s表示)\n(非為'﹁',合取為'∧',析取為'∨',蘊含為'→',等價為'?')", title="求命題公式的真值表")
break
else:
msg = "輸入有誤請重新輸入\n請輸入命題變元的數量(1-4):"
return Gongshi+num
這里最后return的是一個字串,num作為變元個數放在字串的最后一個元素上
2) 獲取由公式轉化成得到逆波蘭運算式
#獲取逆波蘭順序轉化后的公式
def GetNiBoLan(Gongshi): #Gongshi必須轉為串列才能使用
Fuhao = ["﹁","∨","∧","→","?","("] #右括號因為作為符號堆疊的邏輯元素不加入Fuhao識別符號
Yuansu = ["p","q","r","s"]
FuhaoZhan = [] #符號堆疊
NiBoLan = [] #逆波蘭運算式的公式(后綴運算式)
GS_pop = "" #公式堆疊的出堆疊元素
FH_pop = "" #符號堆疊的出堆疊元素
while len(Gongshi)!=0: #這里會遇到一系列問題
GS_pop = Gongshi.pop(0)
if GS_pop in Fuhao:
if len(FuhaoZhan) != 0: #在處理符號堆疊里"﹁"時為了保證邏輯上的正確我們一般處理的原則是遇到"("不pop遇到其他的pop
if FuhaoZhan[-1] == "﹁" and GS_pop!="(": #當遇到運算子時我們的"﹁"就先從堆疊里pop到運算式里去
NiBoLan.append(FuhaoZhan.pop())
FuhaoZhan.append(GS_pop)
elif GS_pop in Yuansu:
NiBoLan.append(GS_pop)
if len(FuhaoZhan) != 0:
if FuhaoZhan[-1] == "﹁": #當遇到元素時我們的"﹁"先讓元素進入運算式后再進入
NiBoLan.append(FuhaoZhan.pop())
elif GS_pop == ")":
FH_pop = FuhaoZhan.pop()
while FH_pop != "(":
NiBoLan.append(FH_pop)
FH_pop = FuhaoZhan.pop()
while len(FuhaoZhan) != 0: #符號堆疊里剩下的都pop出來就完事了
FH_pop = FuhaoZhan.pop()
NiBoLan.append(FH_pop)
return NiBoLan
這里咱們要解決逆波蘭在加減乘除運算中不會出現的按位運算子的問題:
例如給出公式:
﹁p∧q 如果按照一般的逆波蘭表示法將轉化為 pq∧﹁ 這相當于﹁(p∧q) ,這顯然是錯誤的,所以為了解決這一問題,我們采用一種針對邏輯運算子“﹁”的原則:
在符號堆疊里:如果“﹁”后面下一個入堆疊的不是“("我們就讓“﹁”提前出符號堆疊
在運算式里:如果“﹁”后面下一個入堆疊的是元素(p,q,r,s),我們先讓元素進入運算式“﹁”隨后進入
這樣就保證了最后的運算邏輯正確,按照這一原則,公式正確的運算式為p﹁q∧
3) 根據邏輯運算子的運算規則撰寫一個運算器函式
#各符號的邏輯運算
def JiSuan(*CanShu): #這里使用了收集引數,因為引數個可能為一個或者兩個:
# 下面分別是析取、合取、蘊含、等價的邏輯運算
if CanShu[0] == "∨":
if CanShu[1] == 1 or CanShu[2] == 1:
return 1
else:
return 0
if CanShu[0] == "∧":
if CanShu[1] == 0 or CanShu[2] == 0:
return 0
else:
return 1
if CanShu[0] == "→":
if CanShu[1] == 1 and CanShu[2] == 0:
return 0
else:
return 1
if CanShu[0] == "?":
if CanShu[1] != CanShu[2]:
return 0
else:
return 1
if CanShu[0] == "﹁":
if CanShu[1] == 1:
return 0
else:
return 1
4) 計算并獲取真值表
#獲得真值表
def GetValue(NiBoLan,num): # num為命題變元個數
NiBoLanList=NiBoLan.copy() #這個串列用來存盤逆波蘭運算式,方便多次輸出
Fuhao = [ "∨", "∧", "→", "?"] #逆波蘭運算式把括號去掉了,"﹁"作為特殊位運算做特例處理
Yuansu = ["p", "q", "r", "s"]
YuansuZhan = []
YS_value = {"p":0,"q":0,"r":0,"s":0,"k":0} #這里用字典來匹配變元字符的邏輯值,k為計算程序中的臨時變數,默認值都為0
NBL_pop = ""
Value = [] #這個用來存盤所有賦值情況下公式的真值(按賦值順序存盤就行)
YS_valuelist = []#儲存真值賦值
count = 0 #這個用來控制回圈
YS_temp=""
while count < 2**num:
NiBoLanList = NiBoLan.copy() #每次回圈重置逆波蘭串列值回圈利用,注意不能直接用等于,等于是參考在pop參考變數時被參考變數也會被pop
YS_value = {"p": 0, "q": 0, "r": 0, "s": 0, "k": 0} #同理字典也重置
YS_temp = bin(count)[2:] #十進制轉二進制
YS_temp = [int(each) for each in YS_temp] #二進制字串轉串列
YS_temp.reverse()
while len(YS_temp) < num: #長度小于num時其他位賦值0
YS_temp.append(0)
YS_temp.reverse()
YS_valuelist.append(YS_temp[:])
while len(YS_temp) < 4:
YS_temp.append(0) #保證標準的輸出格數pqrs
YS_value["p"] = YS_temp[0] #這樣這樣pqrs四位數的二進制數就賦值好了
YS_value["q"] = YS_temp[1]
YS_value["r"] = YS_temp[2]
YS_value["s"] = YS_temp[3]
while len(NiBoLanList) != 0:
NBL_pop=NiBoLanList.pop(0)
if NBL_pop in Yuansu:
YuansuZhan.append(NBL_pop)
elif NBL_pop in Fuhao:
YS_value["k"] = JiSuan(NBL_pop,YS_value[YuansuZhan.pop(-2)],YS_value[YuansuZhan.pop()]) #只要不是否定,從元素堆疊頂pop變元出來用字典轉換后進行邏輯計算
YuansuZhan.append("k")
elif NBL_pop == "﹁":
YS_value["k"] = JiSuan(NBL_pop, YS_value[YuansuZhan.pop()])
YuansuZhan.append("k")
Value.append(YS_value[YuansuZhan.pop(0)])
count += 1
YS_valuelist.append(Value) #將真值表和公式真值系結一起到時候pop分開就行
return YS_valuelist
在計算時也要注意“﹁”是單個元素的運算子哦
最后的 YS_valuelist是所有真值賦值和公式真值的二維陣列例如﹁p∧q回傳的是:
[[0, 0], [0, 1], [1, 0], [1, 1], [0, 1, 0, 0]]
5) 列印真值表、主析取范式、主合取范式、成真賦值
#列印真值表,主析取范式,主合取范式與成真賦值
def FormPrint(YS_valuelist, num):
value=YS_valuelist.pop() #將公式真值與真值表分開
msg="公式{0}真值表結果如下:\n".format(GongShiChar)
YuansuList=["p","q","r","s"]
#輸出真值表
for i in range(num):
msg = msg+" "+YuansuList[i]+" "
msg = msg+" 真值\n"
for i in range(len(YS_valuelist)):
for each in YS_valuelist[i]:
msg = msg+" "+str(each)+" "
msg = msg+" "+str(value[i])+" \n"
#輸出主析取范式
msg = msg+"主析取范式為:"
for each in YS_valuelist:
if value[YS_valuelist.index(each)] == 1:
msg = msg+"("
for i in range(len(each)):
if each[i] == 1:
msg = msg+YuansuList[i]
else:
msg = msg+"﹁"+YuansuList[i]
if i != len(each)-1:
msg = msg+"∧"
msg = msg+")"
if 1 in value[YS_valuelist.index(each)+1:]:
msg = msg+"∨"
#輸出主合取范式
msg = msg +"\n主合取范式為:"
for each in YS_valuelist:
if value[YS_valuelist.index(each)] == 0:
msg = msg+"("
for i in range(len(each)):
if each[i] == 0:
msg = msg+YuansuList[i]
else:
msg = msg+"﹁"+YuansuList[i]
if i != len(each)-1:
msg = msg+"∨"
msg = msg+")"
if 0 in value[YS_valuelist.index(each)+1:]:
msg = msg+"∧"
#輸出成真賦值
msg = msg+"\n成真賦值為:\n"
for i in range(len(value)):
if 1 not in value:
msg = msg +"該式為永假式"
break
if 0 not in value:
msg = msg + "該式為永真式"
break
if value[i] == 1:
msg = msg +" "+str(YS_valuelist[i])+"\n"
eg.msgbox(msg,title="輸出結果")
6) 主函式部分
#主函式部分
GongShi_And_num = GetGongshi()
GongShi = [each for each in GongShi_And_num]#字串串列化
num = int(GongShi.pop()) #最后一個元素存放的變元數量資訊,pop出來轉化為int就行
GongShiChar=GongShi_And_num[:-1] #用來存放公式字串,供以后輸出
NiBoLan = GetNiBoLan(GongShi) #公式轉逆波蘭運算式
YS_valuelist = GetValue(NiBoLan,num) #逆波蘭運算式求真值
FormPrint(YS_valuelist,num) #輸出資料
測驗結果如下:




如果代碼出現錯誤或例外,歡迎私信!
完整代碼:GitHub - Kibety/-: 求主合取范式、主析取范式、成真賦值、真值表
https://github.com/Kibety/-/tree/main
提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助檔案
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/387919.html
標籤:python
