重言式的判别
2014-12-30 11:35
148 查看
小编今年大二,数据结构让搞个重言式的判别,感觉写得很成功,传上来给大家分享。
下面是程序设计书
可能看一下比较好理解
一、需求分析
1.一个逻辑表达式如果对于其变元的任一种取值均为真,则成为重言式;反之,如果对于其变元的任一种取值都为假,则称为矛盾式,然而,更多的情况下,既非重言式,也非矛盾式。写一个程序通过真值表判别一个逻辑表达式属于上述哪一类。基本要求如下:
(1)逻辑表达式从终端输入,长度不超过一行。逻辑运算符包括“|”、“&”和“~”,分别表示或、与和非,运算优先程度递增,但可有括号改变,即括号内的运算优先。逻辑变元为大写字母。表达式中任何地方都可以含有多个空格符。
(2)若是重言式或矛盾式,可以只显示“True Forever”或“False Forever”,否则显示“Statisfactible”以及变量名序列,与用户交互。若用户对表达式变元取定一组值,程序就求出并显示逻辑表达式的值。
(3)本程序先使用栈将逻辑表达式的变量进行存储,然后将栈中的元素作为二叉树的结点结构,然后根据优先级读取表达式建立二叉树,并通过逐个判断根实现对重言式的判别。
2. 程序执行的命令
(1)输入逻辑表达式 (2)判断表达式是重言式还是矛盾式 (3)若既不是重言式也不式矛盾式,则对变元取定值,并显示逻辑表达式的值 (4)结束
3.测试数据
(1) (A|~A)&(B|~B)
(2) (A&~A)&C
(3) A|B|C|D|E|~A
(4) A&B&C&~B
(5) (A|B)&(A|~B)
(6) A&~B|~A&B;
[预计输出结果]
True Forever
False Forever
True Forever
False Forever
Statisfactible
Statisfactible
二. 概要设计
1.总体的流程:在main函数中输入式子,首先对式子中不同时间的个数n进行判别。再根据n来建立一个叶子节点为n的满二叉树,便于用来记录对于各种不同事件可能取值的全排列。再把这些可能取到的数据一次进行检测。从而达到判别重言式的目的。
2. 为实现上述程序功能,需要两个抽象数据类型,如下:
(1)二叉树模型
ADT tree
{数据对象D:D={ai | ai∈ElemSet, i=1,2,…,n, n≥0}
数据关系R:若D为空集,则称为空二叉树。
若D仅含一个数据元素,则R为空集,否则R={H},H满足关系:
(1) T中存在唯一的一个结点,它没有前驱,称为树的根,用root表示,在集合D中用a1表示;
(2) 若D中元素个数大于1,对于任意的数据元素aj∈D且j≥2,存在唯一的数据元素ai∈D,有<ai, aj>∈H;
(3) 若D中元素个数大于1,对于任意的数据元素ai∈D,仅存在不多于2个数据元素aj,ak∈D且j, k≥i,有< ai, aj >, < ai, ak >∈H,其中,若j<k,则称aj为ai的左孩子节点,ak为ai的右孩子节点。
(4) 对于任意的数据元素aj∈D且j≥2,存在D1 D,D1={ | ∈ElemSet, k=1,2, …,m, m≥0},有唯一的Pj={< , >, < , >, …, < , >, < , >},这个集合Pj表示从根结点到结点aj的一条路径。
基本操作P:
void createTree(int l)
操作结果:构造二叉树。
void addTree(tree * p, intlength)
前提条件:二叉树已建立。
操作结果:根据形参length来对p进行添加树节点,构造一个深度为length的二叉树。
}tree
(2)识别表达式使用的栈定义
ADT SqStack
{数据对象:D={ai| aiÎElemSet,i=1,2,...,n, n> 0}
数据关系:R1={ <>ai-1 ,ai>|ai-1, aiÎD, i=1,2,...,n }
约定an端为栈顶,ai端为栈底。
基本操作P:
int InitStack(SqStack * s)
操作结果:构造一个栈。
char GetTop(SqStack * s)
前提条件:栈已经存在。
操作结果:返回栈顶元素。
void Push(SqStack * s, chare)
前提条件:栈已经存在
操作结果:将元素e压入栈中。
char Pop(SqStack *s)
前提条件:栈已经存在,且栈非空。
操作结果:将栈顶元素充当返回值返回。
} ADT SqStack
3.本程序主要分为四个模块
(1)主函数模块
输入共有几组测试数据,输入每组测试数据,调用各种函数进行测试。
(2)判断数据不同事件个数模块
对于每组测试数据,计算出不同时间的个数,充当返回值返回。
(3)二叉树的建立模块
对与每组不同的测试数据以及测试数据的不同事件的个数。求出该测试数据的对于每个不同事件可能取值的全排列。全排列数据存放在二叉树组中。对于测试后的结果,进行最后的判别。
(4)测试数据模块
把每次的测试数据的全排列传递给该模块,该模块利用栈的性质,来进行测试,判断是为真还是为假。
4.程序各个模块间的关系
三. 详细设计
1.栈类型和二叉树类型
#defineSTACK_INIT_SIZE 100 //定义栈的初始长度
#defineSTACKINCREMENT 10 //当栈长度不足时的增量
typedefstruct //栈的结构体
{
char * base; //栈的底部指针
char * top; //栈的头指针
int stacksize; //栈的长度
}SqStack;
intInitStack(SqStack * s) //栈的初始化
{
s->base = malloc(STACK_INIT_SIZE *sizeof(char)); //为栈分配初始长度
if (!s->base) //若失败就退出
{
exit(1);
}
s->top = s->base;
s->stacksize = STACK_INIT_SIZE;
return 1;
}
charGetTop(SqStack * s) //获得栈顶元素
{
char e;
if (s->top == s->base)
{
exit(1); //若栈为空,则退出
}
e = *(s->top - 1);
return e;
}
void Push(SqStack* s, char e) //进栈
{
if (s->top - s->base >=s->stacksize) //若空间不足,则增加栈的空间
{
s->base = realloc(s->base,(s->stacksize + STACKINCREMENT) * sizeof(char));
if (!s->base)
{
exit(1);
}
s->top = s->base + s->stacksize;
s->stacksize += STACKINCREMENT;
}
*s->top = e;
s->top++;
}
char Pop(SqStack*s) //出栈
{
char e;
if (s->base == s->top)
{
exit(1); //若栈为空,则退出
}
s->top--;
e = *s->top;
return e;
}
//对于main函数中输入的式子,通过getKind函数得到不同事件的个数n,由于每个事件只有两种情况:真或假,我们用0和1来表示。这就是tree结构体重的letter,它用来表示该事件是否为真。
typedef structtree //定义二叉树节点的结构体
{
int letter;
int or; //用于判别在全排列中是否经过该节点
struct tree *right, *left; //该节点的左右孩子
struct tree *par; //该节点的父节点
}tree;
tree root;
void addTree(tree* p, int length) //根据给予的二叉树的深度在根节点的基础上添加树节点
{
if (length > 0)
{
tree *t1, *t2;//t1,t2分别为当前节点的左右子树。
t1 = malloc(sizeof(tree));//为子树申请空间
t2 = malloc(sizeof(tree));
t1->letter = 0;//对于每一个事件,都有两种情况0或1,所以有两个子节点,表示该事件为0和为1的两种情况,从而达到全排列。
t2->letter = 1;
t1->or = t2->or = 0;
//在后续的全排列中,用or来记录该节点是否测试过。
t1->par = p;
//建立父子关系。
t2->par = p;
p->left = t1;
p->right = t2;
addTree(p->left, length - 1);
addTree(p->right, length - 1);
//递归遍历。
}
}
voidcreateTree(int l) //建立二叉树
{
int result; //存放每次EvaluateExpression函数运行的结果
int resultCount[50]; //将每次EvaluateExpression函数运行的结果收集在一起,以判别是否为重言式
int indexOfresultCount = 0; //resultCount的下标,用于遍历
int i = 0;
int length = l; //l为二叉树的层数
int sum; //存放一共有多少测试数据
int temp; //在全排列被个事件的发生情况时临时存放长度
int index; //存放可能结果数组num的下标
tree *p; //addTree函数中用于构建二叉树的临时指针
tree *t; //在全排列被个事件的发生情况时临时指针
p = &root;
root.letter = 0;
root.or = 0;
addTree(&root, length);
temp = length;
sum = pow(2, length);
//把每种情况输入到EvaluateExpression函数中去。
while (sum--)
{
t = &root;
temp = length;
index = 0;
while (temp--)
{
if (t->left->or == 0)
{
t = t->left;
permutation[index++] = t->letter;//permutation数组用来记录每次全排列的可能情况,为全局变量。
}
else
{
t = t->right;
permutation[index++] = t->letter;
}
t->or = 1;
//如果该节点已经1测试了,将它的or赋1.
//检验,从叶子节点开始,如果左右孩子的or为1,则将父节点的or也设为1.
while (t = t->par)
{
if (t->left->or == 1 &&t->right->or == 1)
{
t->or = 1;
}
else
{
break;
}
//将这种结果给予到EvaluateExpression函数中去。
result = EvaluateExpression();
//将每次的测试结果放入数组中,以便于下面重言式的最后判别。
resultCount[indexOfresultCount++] = result;
}
temp = indexOfresultCount; //判断是否为重言式
for (i = 0; i < temp; i++)
{
if (resultCount[i] == 1)//检测到一个数据测试成立,就将resultCount的总数-1。
indexOfresultCount--;
}
if (indexOfresultCount == 0)//resultCount数为0,说明对于所有的测试数据都成立,即为重言式
{
printf("True forever");
}
else if (indexOfresultCount == temp)
{
printf("False forever");
}
else
{
printf("Satisfactible");
}
}
2.测试模块
int Operate(inta, char theta, int b)
{//对传过来的两个操作数a和b以及操作符进行运算
switch (theta)//判断运算符的种类,进行相应的计算
{
case '&':if (a == 1 && b == 1)
return 1;
return 0;
break;
case'|':if (a == 1 || b == 1)
return 1;
return 0;
break;
case'~':if (a == 0)
return 1;
return 0;
break;
}
}
//对每个式子的每一个不同全排列进行测试
int EvaluateExpression()
{
char Precede(char a, char b);
int indexOfString = 0;
SqStack optr; //存放逻辑运算符的栈
SqStack opnd; //存放大写字母的栈
char c; //接收输入事件每一种可能的取值
char x; //在c为等号时,接收出栈的符号
char theta; //在c为大于号时,接收出栈的符号
char a; //a和b分别为theta两旁的操作数
char b;
char temp; //临时存放operte函数的结果
InitStack(&optr);
Push(&optr, '#');//初始情况时先向存放逻辑运算符的栈中压入‘#’表示式子的开端,最后加一个‘#’表示结尾。
c = string[indexOfString++];//取得式子中的操作数或操作符
InitStack(&opnd);
while (c != '#' || GetTop(&optr) !='#')
{
if (c >= 65 && c <= 90) //判断是否大写字母,即是否为操作数
{
c = permutation[c - 65] + 48;//把该事件在permutation数组中对应的取值提取出来,压入栈中。由于permutation是int数组,需要+48.
Push(&opnd, c);
c = string[indexOfString++];//取下一个符号
}
else //否则为运算符
{
switch (Precede(GetTop(&optr), c))//根据Precede函数来判断运算符之间的优先关系。
{
case '<':
Push(&optr, c);
c = string[indexOfString++];
break;
case '=':
x = Pop(&optr);
c = string[indexOfString++];
break;
case '>':
theta = Pop(&optr);
//这里有一个特殊情况需要考虑,&和|都是进行两个操作数的运算,而~只需要一个操作数。
if (theta != '~')
{
b = Pop(&opnd) - 48;
}
else
{
b = 48;
}
a = Pop(&opnd) - 48;//int和char数据之间的转换
temp = Operate(a, theta, b) + 48;
Push(&opnd, temp);
break;
}
}
}
return GetTop(&opnd) - 48;//将判断结果返回
}
int getKind(charstring[]) //获取测试数据不同事件的个数
{
int letter[26] = { 0 };建立一个存放26个数字的数组,由于统计式子中不同事件的个数
int i;
int sum = 0;
for (i = 0; string[i] != '#'; i++)
{
if (string[i] < 65 || string[i] > 90)//判断是否为事件(事件都用大写字母表示)
{
continue;
}
letter[string[i] - 65] = 1;//在对应的letter数组的下表,使其对应的值为1.
}
for (i = 0; i < 26; i++)//统计不同时间的个数。
{
if (letter[i] == 1)
{
sum++;
}
}
return sum;
}
3.主函数模块
int main()
{
int count; //测试数据的个数
int i;
char c;
int kind; //每一次测试数据不同事件的个数
printf("请输入共有几组测试数据:");
scanf("%d", &count);
c = getchar();//先接受上面scanf函数输入时按下的回车键
while (count--)
{
for (i = 0; c = getchar(); i++)
{
if (c == 10)
{
string[i] = '#';//如果用户输入的数据已经结束,则在最后加上#符号,为了在测试模块中进行测试。
break;
}
string[i] = c;
}
kind = getKind(string);
createTree(kind);
printf("\n");
}
return 0;
}
四.测试结果
输入一共有3组测试数据
键入逻辑表达式(A|~A)&(B|~B)
判断出该逻辑表达式是重言式
输入逻辑表达式(A&~A)&C
判断出该逻辑表达式是矛盾式
键入逻辑表达式(A|B)&(A|~B)
判断出该逻辑表达式既不是重言式也不是矛盾式
#include<stdio.h> #include<math.h> #define LENGTHOFDATA 50 //输入数据预计的长度 #define STACK_INIT_SIZE 100 //定义栈的初始长度 #define STACKINCREMENT 10 //当栈长度不足时的增量 char string[LENGTHOFDATA]; int permutation[LENGTHOFDATA]; typedef struct //栈的结构体 { char * base; //栈的底部指针 char * top; //栈的头指针 int stacksize; //栈的长度 }SqStack; int InitStack(SqStack * s) //栈的初始化 { s->base = malloc(STACK_INIT_SIZE * sizeof(char)); //为栈分配初始长度 if (!s->base) //若失败就退出 { exit(1); } s->top = s->base; s->stacksize = STACK_INIT_SIZE; return 1; } char GetTop(SqStack * s) //获得栈顶元素 { char e; if (s->top == s->base) { exit(1); //若栈为空,则退出 } e = *(s->top - 1); return e; } void Push(SqStack * s, char e) //进栈 { if (s->top - s->base >= s->stacksize) //若空间不足,则增加栈的空间 { s->base = realloc(s->base, (s->stacksize + STACKINCREMENT) * sizeof(char)); if (!s->base) { exit(1); } s->top = s->base + s->stacksize; s->stacksize += STACKINCREMENT; } *s->top = e; s->top++; } char Pop(SqStack *s) //出栈 { char e; if (s->base == s->top) { exit(1); //若栈为空,则退出 } s->top--; e = *s->top; return e; } char Precede(char a, char b) //逻辑运算符之间的优先关系判断函数 { int i, j; char Relationship[6][7] = //把优先关系表做成二维数组,从中寻找优先关系 { ' ', '|', '&', '~', '(', ')', '#', '|', '>', '<', '<', '<', '>', '>', '&', '>', '>', '<', '<', '>', '>', '~', '>', '>', '>', '<', '>', '>', '(', '<', '<', '<', '<', '=', ' ', '#', '<', '<', '<', '<', ' ', '=' }; for (i = 1; i < 6; i++) { if (Relationship[i][0] == a) { break; } } for (j = 1; j < 7; j++) { if (Relationship[0][j] == b) { break; } } return Relationship[i][j]; } int Operate(int a, char theta, int b) { switch (theta) { case '&': if (a == 1 && b == 1) return 1; return 0; break; case'|': if (a == 1 || b == 1) return 1; return 0; break; case'~': if (a == 0) return 1; return 0; break; } } int EvaluateExpression() { char Precede(char a, char b); int indexOfString = 0; SqStack optr; //存放逻辑运算符的栈 SqStack opnd; //存放大写字母的栈 char c; //接收输入事件每一种可能的取值 char x; //在c为等号时,接收出栈的符号 char theta; //在c为大于号时,接收出栈的符号 char a; //a和b分别为theta两旁的操作数 char b; char temp; //临时存放operte函数的结果 InitStack(&optr); Push(&optr, '#');//初始情况时先向存放逻辑运算符的栈中压入‘#’表示式子的开端,最后加一个‘#’表示结尾。 c = string[indexOfString++]; InitStack(&opnd); while (c != '#' || GetTop(&optr) != '#') { if (c >= 65 && c <= 90) //判断是否大写字母 { c = permutation[c - 65] + 48; Push(&opnd, c); c = string[indexOfString++]; } else //否则为运算符 { switch (Precede(GetTop(&optr), c)) { case '<': Push(&optr, c); c = string[indexOfString++]; break; case '=': x = Pop(&optr); c = string[indexOfString++]; break; case '>': theta = Pop(&optr); if (theta != '~') { b = Pop(&opnd) - 48; } else { b = 48; } a = Pop(&opnd) - 48; temp = Operate(a, theta, b) + 48; Push(&opnd, temp); break; } } } return GetTop(&opnd) - 48; } int getKind(char string[]) //获取测试数据不同事件的个数 { int letter[26] = { 0 }; int i; int sum = 0; for (i = 0; string[i] != '#'; i++) { if (string[i] < 65 || string[i] > 90) { continue; } letter[string[i] - 65] = 1; } for (i = 0; i < 26; i++) { if (letter[i] == 1) { sum++; } } return sum; } typedef struct tree //定义二叉树节点的结构体 { int letter; int or; //用于判别在全排列中是否经过该节点 struct tree *right, *left; //该节点的左右孩子 struct tree *par; //该节点的父节点 }tree; tree root; void addTree(tree * p, int length) //根据给予的二叉树的深度在根节点的基础上添加树节点 { if (length > 0) { tree *t1, *t2; t1 = malloc(sizeof(tree)); t2 = malloc(sizeof(tree)); t1->letter = 0; t2->letter = 1; t1->or = t2->or = 0; t1->par = p; t2->par = p; p->left = t1; p->right = t2; addTree(p->left, length - 1); addTree(p->right, length - 1); } } void createTree(int l) //建立二叉树 { int result; //存放每次EvaluateExpression函数运行的结果 int resultCount[50]; //将每次EvaluateExpression函数运行的结果收集在一起,以判别是否为重言式 int indexOfresultCount = 0; //resultCount的下标,用于遍历 int i = 0; int length = l; //l为二叉树的层数 int sum; //存放一共有多少测试数据 int temp; //在全排列被个事件的发生情况时临时存放长度 int index; //存放可能结果数组num的下标 tree *p; //addTree函数中用于构建二叉树的临时指针 tree *t; //在全排列被个事件的发生情况时临时指针 p = &root; root.letter = 0; root.or = 0; addTree(&root, length); temp = length; sum = pow(2, length); while (sum--) { t = &root; temp = length; index = 0; while (temp--) { if (t->left->or == 0) { t = t->left; permutation[index++] = t->letter; } else { t = t->right; permutation[index++] = t->letter; } } t->or = 1; while (t = t->par) { if (t->left->or == 1 && t->right->or == 1) { t->or = 1; } else { break; } } result = EvaluateExpression(); resultCount[indexOfresultCount++] = result; } temp = indexOfresultCount; //判断是否为重言式 for (i = 0; i < temp; i++) { if (resultCount[i] == 1)//检测到一个数据测试成立,就将resultCount的总数-1。 indexOfresultCount--; } if (indexOfresultCount == 0)//resultCount数为0,说明对于所有的测试数据都成立,即为重言式 { printf("True forever"); } else if (indexOfresultCount == temp) { printf("False forever"); } else { printf("Satisfactible"); } } int main() { int count; //测试数据的个数 int i; char c; int kind; //每一次测试数据不同事件的个数 printf("请输入共有几组测试数据:"); scanf("%d", &count); c = getchar();//先接受上面scanf函数输入时按下的回车键 while (count--) { for (i = 0; c = getchar(); i++) { if (c == 10) { string[i] = '#'; break; } string[i] = c; } kind = getKind(string); createTree(kind); printf("\n"); } return 0; }
下面是程序设计书
可能看一下比较好理解
一、需求分析
1.一个逻辑表达式如果对于其变元的任一种取值均为真,则成为重言式;反之,如果对于其变元的任一种取值都为假,则称为矛盾式,然而,更多的情况下,既非重言式,也非矛盾式。写一个程序通过真值表判别一个逻辑表达式属于上述哪一类。基本要求如下:
(1)逻辑表达式从终端输入,长度不超过一行。逻辑运算符包括“|”、“&”和“~”,分别表示或、与和非,运算优先程度递增,但可有括号改变,即括号内的运算优先。逻辑变元为大写字母。表达式中任何地方都可以含有多个空格符。
(2)若是重言式或矛盾式,可以只显示“True Forever”或“False Forever”,否则显示“Statisfactible”以及变量名序列,与用户交互。若用户对表达式变元取定一组值,程序就求出并显示逻辑表达式的值。
(3)本程序先使用栈将逻辑表达式的变量进行存储,然后将栈中的元素作为二叉树的结点结构,然后根据优先级读取表达式建立二叉树,并通过逐个判断根实现对重言式的判别。
2. 程序执行的命令
(1)输入逻辑表达式 (2)判断表达式是重言式还是矛盾式 (3)若既不是重言式也不式矛盾式,则对变元取定值,并显示逻辑表达式的值 (4)结束
3.测试数据
(1) (A|~A)&(B|~B)
(2) (A&~A)&C
(3) A|B|C|D|E|~A
(4) A&B&C&~B
(5) (A|B)&(A|~B)
(6) A&~B|~A&B;
[预计输出结果]
True Forever
False Forever
True Forever
False Forever
Statisfactible
Statisfactible
二. 概要设计
1.总体的流程:在main函数中输入式子,首先对式子中不同时间的个数n进行判别。再根据n来建立一个叶子节点为n的满二叉树,便于用来记录对于各种不同事件可能取值的全排列。再把这些可能取到的数据一次进行检测。从而达到判别重言式的目的。
2. 为实现上述程序功能,需要两个抽象数据类型,如下:
(1)二叉树模型
ADT tree
{数据对象D:D={ai | ai∈ElemSet, i=1,2,…,n, n≥0}
数据关系R:若D为空集,则称为空二叉树。
若D仅含一个数据元素,则R为空集,否则R={H},H满足关系:
(1) T中存在唯一的一个结点,它没有前驱,称为树的根,用root表示,在集合D中用a1表示;
(2) 若D中元素个数大于1,对于任意的数据元素aj∈D且j≥2,存在唯一的数据元素ai∈D,有<ai, aj>∈H;
(3) 若D中元素个数大于1,对于任意的数据元素ai∈D,仅存在不多于2个数据元素aj,ak∈D且j, k≥i,有< ai, aj >, < ai, ak >∈H,其中,若j<k,则称aj为ai的左孩子节点,ak为ai的右孩子节点。
(4) 对于任意的数据元素aj∈D且j≥2,存在D1 D,D1={ | ∈ElemSet, k=1,2, …,m, m≥0},有唯一的Pj={< , >, < , >, …, < , >, < , >},这个集合Pj表示从根结点到结点aj的一条路径。
基本操作P:
void createTree(int l)
操作结果:构造二叉树。
void addTree(tree * p, intlength)
前提条件:二叉树已建立。
操作结果:根据形参length来对p进行添加树节点,构造一个深度为length的二叉树。
}tree
(2)识别表达式使用的栈定义
ADT SqStack
{数据对象:D={ai| aiÎElemSet,i=1,2,...,n, n> 0}
数据关系:R1={ <>ai-1 ,ai>|ai-1, aiÎD, i=1,2,...,n }
约定an端为栈顶,ai端为栈底。
基本操作P:
int InitStack(SqStack * s)
操作结果:构造一个栈。
char GetTop(SqStack * s)
前提条件:栈已经存在。
操作结果:返回栈顶元素。
void Push(SqStack * s, chare)
前提条件:栈已经存在
操作结果:将元素e压入栈中。
char Pop(SqStack *s)
前提条件:栈已经存在,且栈非空。
操作结果:将栈顶元素充当返回值返回。
} ADT SqStack
3.本程序主要分为四个模块
(1)主函数模块
输入共有几组测试数据,输入每组测试数据,调用各种函数进行测试。
(2)判断数据不同事件个数模块
对于每组测试数据,计算出不同时间的个数,充当返回值返回。
(3)二叉树的建立模块
对与每组不同的测试数据以及测试数据的不同事件的个数。求出该测试数据的对于每个不同事件可能取值的全排列。全排列数据存放在二叉树组中。对于测试后的结果,进行最后的判别。
(4)测试数据模块
把每次的测试数据的全排列传递给该模块,该模块利用栈的性质,来进行测试,判断是为真还是为假。
4.程序各个模块间的关系
三. 详细设计
1.栈类型和二叉树类型
#defineSTACK_INIT_SIZE 100 //定义栈的初始长度
#defineSTACKINCREMENT 10 //当栈长度不足时的增量
typedefstruct //栈的结构体
{
char * base; //栈的底部指针
char * top; //栈的头指针
int stacksize; //栈的长度
}SqStack;
intInitStack(SqStack * s) //栈的初始化
{
s->base = malloc(STACK_INIT_SIZE *sizeof(char)); //为栈分配初始长度
if (!s->base) //若失败就退出
{
exit(1);
}
s->top = s->base;
s->stacksize = STACK_INIT_SIZE;
return 1;
}
charGetTop(SqStack * s) //获得栈顶元素
{
char e;
if (s->top == s->base)
{
exit(1); //若栈为空,则退出
}
e = *(s->top - 1);
return e;
}
void Push(SqStack* s, char e) //进栈
{
if (s->top - s->base >=s->stacksize) //若空间不足,则增加栈的空间
{
s->base = realloc(s->base,(s->stacksize + STACKINCREMENT) * sizeof(char));
if (!s->base)
{
exit(1);
}
s->top = s->base + s->stacksize;
s->stacksize += STACKINCREMENT;
}
*s->top = e;
s->top++;
}
char Pop(SqStack*s) //出栈
{
char e;
if (s->base == s->top)
{
exit(1); //若栈为空,则退出
}
s->top--;
e = *s->top;
return e;
}
//对于main函数中输入的式子,通过getKind函数得到不同事件的个数n,由于每个事件只有两种情况:真或假,我们用0和1来表示。这就是tree结构体重的letter,它用来表示该事件是否为真。
typedef structtree //定义二叉树节点的结构体
{
int letter;
int or; //用于判别在全排列中是否经过该节点
struct tree *right, *left; //该节点的左右孩子
struct tree *par; //该节点的父节点
}tree;
tree root;
void addTree(tree* p, int length) //根据给予的二叉树的深度在根节点的基础上添加树节点
{
if (length > 0)
{
tree *t1, *t2;//t1,t2分别为当前节点的左右子树。
t1 = malloc(sizeof(tree));//为子树申请空间
t2 = malloc(sizeof(tree));
t1->letter = 0;//对于每一个事件,都有两种情况0或1,所以有两个子节点,表示该事件为0和为1的两种情况,从而达到全排列。
t2->letter = 1;
t1->or = t2->or = 0;
//在后续的全排列中,用or来记录该节点是否测试过。
t1->par = p;
//建立父子关系。
t2->par = p;
p->left = t1;
p->right = t2;
addTree(p->left, length - 1);
addTree(p->right, length - 1);
//递归遍历。
}
}
voidcreateTree(int l) //建立二叉树
{
int result; //存放每次EvaluateExpression函数运行的结果
int resultCount[50]; //将每次EvaluateExpression函数运行的结果收集在一起,以判别是否为重言式
int indexOfresultCount = 0; //resultCount的下标,用于遍历
int i = 0;
int length = l; //l为二叉树的层数
int sum; //存放一共有多少测试数据
int temp; //在全排列被个事件的发生情况时临时存放长度
int index; //存放可能结果数组num的下标
tree *p; //addTree函数中用于构建二叉树的临时指针
tree *t; //在全排列被个事件的发生情况时临时指针
p = &root;
root.letter = 0;
root.or = 0;
addTree(&root, length);
temp = length;
sum = pow(2, length);
//把每种情况输入到EvaluateExpression函数中去。
while (sum--)
{
t = &root;
temp = length;
index = 0;
while (temp--)
{
if (t->left->or == 0)
{
t = t->left;
permutation[index++] = t->letter;//permutation数组用来记录每次全排列的可能情况,为全局变量。
}
else
{
t = t->right;
permutation[index++] = t->letter;
}
t->or = 1;
//如果该节点已经1测试了,将它的or赋1.
//检验,从叶子节点开始,如果左右孩子的or为1,则将父节点的or也设为1.
while (t = t->par)
{
if (t->left->or == 1 &&t->right->or == 1)
{
t->or = 1;
}
else
{
break;
}
//将这种结果给予到EvaluateExpression函数中去。
result = EvaluateExpression();
//将每次的测试结果放入数组中,以便于下面重言式的最后判别。
resultCount[indexOfresultCount++] = result;
}
temp = indexOfresultCount; //判断是否为重言式
for (i = 0; i < temp; i++)
{
if (resultCount[i] == 1)//检测到一个数据测试成立,就将resultCount的总数-1。
indexOfresultCount--;
}
if (indexOfresultCount == 0)//resultCount数为0,说明对于所有的测试数据都成立,即为重言式
{
printf("True forever");
}
else if (indexOfresultCount == temp)
{
printf("False forever");
}
else
{
printf("Satisfactible");
}
}
2.测试模块
int Operate(inta, char theta, int b)
{//对传过来的两个操作数a和b以及操作符进行运算
switch (theta)//判断运算符的种类,进行相应的计算
{
case '&':if (a == 1 && b == 1)
return 1;
return 0;
break;
case'|':if (a == 1 || b == 1)
return 1;
return 0;
break;
case'~':if (a == 0)
return 1;
return 0;
break;
}
}
//对每个式子的每一个不同全排列进行测试
int EvaluateExpression()
{
char Precede(char a, char b);
int indexOfString = 0;
SqStack optr; //存放逻辑运算符的栈
SqStack opnd; //存放大写字母的栈
char c; //接收输入事件每一种可能的取值
char x; //在c为等号时,接收出栈的符号
char theta; //在c为大于号时,接收出栈的符号
char a; //a和b分别为theta两旁的操作数
char b;
char temp; //临时存放operte函数的结果
InitStack(&optr);
Push(&optr, '#');//初始情况时先向存放逻辑运算符的栈中压入‘#’表示式子的开端,最后加一个‘#’表示结尾。
c = string[indexOfString++];//取得式子中的操作数或操作符
InitStack(&opnd);
while (c != '#' || GetTop(&optr) !='#')
{
if (c >= 65 && c <= 90) //判断是否大写字母,即是否为操作数
{
c = permutation[c - 65] + 48;//把该事件在permutation数组中对应的取值提取出来,压入栈中。由于permutation是int数组,需要+48.
Push(&opnd, c);
c = string[indexOfString++];//取下一个符号
}
else //否则为运算符
{
switch (Precede(GetTop(&optr), c))//根据Precede函数来判断运算符之间的优先关系。
{
case '<':
Push(&optr, c);
c = string[indexOfString++];
break;
case '=':
x = Pop(&optr);
c = string[indexOfString++];
break;
case '>':
theta = Pop(&optr);
//这里有一个特殊情况需要考虑,&和|都是进行两个操作数的运算,而~只需要一个操作数。
if (theta != '~')
{
b = Pop(&opnd) - 48;
}
else
{
b = 48;
}
a = Pop(&opnd) - 48;//int和char数据之间的转换
temp = Operate(a, theta, b) + 48;
Push(&opnd, temp);
break;
}
}
}
return GetTop(&opnd) - 48;//将判断结果返回
}
int getKind(charstring[]) //获取测试数据不同事件的个数
{
int letter[26] = { 0 };建立一个存放26个数字的数组,由于统计式子中不同事件的个数
int i;
int sum = 0;
for (i = 0; string[i] != '#'; i++)
{
if (string[i] < 65 || string[i] > 90)//判断是否为事件(事件都用大写字母表示)
{
continue;
}
letter[string[i] - 65] = 1;//在对应的letter数组的下表,使其对应的值为1.
}
for (i = 0; i < 26; i++)//统计不同时间的个数。
{
if (letter[i] == 1)
{
sum++;
}
}
return sum;
}
3.主函数模块
int main()
{
int count; //测试数据的个数
int i;
char c;
int kind; //每一次测试数据不同事件的个数
printf("请输入共有几组测试数据:");
scanf("%d", &count);
c = getchar();//先接受上面scanf函数输入时按下的回车键
while (count--)
{
for (i = 0; c = getchar(); i++)
{
if (c == 10)
{
string[i] = '#';//如果用户输入的数据已经结束,则在最后加上#符号,为了在测试模块中进行测试。
break;
}
string[i] = c;
}
kind = getKind(string);
createTree(kind);
printf("\n");
}
return 0;
}
四.测试结果
输入一共有3组测试数据
键入逻辑表达式(A|~A)&(B|~B)
判断出该逻辑表达式是重言式
输入逻辑表达式(A&~A)&C
判断出该逻辑表达式是矛盾式
键入逻辑表达式(A|B)&(A|~B)
判断出该逻辑表达式既不是重言式也不是矛盾式
相关文章推荐
- 重言式判别
- 重言式判别
- 数据结构实习——重言式的判别(写的不好不要见怪)
- 重言式判别 (数据结构课程设计)
- C++重言式判别
- 如何判别当前进程下是否存在临时表#t
- py中的目录与文件判别代码
- fisher线性判别里的广义瑞丽商
- 关于不能用莱布尼兹准则判别级数敛散性时的其中一种方法示例
- 重言式判定------参考了别人的代码。。
- 日本手机开发之一——运营商判别和手机种类名获取
- 大端模式和小端模式的判别!
- Android中如何判别WebView加载完毕并使用ProgressDialog进行提示
- 生成模型和判别模型
- 陶哲轩实分析 推论 7.5.3 (比例判别法) 证明
- 线性判别分析(LDA) 主成分分析(PCA)
- C#windows service效劳装置、注册、判别效劳能否存在、能否发动
- 【转】线性判别分析(Linear Discriminant Analysis, LDA)算法分析
- LL(1)文法判别之First集合、Follow集合、Select集合求法
- 怎样判别蓄电池的好坏?