用C语言求解合式公式的主合取范式和主析取范式

本文档展示了一个C语言程序,用于处理命题逻辑表达式,包括逆波兰表示法、二进制枚举和真值计算。程序实现了从输入的命题公式到后缀表达式的转换,并通过二进制枚举法找出所有可能的真值组合,进一步计算得到主析取范式和主合取范式。
摘要由CSDN通过智能技术生成

我最爱的冯老师在离散课上出了一个附加题。
为了能够加更多的学分也是出于自己的好奇,我决定尝试一下。
思路:二进制枚举+逆波兰表达式

#include<stdio.h>
#include<string.h>
#define T 1
#define F 0

struct Stack{
    int top;
    char arr[80];
}number={-1},symbol={-1};
//number储存后缀表达式结果
//symbol 操作符栈,用来保存操作符 

int book[26];//记录变元元素和个数 
//a对应下标为0
//b对应下标为1
//c对应下标为2
//以此类推 
int num=0;//记录变元的个数 
int alphabet_true_false[26];
/*
上面的book数组每个元素存放的是
该下标对应的字母是否存在

类似的这个alphabet_true_false存放的是
该下标对应的字母变元的真值(true or false) 
当然,只有book[i]不为零的情况下该数组才会奏效 
*/ 
int enum_result[80];
/*
如果二进制值为110110010
那么该数组从前往后数每个元素分别为:
1 1 0 1 1 0 0 1 0
说白了就是把十进制转为二进制01值并存到enum_result里罢了 
*/



char str_copy[80];//输入的字符串的副本 

void count_num(char str[]){//记录变元的个数,用全局变量num储存 
	int i,len=strlen(str);
	for(i=0;i<len;i++){
        if(str[i]>='a'&&str[i]<='z'){
            if(book[str[i]-'a']==0){ 
				book[str[i]-'a']=1;    
				++num; 
			} 
    	}      
    }
}
 
void scan_string(char str[]){
	//输入小写字符
	//例如!(p|q)>(q&!r|(r>p)) 
	int i;
    gets(str);
    strcpy(str_copy,str);
    
    count_num(str);//记录变元的个数,用全局变量num储存 
    int len=strlen(str);
    for(i=0;i<len;i++){
    	//对操作符进行优先级处理
		//把字符数组的操作符改为计算机可判断优先级的特别字符 
        switch(str[i]){
            case '!':str[i]=')'+5;break;
            case '&':str[i]=')'+4;break;
            case '|':str[i]=')'+3;break;
            case '>':str[i]=')'+2;break;
            case '=':str[i]=')'+1;break;
        }        
    }
}

void reverse_polish(char s[]){
	//把命题公式改为后缀表达式
	//并把后缀处理的结果保存在number结构体里
	//number结构体里原本存放的是变元,就是那些字母。
	//symbol里存放的是操作符, 经过后缀处理完后symbol里应该为空。 
	//经过后缀处理完后就会把symbol里的操作符也转移到了number里 
    int i,len=strlen(s);
    for(i=0;i<len;i++){
        if(s[i]>='a'&&s[i]<='z'){//如果是命题变元就直接入number栈 
            number.arr[++number.top]=s[i];          
        }else if(s[i]>')'&&s[i]<=5+')'){
            if(symbol.top==-1||symbol.arr[symbol.top]==')'){
                ++symbol.top;
                symbol.arr[symbol.top]=s[i];
            }else if(s[i]>=symbol.arr[symbol.top]){
                ++symbol.top;
                symbol.arr[symbol.top]=s[i];
            }else{
                while(symbol.top!=-1&&s[i]<symbol.arr[symbol.top]){
                    ++number.top;
                    number.arr[number.top]=symbol.arr[symbol.top];
                    --symbol.top;
                }
                --i;
            }
        }
		else if(s[i]=='('||s[i]==')'){
		    if(s[i]=='('){
                ++symbol.top;
                symbol.arr[symbol.top]=s[i];
            }else{
                while(symbol.arr[symbol.top]!='('){
                    ++number.top;
                    number.arr[number.top]=symbol.arr[symbol.top];
                    --symbol.top;              
                }
                if(symbol.top!=-1)--symbol.top;
            }
        }       
    }
    while(symbol.top!=-1){
        ++number.top;
        number.arr[number.top]=symbol.arr[symbol.top];
        --symbol.top;                                
    }
}

