C语言实现主析取范式
第一次写,不喜勿喷
(先来水一波)
先保证输入的式子是合法的 (不合法我也不会,太弱了)
难点主要是计算真值表,可以用堆栈实现,建立两个堆栈,一个储存操作符,另一个储存数值 (手写堆栈)
int OPNUM[300], OPOP[300], inum = 0, inop = 0;//储存数字和操作符的堆
储存的是int类型,在这之前有将操作符转化成对应的优先级
int charge_operator(char ch){//判断优先级
if (ch == '!') return 1;
else if (ch == '&') return 2;
else if (ch == '|') return 3;
else if (ch == '$') return 4;//单条件
else if (ch == '@') return 5;//双条件
else if (ch == '(') return 6;
else if (ch == ')') return 7;
return 10;
}
如果上一个操作符和这个操作符的优先级相同或者上一个操作符的优先级大于这个操作的优先级,则可以计算,也就是取上一个操作的操作符,和数值栈的前两个元素计算,然后再压入栈。否则操作符直接入栈,操作符堆其实是维持一个单调递增的序列。
if(cha>=OPOP[inop] && inop){//保证有操作符
int op = OPOP[inop];
int a, b, c;
a = OPNUM[inum-1], b = OPNUM[inum], inum--;//取数值栈的前两个
c = operator_A(op, a, b);//计算数值
OPNUM[inum]=c;//数值入栈
OPOP[inop] = cha;//操作符入栈
}
else OPOP[++inop]=cha;
这里主要判断一下条件非,和左括号:
条件非的判断:只需要在每个数值入栈之前看一下操作符是不是条件非
//f数组用来储存表达式
if (f[i]=='0' || f[i] == '1'){
if (OPOP[inop] == 1) {OPNUM[++inum] = (f[i] == '1'? 0:1); inop--;} //判断!的情况
else OPNUM[++inum] = f[i] - '0';
}
在条件非入栈的时候,看一下上一个操作符是不是条件非,如果是两个条件非就相互抵消了,例如!!0, 两个非就直接抵消。
如果是左括号直接入栈,如果是右括号,直接往前计算到左括号。
最后别忘记堆栈中剩余的元素
while(inop!=0){
int op = OPOP[inop];
inop--;
int a, b, c;
a = OPNUM[inum-1], b = OPNUM[inum], inum--;
c = operator_A(op, a, b);
OPNUM[inum]=c;
}
直接上代码
#include<stdio.h>
#include<bits/stdc++.h>
#define extraction '|' //析取
#define conjunction '&' //合取
#define non '!'//非
/*
双条件@
单条件$
*/
char forluma[1100]; //表示表达式和表达式的列数,数组从1开始
int n, col, row,num;//用来储存真值表中的行数和列数,num表示变量的个数
int Map[300][300],mp[300],nowval[30];//用mp维护数组,map存放真值表,nowval存当前表达式每个字母的取值
void init(){
memset(forluma,0, sizeof(forluma));
memset(Map, 0, sizeof(Map));
memset(mp, 0, sizeof(mp));
col=0,row=0;
}
void read(){
char ch;
int cnt = 0;
while(scanf("%c", &ch) != EOF){
if ((ch >='A' && ch <= 'Z') || ch == '(' || ch == ')' || ch ==extraction || ch == conjunction || ch == non
|| ch == '@' || ch == '$'){//去掉输入的空格空格
forluma[++cnt] = ch;
}
if (ch >='A'&&ch<='Z'&&!mp[ch]){//用mp维护字母
mp[ch]=++num;
}
}
n = cnt, cnt = 0;
for (int i = 1; i <= 100; i++){//离散化变量
if (mp[i])mp[i]=++cnt;
}
}
int operator_A(int ch, int x, int y){//计算式子的值
if (ch == 2) return x & y;
else if (ch == 3) return x | y;
else if (ch == 4){
if (x == 1 && y == 0) return 0;
else return 1;
}
else if(ch == 5){
if (x == y) return 1;
else return 0;
}
return 0;
}
int charge_operator(char ch){//判断优先级
if (ch == '!') return 1;
else if (ch == '&') return 2;
else if (ch == '|') return 3;
else if (ch == '$') return 4;
else if (ch == '@') return 5;
else if (ch == '(') return 6;
else if (ch == ')') return 7;
return 10;
}
char f[1200];//赋值的式子
int calculate(){
for (int i = 1; i <= n; i++){//把每个字母的值赋值给式子
if (forluma[i] >='A' && forluma[i] <= 'Z') f[i]=nowval[mp[forluma[i]]] + '0';
else f[i] = forluma[i];
}
int OPNUM[300], OPOP[300], inum = 0, inop = 0;//储存数字和操作符的堆
for (int i = 1; i <= n; i++){
if (f[i]=='0' || f[i] == '1'){
if (OPOP[inop] == 1) {OPNUM[++inum] = (f[i] == '1'? 0:1); inop--;} //判断!的情况
else OPNUM[++inum] = f[i] - '0';
}
else {
int cha = charge_operator(f[i]);
if(cha == 7){//右括号的情况
int op = OPOP[inop]; inop--;//取运算符
while(op != 6){//一直算到左括号
int a, b, c;
a = OPNUM[inum-1], b = OPNUM[inum], inum--;
c = operator_A(op, a, b);
OPNUM[inum]=c;
op=OPOP[inop--];
}
op=OPOP[inop];
if(op==1) {OPNUM[inum] = !OPNUM[inum];inop--;}//判断'('前面是不是条件非
}
else if(cha == 6) OPOP[++inop] = cha;
else if (cha == 1){//条件非的判断
if(OPOP[inop] == 1) inop--;//!!0 = 0,两个非相互抵消
else OPOP[++inop] = cha;
}
else{
if(cha>=OPOP[inop] && inop){//保证有操作符,上一个操作符的优先级大于等于这个操作的优先级
int op = OPOP[inop];
int a, b, c;
a = OPNUM[inum-1], b = OPNUM[inum], inum--;
c = operator_A(op, a, b);
OPNUM[inum]=c;
OPOP[inop] = cha;
}
else OPOP[++inop]=cha;
}
}
}
while(inop!=0){//最后清空操作符栈
int op = OPOP[inop];
inop--;
int a, b, c;
a = OPNUM[inum-1], b = OPNUM[inum], inum--;
c = operator_A(op, a, b);
OPNUM[inum]=c;
}
return OPNUM[1];
}
void dfs(int now_x){//枚举每一种情况
if (now_x == num){
row++;
for (int i = 1; i <= now_x; i++) Map[row][i] = nowval[i];
Map[row][now_x+1] = calculate();
return ;
}
nowval[now_x + 1] = 0;
dfs(now_x + 1);
nowval[now_x + 1] = 1;
dfs(now_x + 1);
}
void work(){
dfs(0);
printf("真值表:\n\t\t");
char abc[30];
int cnt = 0;
for (int i = 1; i <= 100; i++){
if (mp[i]) {printf("%c ", i);abc[++cnt]=i;}
}
printf("\n\t\t");
for(int i = 1; i<=row; i++){
for (int j = 1; j <= num + 1; j++){
printf("%d ",Map[i][j]);
}
printf("\n\t\t");
}
char xi[2000], he[2000];
int x=0, h=0;
for (int i = 1; i <= row; i++){
if (Map[i][num+1]==1){
if (x) xi[++x] = '|';
xi[++x] = '(';
for (int j = 1; j <= num; j++){
if(j!=1)xi[++x]='&';
if (Map[i][j] == 0)xi[++x]='!';
xi[++x]=abc[j];
}
xi[++x]=')';
}
else {
if (h)he[++h]='&';
he[++h]='(';
for (int j = 1; j <= num; j++){
if (j!=1)he[++h]='|';
if (Map[i][j]==1)he[++h]='!';
he[++h]=abc[j];
}
he[++h]=')';
}
}
printf("\n主析取范式:");
for (int i = 1; i <= x; i++) {
if (xi[i]=='|') printf("∨");
else if (xi[i]=='&') printf("∧");
else if (xi[i]=='!') printf("ㄱ");
else printf("%c",xi[i]);
}
printf("\n主合取范式:");
for (int i = 1; i <= h; i++){
if (he[i]=='|') printf("∨");
else if (he[i]=='&') printf("∧");
else if (he[i]=='!') printf("ㄱ");
else printf("%c",he[i]);
}
}
int main(){
printf("请输入逻辑表达式:\n");
printf("与逻辑:& 或逻辑:| 非逻辑:! 单条件:$ 双条件:@\n");
printf("请注意:表达式错误程序可能无法运行或者结果错误\n");
init();
read();
work();
}
输入:
(P$R)&(Q$!R)&(!R$(P|Q))
输出:
请输入逻辑表达式:
与逻辑:& 或逻辑:| 非逻辑:! 单条件:$ 双条件:@
请注意:表达式错误程序可能无法运行或者结果错误
真值表:
P Q R
0 0 0 0
0 0 1 1
0 1 0 1
0 1 1 0
1 0 0 0
1 0 1 1
1 1 0 0
1 1 1 0
主析取范式:(ㄱP∧ㄱQ∧R)∨(ㄱP∧Q∧ㄱR)∨(P∧ㄱQ∧R)
主合取范式:(P∨Q∨R)∧(P∨ㄱQ∨ㄱR)∧(ㄱP∨Q∨R)∧(ㄱP∨ㄱQ∨R)∧(ㄱP∨ㄱQ∨ㄱR)
如有错误欢迎指正