栈的应用——程序求主范式

          用程序求命题逻辑里面的主析取、主合取范式在学java的时候已经有这个想法了,当时还想做个GUI,不过没办法实现。昨天看了下数据结构书,讲到栈应用一节,谈到用“算符优先法”求表达式值,这个想法再次浮现,经过6、7小时的努力,乃成。


       首先谈谈我对这个算法的理解。要点是:是否计算这个运算符的,取决于下一运算符。如计算

                                       4+2*3-10/3

从左往右,遇到运算符+,不急运算,下一运算符是*,优先级高于+,所以前面的4+2不能计算。下一运算符是-,低于前一运算符*,所以*要运算。于是表达式转换成

                  4+6-10/3

再来,-的优先级低于它的下一运算符/,所以6-10不能计算。对于同级运算符,如2+3+4,规定后面的+比前面的+优先级大,这样才符合整体的“下一运算符优先高则计算”的规则。

    具体的程序实现是设置两个栈,分别储存操作符和操作数。对于读取的字符:

①是操作数,压栈;

②是操作符,比较前一操作符(即操作符栈顶元素)优先级。(后一操作符优先级)较低,计算前一操作符,结果压栈;较高,不计算前一操作符,操作符压栈。

   还有一个不大不小的问题是括号和表达式开始结束标志(这里设为'#')的处理。其实也很容易处理,如下表(行与列比较):

 

 其他(   )  #    
其他看情况低于高于高于 
低于低于(少出现)等于(除顶元素)语法不允许 
高于(不出现)语法不允许大于(少出现)大于 
#小于小于语法不允许等于 

其他操作符是指'+'、 '-'、 '* '、 '/'。

首先说一下的是表的看法:比如二行二列的意思是,其他操作符的优先级低于'(',即后一操作符优先级较高,操作符压栈。等于的情况,程序操作是除顶元素(即'(')。四行二列,说:')'跟其他操作符不会出现,这是算法的特殊性的造成的,下面是一个算术式的程序计算过程:

         (2+3*4)*4 => #(2+3*4)*4# => #(2+12)*4# => #(14)*4#

至此,'('与')'相遇,除栈顶元素'(',而之前再进行括号内计算时,')'一直没有入栈,这时进行删除栈顶元素时,会直接略过')'而读取符号*,下面继续:

          #14*4# => #56# => 56

'#'与'#'相遇,执行'('与')'相遇的操作。最后返回操作数栈顶元素,即为结果。

     这个程序我有编写,不过这篇文章的主题不是表达式的计算,粘贴过来没意义而。今天的主题是如何求主范式。

//---------------------------完美分割线--------------------------------------


     基本的思想是求得真值表,具体方法请参考离散数学书。这个问题在我脑海盘桓已久,请容我啰嗦几句我以前的想法:

①定义and(),or(),not()函数,后根据逻辑表达式相互嵌套,最终对这个多重函数赋值,根据返回值计算极小项;

②定义函数,参数包含函数指针,如and(bool (*p)(bool,bool), bool (*q)(bool,bool));实现函数的嵌套;

③使用递归实现多重循环(因为要对嵌套函数赋值)。

这些想法很难实现。

     现在的想法是将命题公式中的命题变项转换成具体布尔值,如命题公式A+B*C转换成1+1*0,这样就与一般表达式的计算挂上钩。改变A,B,C的布尔值得出相应的结果,A,B,C值和命题逻辑的返回值即组成真值表。A、B、C布尔值的储存在一个bool型的数组中,定义一个函数实现自增1(即bool value[3] = {1,0,1}自增1变成{1,1,0})

     逻辑运算符有析取、合取、取非、蕴含、等价。它们也有运算的优先顺序,比较好定义它们的优先顺序。其中有一个特殊的运算符:取非,特殊在于它是一元运算符,当它后面跟着的是比它低的运算符时,不能像其他运算符一样,在操作数栈中取前两个布尔值,进行运算。神奇的是,我一下就想到了解决办法:在取非号前加一个操作数:

              A*(!B+C) => A*(1!B+C)