int check(int num){//判断enum_result[]的二进制情况组成的是极小值(return 1)还是极大值(return 0)
	struct Stack ans={-1};
	int i,k=0,len=0,temp;
	for(i=0;i<26;i++){
		if(book[i]){
			++len;
			alphabet_true_false[i]=enum_result[k++];//enum_result里只有真或假(1,0) 
			//这一步类似于变元p被赋值为false(0)
			//或者t被赋值为true(1)
		}
	}
	for(i=0;i<=number.top;i++){
		if(number.arr[i]>='a'&&number.arr[i]<='z'){
			++ans.top;
			ans.arr[ans.top]=alphabet_true_false[number.arr[i]-'a'];
		}else{
			switch(number.arr[i]){
				case '.':
					ans.arr[ans.top]=!ans.arr[ans.top];
					break;
				case '-':
					ans.arr[ans.top-1]=ans.arr[ans.top-1]&ans.arr[ans.top];
					--ans.top;
					break;
				case ',':
					ans.arr[ans.top-1]=ans.arr[ans.top-1]|ans.arr[ans.top];
					--ans.top;
					break;
				case '+':
					ans.arr[ans.top-1]=!ans.arr[ans.top-1]|ans.arr[ans.top];
					--ans.top;
					break;
				case '*':
					ans.arr[ans.top-1]=
									 (!ans.arr[ans.top-1]|ans.arr[ans.top])
									&(!ans.arr[ans.top]|ans.arr[ans.top-1]);
					--ans.top;
					break;
			}
		}
	}
	printf("%4d\n",ans.arr[0]); 
	return ans.arr[0];
}

void binary_enumeration(int num){
	//二进制枚举 2^num次 遍历所有情况 
	//
	int i,j;
	int conjunction_top=-1,disjunction_top=-1;/*也把下面的两个数组看作栈,
	使用这两个top变量指向的就是栈顶元素的下标 */ 
	int result_conjunction[80]={0};//储存的是主合取结果 
	int result_disjunction[80]={0};//储存的是主析取结果 
	
	
	for(i=0;i<26;i++)
        if(book[i])printf("%4c|",i+'a');
	putchar(' '),puts(str_copy);
	
	//二进制枚举用的是两层循环
	//最外面的一层循环如果num是4
	//则1<<num的二进制就为(1111),十进制就是15
	//说白了就是循环15次,让变量i自增15次 
	//而里面的循环则是:
	//读取变量i的二进制中的第j位比特值
	//假如变量i位5,则i的二进制就是101
	// 
	for(i=0;i<(1<<num);i++){// 
		memset(enum_result,0,num);
		for(j=0;j<num;j++){//这一层的循环说白了就是把变量j的值转换为对应的二进制,并把二进制值储存到enum_result数组里 
			if(i&(1<<(num-j-1))){
				enum_result[j]=T;
			}else{
				enum_result[j]=F;
			}
			printf("%4d|",enum_result[j]);
		}
		//里层循环完之后现在的enum_result每一位里存放的就是j的二进制对应的值 
		if(check(num)){
			result_disjunction[++disjunction_top]=i;//主析取十进制下标 
		}else{
			result_conjunction[++conjunction_top]=i;//主合取十进制下标 
		}
	}
	puts("\n主析取范式为:");
	for(i=0;i<=disjunction_top;i++){
		printf("m%d%s",result_disjunction[i],i^disjunction_top?"&":"");
	}
	puts("\n主合取范式为:");
	for(i=0;i<=conjunction_top;i++){
		printf("M%d%s",result_conjunction[i],i^conjunction_top?"|":"");
	}
}

void show(char str[]){
	int i; 
	puts("\n后缀表达式为:");
    for(i=0;i<=number.top;i++){
    	switch(number.arr[i]){
            case ')'+5:number.arr[i]='!';break;
            case ')'+4:number.arr[i]='&';break;
            case ')'+3:number.arr[i]='|';break;
            case ')'+2:number.arr[i]='>';break;
            case ')'+1:number.arr[i]='=';break;
        } 
        printf("%c",number.arr[i]);
    }
    
    puts("\n使用的命题变项:");
    for(i=0;i<26;i++)
        if(book[i])printf("%c ",i+'a');
}

/*
           优先级
非  :!      5
合取:&       4
析取:|       3
蕴涵:>       2
等价:=       1
*/

int main()
{
    int i=0;
    char str[80];
    scan_string(str);//输入小写字符
	reverse_polish(str);//逆波兰表达式 去括号 
    binary_enumeration(num);//把变元个数num传入函数
	//有num个变元就有2^num个情况,对每一个二进制情况经进行heck(), 
	show(str);
    return 0;
}

大一,寒假时为了蓝桥杯学了不少算法和数据结构的知识。
写了将近4个小时。又复习了一遍后缀表达式。
算法方面没有什么不理解的地方,大部分时间都是细节上的错误,
比如

for(i=0;i<=number.top;i++){
	switch(number.arr[i]){
	...
	}
}

写成

for(i=0;i<=number.top;i++){
	switch(number.arr[number.top]){//循环的是i,我却循环成了栈顶元素。这个地方卡了差不多一个小时。可能是因为太晚了脑子有点糊涂
	...
	}
}

程序跑通了调的没bug了之后拿给老师看,老师理所当然的给出了肯定的评价;
就是不知道她什么时候能把我这个分给加上。

以下是运行结果:

为了配的上我心中的那个执念
lonely but only study
lonely but only suppress

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值