重言式的判别

来源:互联网 发布:淘宝商城家具2门大橱 编辑:程序博客网 时间:2024/04/29 03:49

小编今年大二,数据结构让搞个重言式的判别,感觉写得很成功,传上来给大家分享。

#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)

判断出该逻辑表达式既不是重言式也不是矛盾式

 

 


0 0
原创粉丝点击