所额外加的操作数可以是0也可以是1。

下面是6、7个小时的努力成果:

#include <iostream>
#include <cmath>
#include <stack>

using namespace std;
const int expLength = 100;	//输入命题公式字符容量
const int OP_NUMBER = 8;	//包括括号、#在内的操作符数目
//操作符列表,#表示表达式开始与结束
const char opList[OP_NUMBER] = {'!', '+', '*', 'c', 'e', '(', ')', '#'};
/*优先级列表(行与列比较):
*1表示大于,计算,结果压栈;
*0表示等于,将从操作符栈中去除;
*-1表示小于,操作符压栈
*2表语法不对
*/
const char opPriority[OP_NUMBER][OP_NUMBER] = 
{//       !  +  *  c  e  (  )  #
/* ! */	{-1, 1, 1, 1, 1,-1, 1, 1},
/* + */	{-1, 1,-1, 1, 1,-1, 1, 1},
/* * */	{-1, 1, 1, 1, 1,-1, 1, 1},
/* c */	{-1,-1,-1, 1, 1,-1, 1, 1},
/* e */	{-1,-1,-1, 1, 1,-1, 1, 1},
/* ( */	{-1,-1,-1,-1,-1,-1, 0, 2},
/* ) */ { 1, 1, 1, 1, 1, 2, 1, 1},
/* # */ {-1,-1,-1,-1,-1,-1, 2, 0}
};

bool boolExpCompute(char* exp, bool* value);//对每个赋值求公式真假
/********辅助函数**********/
bool isOperator(char c);			  //判断是否是操作符
int getOpPosition(char c);			  //返回操作符在opList的位置
int getPriority(char op1, char op2);              //返回op1,op2的优先顺序
void printResult(char* exp, int num);             //打印范式
void binIncrease(bool* digit, int length);        //赋值数组自增1
bool calculate(bool value1, char op, bool value2);//计算v1 op v2的值
/*******表达式合法性检验******/
bool operandCheck(char* exp, int n);    //输入字符是否合法
bool bracketMatch(char* exp);		//括号是否匹配

int main()
{
	char exp[expLength];
	int len;
	while(true)
	{
		cout << "输入以下信息(输入之时按'q'键结束程序):\n";

		cout << "命题变项数目:";
		if(!(cin >> len))//如果输入非整型
		{
			cin.clear();
			while(cin.get() != '\n');
			break;
		}
		cin.get();	//输入len值合法时清除回车符

		cout << "输入命题公式:";
		cin.get(exp,expLength).get();
		if(exp[0] == 'q' && exp[1] == '\0')//如果exp = "q"
			break;
		//exp结尾加#
		for(int i = 0;i< expLength; i++)
		{
			if(exp[i] == '\0')
			{
				exp[i] = '#';
				exp[i+1] = '\0';
				break;
			}	
		}
		printResult(exp,len);
	}

	cout << "键入任何符号,关闭窗口...";
	cin.get();
}

void binIncrease(bool* digit, int length)
{
	bool isCarry = true;
	for(int i = length -1; i>= 0;i--)
	{
		if(digit[i] && isCarry)//如果有进位,且这一位为1
		{
			digit[i] = false;
			isCarry = true;
		}
		else if(!digit[i] && isCarry)//如果有进位,且这一位为0
		{
			digit[i] = true;
			isCarry = false;
		}
		//没有进位,保持原数
		else break;
	}
}

bool boolExpCompute(char* exp, bool* value)
{
	int i;	//多数循环计数器
	bool temp1,temp2;
	char tempOp;
	stack<char> optr;	//操作符栈
	stack<bool> opnd;	//布尔值栈

	optr.push('#');		//表达式开始标志
	i = 0;

	while(exp[i] != '\0')
	{
		//如果是“非”操作符,加入某一布尔值(这里是true),将“非”转换成二元操作符
		if(exp[i] == '!')opnd.push(true);

		if(!isOperator(exp[i]))
		{	//将字母转换成布尔值
			opnd.push(value[exp[i] - 'A']);
			i++;
		}
		else 
		{
			switch(getPriority(optr.top(),exp[i]))
			{
			case -1://栈顶操作符优先级小于下一操作符
				optr.push(exp[i]);
				i++;
				break;
			case 0://优先级相等('('和')'或'#'和'#'相遇),去括号
				optr.pop();
				i++;
				break;
			case 1://栈顶操作符优先级大于于下一操作符,计算,结果压栈
				tempOp = optr.top();optr.pop();
				temp2 = opnd.top();opnd.pop();
				temp1 = opnd.top();opnd.pop();
				opnd.push(calculate(temp1,tempOp,temp2));
				break;
			default:break;
			}
		}
	}
	return opnd.top();
}

