離散數學:驗證P,Q兩個邏輯表示式是否邏輯等價(C語言實現)
阿新 • • 發佈:2019-01-03
一、程式通過編譯,並實現兩個命題的各種邏輯運算
二、任意輸入字串P和Q邏輯表示式的合法性檢查
三、利用真值表方法驗證他們的等價性
一、演算法分析
① 求任意一個命題公式的真值表,,根據真值表驗證他們的等價性
C語言演算法:
首先是輸入一個合理的式子,然後從式子中查找出變數的個數,開闢一個二進位制函式,用來生成真值表,然後用函式運算,輸出結果,並根據結果歸類給正規化,再根據正規化驗證等價性。
函式部分,主要是3個函式,一個為真值表遞加函式,通過二進位制的加法原理遞進產生,一個為分級運算函式,這個函式是通過判斷括號,選出最內級括號的內容執行運算函式,這樣一級一級向外運算,最後得出最終結果,剩下一個為主運算函式,按照運算子號的優先順序按順序進行運算,如先將所有非運算運算完,再執行與運算。如此運算。
②
/**主運算函式**/ intMAP(char sz[N],char ccu[N],int icu[N],int h0) { int i, h = 0, j = 0, j1 = 0, j2 = 0, j3 = 0,j4 = 0, j5 = 0, i1, i2, p1 = -1, p2 = -1, s; char dt[N]; s = strlen(sz); if(s == 1) if(sz[0] == -2) //判斷是否是最後一項 return0; else return1; //1就是sz[0]的值、 else{ for(i = 0; i < s-j; i++) //先處理非 if(sz[i]== '!'){ for(i1 = 0; i1 < h0; i1++) if(sz[i+1]== ccu[i1])//將變數賦值並給P1 p1 = icu[i1]; if(sz[i+1] == -2)//如果是前運算結果的0,則P1等於0 p1= 0; if(p1 == -1)//如果是數字,直接給P1 p1= sz[i+1]; dt[j+2] = !p1;//非運算 sz[i] = j+2; j++; p1 = 0; for(i1 = i+1; i1 < s-j; i1++) sz[i1]= sz[i1+1];//將後續式子前移一項 } p1= -1; j1= j; for(i = 0; i < s-j1-2*j2; i++) // 處理與 if(sz[i]== '&'){ for(i1 = 0; i1 < h0; i1++){ if(sz[i-1]== ccu[i1])//將變數賦值並給P1 p1 = icu[i1]; if(sz[i+1]== ccu[i1])//將變數賦值並給P2 p2 = icu[i1]; } for(i2 = 2; i2 < j+2; i2++) { if(sz[i-1] == i2) //如果為前計算結果,將結果賦值並給P1 p1 = dt[i2]; if(sz[i+1] == i2) //如果為前計算結果,將結果賦值並給P2 p2 = dt[i2]; } if(sz[i-1] == -2)//如果是前運算結果的0,則P1等於0 p1 = 0; if(sz[i+1] == -2)//如果是前運算結果的0,則P2等於0 p2 = 0; if(p1 == -1) //如果是數字,直接給P1 p1 = (int)(sz[i-1]); if(p2 ==-1)//如果是數字,直接給P2 p2 = (int)(sz[i+1]); dt[j+2] = p1 && p2;//與運算 sz[i-1] = j+2; j++; j2++; p1 = -1; p2 = -1; for(i1 = i; i1 < s-j1-2*j2; i1++)//將後續式子前移兩項 sz[i1] = sz[i1+2]; i = i-1; } for(i = 0; i < s-j1-2*j2-2*j3; i++) // 處理或。 if(sz[i] == '|'){ for(i1 = 0; i1 < h0; i1++){ if(sz[i-1] == ccu[i1])//將變數賦值並給P1 p1 = icu[i1]; if(sz[i+1] == ccu[i1])//將變數賦值並給P2 p2 = icu[i1]; } for(i2=2;i2<j+2;i2++) { if(sz[i-1] == i2) //如果為前計算結果,將結果賦值並給P1 p1 = dt[i2]; if(sz[i+1] == i2)//如果為前計算結果,將結果賦值並給P2 p2 = dt[i2]; } if(sz[i-1] == -2)//如果是前運算結果的0,則P1等於0 p1 = 0; if(sz[i+1] == -2)//如果是前運算結果的0,則P2等於0 p2 = 0; if(p1 == -1)//如果是數字,直接給P1 p1 = sz[i-1]; if(p2 == -1)//如果是數字,直接給P2 p2 = sz[i+1]; dt[j+2] = p1 || p2;//或運算 sz[i-1] = j+2; j++; j3++; p1 = -1; p2 = -1; for(i1 = i; i1 < s-j1-2*j2-2*j3;i1++)//將後續式子前移兩項 sz[i1]=sz[i1+2]; i--; } for(i = 0; i < s-j1-2*j2-2*j3-2*j4;i++) // 處理蘊含。 if(sz[i] == '^'){ for(i1 = 0; i1 < h0; i1++){ if(sz[i-1] == ccu[i1])//將變數賦值並給P1 p1 = icu[i1]; if(sz[i+1] == ccu[i1])//將變數賦值並給P2 p2 = icu[i1]; } for(i2 = 2; i2 < j+2; i2++) { if(sz[i-1] == i2) //如果為前計算結果,將結果賦值並給P1 p1 = dt[i2]; if(sz[i+1] == i2) //如果為前計算結果,將結果賦值並給P2 p2 = dt[i2]; } if(sz[i-1]== -2)//如果是前運算結果的0,則P1等於0 p1 =0; if(sz[i+1]== -2)//如果是前運算結果的0,則P2等於0 p2 = 0; if(p1== -1)//如果是數字,直接給P1 p1 =sz[i-1]; if(p2== -1)//如果是數字,直接給P2 p2 = sz[i+1]; dt[j+2]= (!p1) || p2;//蘊含運算 sz[i-1]= j+2; j++; j4++; p1= -1; p2= -1; for(i1= i; i1 < s-j1-2*j2-2*j3-2*j4; i1++)//將後續式子前移兩項 sz[i1] = sz[i1+2]; i--; } for(i = 0; i < s-j1-2*j2-2*j3-2*j4-2*j5; i++) // 處理等值。 if(sz[i] == '~'){ for(i1 = 0; i1 < h0; i1++){ if(sz[i-1] == ccu[i1])//將變數賦值並給P1 p1 = icu[i1]; if(sz[i+1] == ccu[i1])//將變數賦值並給P2 p2 = icu[i1]; } for(i2 = 2; i2 < j+2; i2++) { if(sz[i-1] == i2) //如果為前計算結果,將結果賦值並給P1 p1 = dt[i2]; if(sz[i+1] == i2) //如果為前計算結果,將結果賦值並給P2 p2 = dt[i2]; } if(sz[i-1] == -2)//如果是前運算結果的0,則P1等於0 p1 = 0; if(sz[i+1] == -2)//如果是前運算結果的0,則P2等於0 p2 = 0; if(p1 == -1)//如果是數字,直接給P1 p1 = sz[i-1]; if(p2 == -1)//如果是數字,直接給P2 p2 = sz[i+1]; dt[j+2] = (!p1 || p2) && (!p2|| p1);//等值運算 sz[i-1] = j+2; j++; j5++; p1 = -1; p2 = -1; for(i1 = i; i1 <s-j1-2*j2-2*j3-2*j4-2*j5; i1++)//將後續式子前移兩項 sz[i1] = sz[i1+2]; i--; } return dt[j+1];//返回結果 } }
③
/**分級運算函式**/ intCR(char sz[N],char ccu[N],int icu[N],int h0) { int i,j,h,s,kh = 0,wz[N],a; char xs1[N],ckh[N]; //xs1用來儲存括號內的字元 ckh用來儲存括號。 s= strlen(sz); for(i = 0; i < s; i++) if(sz[i] == '(' || sz[i] == ')'){ //判斷括號 wz[kh]= i; //儲存括號位置 ckh[kh]= sz[i]; //儲存括號型別 kh++; } if(kh == 0) returnMAP(sz,ccu,icu,h0); //如果無括號,直接執行 else{ for(i= 0; i < kh; i++) if(ckh[i] == ')') //找到第一個" )" break; for(j= wz[i-1]+1,h=0; j < wz[i]; j++,h++) //儲存最內級括號中的內容 xs1[h] = sz[j]; xs1[h]= '\0'; a= MAP(xs1,ccu,icu,h0); //執行最內級括號的式子,得到結果 if(a== 1) //判斷並存儲結果 sz[wz[i-1]] = 1; else sz[wz[i-1]] = -2; for(j= wz[i-1]+1; j < s+wz[i-1] - wz[i]; j++)//將括號後內容前移 sz[j] = sz[j + wz[i] - wz[i-1]]; sz[j]= '\0'; returnCR(sz,ccu,icu,h0);//迴圈執行 } }
④
/**二進位制賦值函式**/
voidBVA(int b[N],int f){
int i;
i= f;
if(b[f] == 0) //加1
b[f]= 1;
else //進位
{
b[f]= 0;
BVA(b,--i);
}
}
⑤
/**根據正規化判斷兩個邏輯表示式是否等價**/
for(i1 = 0;i1 < h; i1++){
hq1[j] = hq[i1];
}
for(i1 = 0;i1 < h; i1++){
hq2[j] = hq[i1];
}
for(i1 = 0;i1 < h; i1++){//判斷兩個邏輯表示式是否等價
if(hq1[i1] != hq2[i1]){
flag = 0;
break;
}
else
flag = 1;
}
if(flag ==0)printf("\t\t# 您輸入的兩個邏輯表示式不等價 #\n");
else if(flag ==1)printf("\t\t# 您輸入的兩個邏輯表示式等價 #\n");
程式執行介面如下:
程式清單
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<conio.h>
#include<math.h>
#define N 50
int hq[N],i1,h=0;//定義全域性變數
/**主運算函式**/
int MAP(char sz[N],char ccu[N],int icu[N],int h0)
{
int i, h = 0, j = 0, j1 = 0, j2 = 0, j3 = 0, j4 = 0, j5 = 0, i1, i2, p1 = -1, p2 = -1, s;
char dt[N];
s = strlen(sz);
if(s == 1)
if(sz[0] == -2) //判斷是否是最後一項
return 0;
else
return 1; //1 就是sz[0]的值、
else{
for(i = 0; i < s-j; i++) //先處理非
if(sz[i] == '!'){
for(i1 = 0; i1 < h0; i1++)
if(sz[i+1] == ccu[i1])//將變數賦值並給P1
p1 = icu[i1];
if(sz[i+1] == -2)//如果是前運算結果的0,則P1等於0
p1 = 0;
if(p1 == -1)//如果是數字,直接給P1
p1 = sz[i+1];
dt[j+2] = !p1;//非運算
sz[i] = j+2;
j++;
p1 = 0;
for(i1 = i+1; i1 < s-j; i1++)
sz[i1] = sz[i1+1];//將後續式子前移一項
}
p1 = -1;
j1 = j;
for(i = 0; i < s-j1-2*j2; i++) // 處理與
if(sz[i] == '&'){
for(i1 = 0; i1 < h0; i1++){
if(sz[i-1] == ccu[i1])//將變數賦值並給P1
p1 = icu[i1];
if(sz[i+1] == ccu[i1])//將變數賦值並給P2
p2 = icu[i1];
}
for(i2 = 2; i2 < j+2; i2++) {
if(sz[i-1] == i2) //如果為前計算結果,將結果賦值並給P1
p1 = dt[i2];
if(sz[i+1] == i2) //如果為前計算結果,將結果賦值並給P2
p2 = dt[i2];
}
if(sz[i-1] == -2)//如果是前運算結果的0,則P1等於0
p1 = 0;
if(sz[i+1] == -2)//如果是前運算結果的0,則P2等於0
p2 = 0;
if(p1 == -1) //如果是數字,直接給P1
p1 = (int)(sz[i-1]);
if(p2 ==-1)//如果是數字,直接給P2
p2 = (int)(sz[i+1]);
dt[j+2] = p1 && p2;//與運算
sz[i-1] = j+2;
j++;
j2++;
p1 = -1;
p2 = -1;
for(i1 = i; i1 < s-j1-2*j2; i1++)//將後續式子前移兩項
sz[i1] = sz[i1+2];
i = i-1;
}
for(i = 0; i < s-j1-2*j2-2*j3; i++) // 處理或。
if(sz[i] == '|'){
for(i1 = 0; i1 < h0; i1++){
if(sz[i-1] == ccu[i1])//將變數賦值並給P1
p1 = icu[i1];
if(sz[i+1] == ccu[i1])//將變數賦值並給P2
p2 = icu[i1];
}
for(i2=2;i2<j+2;i2++) {
if(sz[i-1] == i2) //如果為前計算結果,將結果賦值並給P1
p1 = dt[i2];
if(sz[i+1] == i2)//如果為前計算結果,將結果賦值並給P2
p2 = dt[i2];
}
if(sz[i-1] == -2)//如果是前運算結果的0,則P1等於0
p1 = 0;
if(sz[i+1] == -2)//如果是前運算結果的0,則P2等於0
p2 = 0;
if(p1 == -1)//如果是數字,直接給P1
p1 = sz[i-1];
if(p2 == -1)//如果是數字,直接給P2
p2 = sz[i+1];
dt[j+2] = p1 || p2;//或運算
sz[i-1] = j+2;
j++;
j3++;
p1 = -1;
p2 = -1;
for(i1 = i; i1 < s-j1-2*j2-2*j3; i1++)//將後續式子前移兩項
sz[i1]=sz[i1+2];
i--;
}
for(i = 0; i < s-j1-2*j2-2*j3-2*j4; i++) // 處理蘊含。
if(sz[i] == '^'){
for(i1 = 0; i1 < h0; i1++){
if(sz[i-1] == ccu[i1])//將變數賦值並給P1
p1 = icu[i1];
if(sz[i+1] == ccu[i1])//將變數賦值並給P2
p2 = icu[i1];
}
for(i2 = 2; i2 < j+2; i2++) {
if(sz[i-1] == i2) //如果為前計算結果,將結果賦值並給P1
p1 = dt[i2];
if(sz[i+1] == i2) //如果為前計算結果,將結果賦值並給P2
p2 = dt[i2];
}
if(sz[i-1] == -2)//如果是前運算結果的0,則P1等於0
p1 = 0;
if(sz[i+1] == -2)//如果是前運算結果的0,則P2等於0
p2 = 0;
if(p1 == -1)//如果是數字,直接給P1
p1 = sz[i-1];
if(p2 == -1)//如果是數字,直接給P2
p2 = sz[i+1];
dt[j+2] = (!p1) || p2;//蘊含運算
sz[i-1] = j+2;
j++;
j4++;
p1 = -1;
p2 = -1;
for(i1 = i; i1 < s-j1-2*j2-2*j3-2*j4; i1++)//將後續式子前移兩項
sz[i1] = sz[i1+2];
i--;
}
for(i = 0; i < s-j1-2*j2-2*j3-2*j4-2*j5; i++) // 處理等值。
if(sz[i] == '~'){
for(i1 = 0; i1 < h0; i1++){
if(sz[i-1] == ccu[i1])//將變數賦值並給P1
p1 = icu[i1];
if(sz[i+1] == ccu[i1])//將變數賦值並給P2
p2 = icu[i1];
}
for(i2 = 2; i2 < j+2; i2++) {
if(sz[i-1] == i2) //如果為前計算結果,將結果賦值並給P1
p1 = dt[i2];
if(sz[i+1] == i2) //如果為前計算結果,將結果賦值並給P2
p2 = dt[i2];
}
if(sz[i-1] == -2)//如果是前運算結果的0,則P1等於0
p1 = 0;
if(sz[i+1] == -2)//如果是前運算結果的0,則P2等於0
p2 = 0;
if(p1 == -1)//如果是數字,直接給P1
p1 = sz[i-1];
if(p2 == -1)//如果是數字,直接給P2
p2 = sz[i+1];
dt[j+2] = (!p1 || p2) && (!p2 || p1);//等值運算
sz[i-1] = j+2;
j++;
j5++;
p1 = -1;
p2 = -1;
for(i1 = i; i1 < s-j1-2*j2-2*j3-2*j4-2*j5; i1++)//將後續式子前移兩項
sz[i1] = sz[i1+2];
i--;
}
return dt[j+1];//返回結果
}
}
/**分級運算函式**/
int CR(char sz[N],char ccu[N],int icu[N],int h0)
{
int i,j,h,s,kh = 0,wz[N],a;
char xs1[N],ckh[N]; //xs1用來儲存括號內的字元 ckh用來儲存括號。
s = strlen(sz);
for(i = 0; i < s; i++)
if(sz[i] == '(' || sz[i] == ')'){ //判斷括號
wz[kh] = i; //儲存括號位置
ckh[kh] = sz[i]; //儲存括號型別
kh++;
}
if(kh == 0)
return MAP(sz,ccu,icu,h0); //如果無括號,直接執行
else{
for(i = 0; i < kh; i++)
if(ckh[i] == ')') //找到第一個" )"
break;
for(j = wz[i-1]+1,h=0; j < wz[i]; j++,h++) //儲存最內級括號中的內容
xs1[h] = sz[j];
xs1[h] = '\0';
a = MAP(xs1,ccu,icu,h0); //執行最內級括號的式子,得到結果
if(a == 1) //判斷並存儲結果
sz[wz[i-1]] = 1;
else
sz[wz[i-1]] = -2;
for(j = wz[i-1]+1; j < s+wz[i-1] - wz[i]; j++)//將括號後內容前移
sz[j] = sz[j + wz[i] - wz[i-1]];
sz[j] = '\0';
return CR(sz,ccu,icu,h0);//迴圈執行
}
}
/**二進位制賦值函式**/
void BVA(int b[N],int f){
int i;
i = f;
if(b[f] == 0) //加1
b[f] = 1;
else //進位
{
b[f] = 0;
BVA(b,--i);
}
}
/**求任意一個命題公式的真值表**/
void Matrice(){
int i2,d=1,icu[N],kh=0,jg,j=0,h0; //icu[N]用於存放變數值,kh括號計數,jg存放結果
int bj=0,x=0,xq[N]; //hq[N]存放合取結果xq[N]存放析取結果
char sz[N],ccu[N],sz0[N]; //sz[N]存放式子,ccu[N]存放變數,sz0[N]也是用於存放式子
hq[0]=-1;
xq[0]=-1;
printf("\n\t\t*************************************************\n");
printf("\t\t** **\n");
printf("\t\t** (可運算真值表,主正規化,支援括號) **\n");
printf("\t\t** **\n");
printf("\t\t** 用!表示非 **\n");
printf("\t\t** 用&表示與 **\n");
printf("\t\t** 用|表示或 **\n");
printf("\t\t** 用^表示蘊含 **\n");
printf("\t\t** 用~表示等值 **\n");
printf("\t\t** **\n");
printf("\t\t*************************************************\n");
sg: printf("\t\t");
gets(sz);
strcpy(sz0,sz);
for(i1 = 0; (size_t)i1 < strlen(sz); i1++){
if(sz[i1] =='!' || sz[i1] == '&' || sz[i1] == '|' || sz[i1] == '^' || sz[i1] == '~' || sz[i1] == '(' || sz[i1] == ')' || sz[i1] >= 'a' && sz[i1] <= 'z' || sz[i1] >= 'A' && sz[i1] <= 'Z'){
if(sz[i1] == ')' || sz[i1] == '(')//儲存括號數量
kh++;
if(sz[i1] >= 'a' && sz[i1] <= 'z' || sz[i1] >= 'A' && sz[i1] <= 'Z') {
if(sz[i1+1] >= 'a' && sz[i1+1] <= 'z' || sz[i1+1] >= 'A' && sz[i1+1] <= 'Z'){
printf("\t\t# 您輸入的資料不合法!請重新輸入!\n");
goto sg;
}
for(i2 = 0; i2 < j; i2++) //判斷並儲存變數。
if(ccu[i2] == sz[i1])//去除重複變數
d = 0;
if(d == 1){
ccu[j] = sz[i1];
j++;
}
d = 1;
}
}
else{
printf("\t\t# 您輸入的資料不合法!請重新輸入!\n");
goto sg;
}
}
printf("\n\t\t*************************************************\n");
printf("\n\t\t該命題公式中的變數個數為:%d\n",j);//輸出變數個數
h0 = j;
printf("\n\t\t輸出真值表如下:\n \n"); //輸出真值表表頭
printf("\t\t");
for(i1 = 0; i1 < h0; i1++)
printf(" %c ",ccu[i1]);
printf(" ");
printf("\t\t");
puts(sz);
printf("\n");
for(i1 = 0; i1 < j; i1++) ///////先將所有的變數賦值為零。
icu[i1] = 0;
printf("\t\t");
for(i2 = 0; i2 < j; i2++)//輸出真值表前項
printf(" %d ",icu[i2]);
jg = CR(sz,ccu,icu,h0); //用函式求結果
if(jg == 0)
hq[h++] = bj;
printf("\t\t%d\n",jg);//輸出運算結果
strcpy(sz,sz0);
for(i1 = 0; i1 < (int)pow(2,j)-1; i1++){
++bj;
BVA(icu,j-1); //賦值變數
jg = CR(sz,ccu,icu,h0);
if(jg == 0)
hq[h++] = bj;
strcpy(sz,sz0); //恢復被修改的陣列。
printf("\t\t");
for(i2 = 0; i2 < j; i2++)
printf(" %d ",icu[i2]);//輸出真值表前項
printf("\t\t%d\n",jg);//輸出運算結果
}
printf("\n");
}
void Ma_Start(){
int hq1[N],hq2[N],j = 0,flag = 0;
printf("\n\t\t*************************************************\n");
printf("\t\t** **\n");
printf("\t\t# 輸入合法的邏輯表示式P #");
getchar();
Matrice();
for(i1 = 0; i1 < h; i1++){
hq1[j] = hq[i1];
}
printf("\t\t# 輸入合法的邏輯表示式Q #");
Matrice();
for(i1 = 0; i1 < h; i1++){
hq2[j] = hq[i1];
}
for(i1 = 0; i1 < h; i1++){//判斷兩個邏輯表示式是否等價
if(hq1[i1] != hq2[i1]){
flag = 0;
break;
}
else
flag = 1;
}
if(flag == 0)printf("\t\t# 您輸入的兩個邏輯表示式不等價 #\n");
else if(flag == 1)printf("\t\t# 您輸入的兩個邏輯表示式等價 #\n");
printf("\t\t** **\n");
printf("\t\t*************************************************\n");
printf("\t\t");
}
void Start(){
char s;
printf("\t\t*************************************************\n");
printf("\t\t** **\n");
printf("\t\t** 歡迎使用邏輯運算器 **\n");
printf("\t\t** **\n");
printf("\t\t*************************************************\n");
navi : printf("\n\t\t*************************************************\n");
printf("\t\t** **\n");
printf("\t\t** 您可以選擇a或b執行以下操作 **\n");
printf("\t\t** a、測試P,Q兩個邏輯表示式是否邏輯等價 **\n");
printf("\t\t** b、退出系統 **\n");
printf("\t\t** **\n");
printf("\t\t*************************************************\n");
printf("\t\t");
scanf("%c",&s);
switch(s){
case 'a' : system("cls");Ma_Start();break;
case 'b' : break;
default : printf("\t\tenter data error!\n");
}
if(s != 'b')
goto navi;
else{ //退出
printf("\t\t*************************************************\n");
printf("\t\t** **\n");
printf("\t\t** 歡迎再次使用!謝謝! **\n");
printf("\t\t** **\n");
printf("\t\t*************************************************\n");
}
}
int main(){
Start();
return 0;
}
總結:求任意一個命題公式的真值表及其主正規化就相對較難,解決了求命題公式的真值表及其主正規化的問題後,求兩邏輯表示式是否等價就變得十分簡單,關鍵在與求一個命題公式的真值表。