Basic Computer Science – Learning proporsition and implement the required methods specified in the lab specification.
这是 计算机科学基础 课程中的第四个实验,主要是借助计算机语言来验证完成命题逻辑的相关实验。
本次实验主要考察命题逻辑的相关的概念,主要包含六个子任务,前四个为基础逻辑运算,后两个为实际应用。
为了达到实验效果,在该实验中禁止使用STL容器。
Explain
在本次实验中存在一些非ASCII字符集字符,在一些环境下无法正常显示,因此本文规定:
在代码中:
- 使用 & 替代 ∧ 作为 Conjunction;
- 使用 | 替代 ∨ 作为 Disjunction;
- 使用 ! 替代 ¬ 作为 Negation。
在正文中根据情况选择适合的符号。
Analysis
基础的命题逻辑为 Conjunction(∧)、Disjunction(∨)、Negation(¬),前四个实验分别对前面所属的三种以及 imply(→) 进行模拟验证。
前三种命题逻辑,根据其定义设置函数或者直接使用逻辑运算符 & | ! 即可达到实验效果。在此不再详细描述。
Implication(→)
对于 Imply ,我们也可以通过if else的判断返回其值。
这里给出 Imply 的真值表,示例代码详见 Example 。
P | Q | P→Q |
---|---|---|
T | T | T |
T | F | F |
F | T | T |
F | F | T |
Print Truth Table
对于给定的表达式((P->Q)∧¬S)∨(¬Q->R),打印它的真值表。
我们可以通过多层循环,来输出所有可能的情况。
注意,这里可能出现因为编码不同导致的乱码。
printf(" P | Q | R | S | P->Q | !S | (P->Q)&!S | !Q | !Q->R | ((P->Q)&!S)|(!Q->R)\n");
for(int P=1;P>=0;P--)
{
for(int Q=1;Q>=0;Q--)
{
for(int R=1;R>=0;R--)
{
for(int S=1;S>=0;S--)
{
printf("---+---+---+---+------+----+-----------+----+-------+--------------------\n");
printf(" %d | %d | %d | %d | %d | %d | %d",P,Q,R,S,Implication(P,Q),Negation(S),Conjunction(Implication(P,Q),Negation(S)));
printf(" | %d | %d | %d\n",Negation(Q),Implication(Negation(Q),R),Disjunction(Conjunction(Implication(P,Q),Negation(S)),Implication(Negation(Q),R)));
}
}
}
}
Syllogisms
Syllogisms 即三段论推理,是一个简单的逻辑推理。
它分为三部分:
- Major premise 主要条件;
- Minor premise 次要条件;
- Conclusion 结论。
三段论中 Major premise 和 Minor premise 默认为 True ,这是之后推理判断的基础,其描述通常如下:
Major premise 为 P->Q (其中 P Q 可能有若干小命题组成), Minor premise 为 R ,Conclusion 为 C 。
对于 Minor premise 和 Conclusion ,均为 Major premise 的子命题。给定一个 Minor premise ,需要通过 Major premise 推理出 Conclusion 的正确性。
最终可以得出三种结果:
- Conclusion is always true.
- Conclusion may not always be true.
- Conclusion is always false.
Truth Table 求解
使用 Truth Table 求解的过程如下:
- 画出 Truth Table;
- 找到到所有 P->Q 为真的情况;
- 找到上述情况中包含 R 的情况;
- 在上述情况中检查 C 是否恒为 True,根据实际分为上述的三种结果。
这种方式在较为简单的命题以及验算的时更直观,只需要枚举出所有可能的情况即可。
推理验证
使用推理验证就是通过判断命题之间的关系是否矛盾来得出最终结论。
这种方式更符合人的思维习惯。但是使用计算机模拟较为复杂,
本文在这里使用的解决方式更接近于 Truth Table 求解:
- 对每一种命题可能的取值进行枚举,找到其中使 Major premise 成立的情况;
- 在这个前提下查看 Minor premise 是否成立,如果成立则进行下一步,否则继续枚举;
- 在上一步的前提下,我们检查此时的 Conclusion 的取值,将其记录继续枚举;
- 枚举结束后检查 Conclusion 的记录情况,并根据实际分为上述的三种结果。
代码实现
struct Propositional
我们使用结构体存储命题 R 与 C 。
这里包含两个成员变量及一个成员函数:
- parent 表示该命题和 Major premise 中的哪个子命题相关;
- flag 表示该命题是否是 Major premise 中原始命题的否定;
- Propositional() 初始化,默认为不是命题的否定。
struct Propositional{
char parent;
int flag;
Propositional()
{
flag=0;
}
};
input()
对于输入,首先规定好输入格式,然后对数据进行分析处理。
void input()
{
char p[10],q[10],r[10],c[10];
scanf("If %s then %s\n",p,q);
scanf("%s\n",r);
scanf("Conclusion %s\n",c);
//输入结束,开始处理输入
if(p[strlen(p)-2]==r[strlen(r)-1])//判断R是不是关于P的命题
{
R.parent='p';
C.parent='q';
if(r[0]!=p[0])//判断状态是否相同
{
R.flag=1;
}
if(c[0]!=q[0])
{
C.flag=1;
}
}else{
R.parent='q';
C.parent='p';
if(r[0]!=q[0])//判断状态是否相同
{
R.flag=1;
}
if(c[0]!=p[0])
{
C.flag=1;
}
}
}
int cnt; int sum;
在这里我们使用 cnt 统计 Conclusion 被记录的总次数,使用 sum 记录 Conclusion 的数据。
由于 Conclusion 只有两种取值,因此可以进行如下判断:
- sum != 0 & sum == cnt 说明 Conclusion 恒为真;
- sum != 0 & sum != cnt 说明 Conclusion 不恒为真;
- sum == 0 说明 Conclusion 恒为假。
exclusiveOr
异或运算的自定义函数,也可以直接使用 ‘^’ 或者 ‘xor’ 进行运算。
这里用于简化 Conclusion 的记录
int exclusiveOr(int P,int Q)
{
if(P!=Q) return 1;
else return 0;
}
recode() & check()
recode() & checkj() 函数的作用是记录 Conclusion 。
其中 recode() 函数使用了异或简化了代码。
void recode(int t)//记录次数
{
sum+=exclusiveOr(C.flag,t);
cnt++;
}
void check()
{
for(int p=0;p<=1;p++)
{
for(int q=0;q<=1;q++)
{
if(!Implication(p,q)) continue;//判断 Major premise 是否成立
//如果成立,开始判断 Minor premise 并且记录 Conclusion
if(R.parent=='p')
{
if(exclusiveOr(R.flag,p)) recode(q);
}else{
if(exclusiveOr(R.flag,q)) recode(p);
}
}
}
}
Example
Task1
For propositions P and Q (where the value of P or Q could be 0 or 1), implement a method for the conjunction ∧ operator of P ∧ The signature of the method is: int conjunction(int P, int Q)
#include <bits/stdc++.h>
using namespace std;
int Conjunction(int P,int Q)
{
if(P==1&&Q==1) return 1;
else return 0;
}
int main()
{
int P,Q;
cin>>P>>Q;
cout<<Conjunction(P,Q);
return 0;
}
Task2
For propositions P and Q (where the value of P or Q could be 0 or 1), implement a method for the disjunction ∨ operator of P ∨ The signature of the method is: int disjunction(int P, int Q)
#include <bits/stdc++.h>
using namespace std;
int Disjunction(int P,int Q)
{
if(P==1||Q==1) return 1;
else return 0;
}
int main()
{
int P,Q;
cin>>P>>Q;
cout<<Disjunction(P,Q);
return 0;
}
Task3
For a propositions P (where the value of P could be 0 or 1), implement a method for the negation ¬ operator of ¬P. The signature of the method is: int negation(int P)
#include <bits/stdc++.h>
using namespace std;
int Negation(int P)
{
if(P==1) return 0;
else return 1;
}
int main()
{
int P;
cin>>P;
cout<<Negation(P);
return 0;
}
Task4
For propositions P and Q (where the value of P or Q could be 0 or 1), implement a method for the implication -> operator of P -> The signature of the method is: int implication(int P, int Q)
#include <bits/stdc++.h>
using namespace std;
int Implication(int P, int Q)
{
if(P==1&&Q==0) return 0;
else return 1;
}
int main()
{
int P,Q;
cin>>P>>Q;
cout<<Implication(P,Q);
return 0;
}
Task5
Write a method to print the truth table of propositions P,Q,R,S and ((P->Q) ∧¬S) ∨ (¬Q->R).
#include <bits/stdc++.h>
using namespace std;
int Conjunction(int P,int Q)
{
if(P==1&&Q==1) return 1;
else return 0;
}
int Disjunction(int P,int Q)
{
if(P==1||Q==1) return 1;
else return 0;
}
int Negation(int P)
{
if(P==1) return 0;
else return 1;
}
int Implication(int P, int Q)
{
if(P==1&&Q==0) return 0;
else return 1;
}
int main()
{
printf(" P | Q | R | S | P->Q | !S | (P->Q)&!S | !Q | !Q->R | ((P->Q)&!S)|(!Q->R)\n");
for(int P=1;P>=0;P--)
{
for(int Q=1;Q>=0;Q--)
{
for(int R=1;R>=0;R--)
{
for(int S=1;S>=0;S--)
{
printf("---+---+---+---+------+----+-----------+----+-------+--------------------\n");
printf(" %d | %d | %d | %d | %d | %d | %d",P,Q,R,S,Implication(P,Q),Negation(S),Conjunction(Implication(P,Q),Negation(S)));
printf(" | %d | %d | %d\n",Negation(Q),Implication(Negation(Q),R),Disjunction(Conjunction(Implication(P,Q),Negation(S)),Implication(Negation(Q),R)));
}
}
}
}
return 0;
}
Task6
Write a method to check that, for a syllogy (using p, q to represent atomic proposition), if the conclusion can be drawn.
#include <bits/stdc++.h>
using namespace std;
int Negation(int P)
{
if(P!=0) return 0;
else return 1;
}
int exclusiveOr(int P,int Q)
{
if(P!=Q) return 1;
else return 0;
}
int Implication(int P, int Q)
{
if(P==1&&Q==0) return 0;
else return 1;
}
struct Propositional{
char parent;//表示依附于哪一个命题,可以为p或者q。
int flag;//表示是不是要取反。
Propositional()
{
flag=0;//默认表示不取反
}
}R,C;
int cnt=0;//存储 Minor premise 为 True 时 Major premise 成立的次数
int sum=0;//存储在上述条件成立时 Conclusion 成立的次数
void input()
{
char p[10],q[10],r[10],c[10];
scanf("If %s then %s\n",p,q);
scanf("%s\n",r);
scanf("Conclusion %s\n",c);
//输入结束,开始处理输入
if(p[strlen(p)-2]==r[strlen(r)-1])//判断R是不是关于P的命题
{
R.parent='p';
C.parent='q';
if(r[0]!=p[0])//判断状态是否相同
{
R.flag=1;
}
if(c[0]!=q[0])
{
C.flag=1;
}
}else{
R.parent='q';
C.parent='p';
if(r[0]!=q[0])//判断状态是否相同
{
R.flag=1;
}
if(c[0]!=p[0])
{
C.flag=1;
}
}
}
void recode(int t)//记录次数
{
sum+=exclusiveOr(C.flag,t);
cnt++;
}
void check()
{
for(int p=0;p<=1;p++)
{
for(int q=0;q<=1;q++)
{
if(!Implication(p,q)) continue;//判断 Major premise 是否成立
//如果成立,开始判断 Minor premise 并且记录 Conclusion
if(R.parent=='p')
{
if(exclusiveOr(R.flag,p)) recode(q);
}else{
if(exclusiveOr(R.flag,q)) recode(p);
}
}
}
}
int main()
{
input();
check();
if(sum)
{
if(sum==cnt) cout<<"Conclusion is always true.";
else cout<<"Conclusion may not always be true.";
}else{
cout<<"Conclusion is always false.";
}
}