用程序求命题逻辑里面的主析取、主合取范式在学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;
}