bool isOperator(char c)
{
	for(int i = 0; i< OP_NUMBER; i++)
		if(c == opList[i])return true;
	return false;
}

int getOpPosition(char c)
{
	for(int i = 0; i< OP_NUMBER; i++)
		if(c == opList[i])return i;
	return 0;
}

int getPriority(char op1, char op2)
{
	int op1_pos = getOpPosition(op1);
	int op2_pos = getOpPosition(op2);
	return opPriority[op1_pos][op2_pos];
}

bool calculate(bool value1, char op, bool value2)
{
	switch(op)
	{
		case '+':return value1 || value2;
		case '*':return value1 && value2;
		case '!':return !value2;
		case 'c':return (!value1 || value2);
		case 'e':return (value1 == value2);
		default :return true;
	}
}

bool bracketMatch(char* str)
{
	char ch;
	int i, tempCh;
	stack<char> s;
	i = 0;
	while((ch = str[i]) != '\0')
	{
		if(ch == '(' || ch == ')' )
		{
			switch(ch)
			{ 
			case '(':
				s.push(ch);break;
			case ')':
				if(s.size() == 0)return false;
				tempCh = s.top();
				if(tempCh == '(')s.pop();
				break;
			}
		}
		i++;
	}
	return s.empty();
}

bool operandCheck(char* exp, int n)
{
	for(int i = 0; exp[i] != '\0'; i++)
	{	
		if(exp[i] >= 'A' && exp[i] <= 'Z')
		{	
			if(exp[i] > 'A' + n - 1)return false;
		}
		else if(!isOperator(exp[i]))return false;
	}
	return true;
}

void printResult(char* exp, int len)
{
	int i = 0;
	wchar_t wch[3] = L"∑∏";
	int maxIndex = (int)pow(2.0,len);
	//index下标值是极小或极大项下标,true表示对应下标是极小项的,否则为极大项
	bool* index = new bool[maxIndex];
	bool* value = new bool[len];

	wcout.imbue( locale("chs"));//支持打印宽位字符
	for(i = 0; i< len; i++)
		value[i] = false;
	for(i = 0; i< maxIndex; i++)
		index[i] = false;

	if(!bracketMatch(exp))
	{
		cout << "括号不匹配,重来" << endl << endl;
	}
	else if(!operandCheck(exp,len))
	{
		cout << "含非法字符,重来" << endl << endl;
	}
	else
	{
		for(i = 0; i< maxIndex; i++)
		{
			index[i] = boolExpCompute(exp,value);
			binIncrease(value, len);
		}
		//除去exp尾部#,便于打印结果
		for(i = 0; exp[i] != '\0'; i++);
		exp[i - 1] = '\0';
		//主析取范式按格式打印
		cout << exp << " = ";		
		wcout << wch[0] ;cout << "m(";

		for(i = 0; i< maxIndex; i++)
			if(index[i]){ cout << i; break; }
		for(int j = i + 1; j< maxIndex; j++)
			if(index[j])cout << "," << j;

		cout << ")\n";
		//主合取范式按格式打印
		cout << exp << " = ";		
		wcout << wch[1] ;cout << "M(";

		for(i = 0; i< maxIndex; i++)
			if(!index[i]){ cout << i; break; }
		for(int j = i + 1; j< maxIndex; j++)
			if(!index[j])cout << "," << j;

		cout << ")\n\n";
	}

	delete [] index,value;
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值