离散数学中有这样一种证明题:!B,A→B,C→D,A∨B => D
如何用C++编码的方法导出推理过程呢?
①我们先看结果:
②程序的食用说明:
1.前提中只能出现析取(+),单条件(-),双条件(=)和文字(p或!p),且均为简单式
2.结论中只能够是文字(p或!p)
3.推理的某些步骤可能是不必要的,但还是输出了
③程序的原理:
有两个版本,一个基于“假言推理”,一个基于“消解法”
假言推理:A∧(A→B)=> B
消解法:A∧(!A∨B) => B
1、定义一个结构体Sentence,成员包含:
primary:原字符串;
【若是条件式】前件left,后件right(left→right)(或者保存析取式的左右部分)
【若不是前提条件】basis[0]和basis[1]保存了推出此式的依据编号
【若不是前提条件】字符串reason保存了推出此式的理由
Pos:保存了该式子的编号
isLetter、isCond:是否为文字、是否为条件式(消解式)
2、处理输入(假言推理)
遇到析取式:转换为2个单条件式;
遇到双条件式:转换为4个单条件式;
遇到单条件式:再衍生出1个逆否单条件式;
遇到其它:无法处理,直接保存;
(注意要把!!p处理成!p,把!!!p处理为!p)
处理输入(消解法)
遇到析取式:直接保存;
遇到双条件式:转换为2个单条件式,再转化为两个析取式;
遇到单条件式:转化为1个析取式;
3、开始推理(敲黑板):
在所有结构体中搜索文字;
找到文字再搜索条件式,若条件式的前件与文字相同,则使用“假言推理”(或“消解法”,前件或后件有一个与文字相同)(这也是推理过程中唯一使用的定理,无法使用传递律等……),以条件式的后件创建一个结构体放入结构体组的尾部;
继续搜索……
(其实就是一个双层for循环)
4、输出我们的推理过程
如果结论与我们推理过程中某个文字相同,就代表我们推理成功,立即停止推理;
若到最后都无法找到匹配的文字,则输出推理失败。
“假言推理”版本如下:
/* std == c++11 */
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
#include <algorithm>
using namespace std;
//改进1:将字符操作改为string操作,极大地增强了可读性
struct Sentence
{
string primary; //primary string
string left, right; //left - right (isCondition == true)
int basis[2] = { 0 }; //store the number of reasons
string reason; //how conducts to it (isPrim == false)
int Pos = 0; //position
bool isLetter, isCond, isUsed = 0;
};
//改进2:利用find()和replace()将!!p改成p,降低了编写难度
void to_pos( string &a )
{
size_t p;
while ((p = a.find( "!!" ) + 1)) a.erase( p - 1, 2 );
}
//改写:原函数int setNiyou();
//解释: 目标数组 初始字符串 左件 右件 编号 推理理由 依据1 依据2 是否条件式 是否文字
void setStc( vector<Sentence>& stc, string pri, string l, string r, int p, string& re, int r1, int r2, bool isC, bool isL)
{
Sentence tmp;
tmp.primary = pri;
tmp.left = l;
tmp.right = r;
tmp.reason = re;
tmp.Pos = p;
tmp.basis[0] = r1;
tmp.basis[1] = r2;
tmp.isCond = isC;
tmp.isLetter = isL;
stc.push_back( tmp );
}
//改写:原函数int inputPrimary();
//解释:将所有前提尽可能转化为条件式
int inputPrim( vector<Sentence>& stc )
{
int count = 1; //NO.count
size_t con; //the position of the connect-sign
string re1 = "前提条件";
string re2 = "原命题的逆否命题";
string re3 = "双条件导出的条件式";
string re4 = "析取式转换为条件式";
string temp, left, right;
while (getline( cin, temp ) && temp.empty() == false)
{
//p or !p
if (temp.length() == 1 || (temp.length() == 2 && temp[0] == '!'))
{
setStc( stc, temp, "", "", count, re1, 0, 0, 0, 1 );
++count;
}
else
{
con = temp.find_first_of( "-=+" );
//others
if (con == temp.length())
{
setStc( stc, temp, "", "", count, re1, 0, 0, 0, 0 );
++count;
}
else
{
if (temp[con] == '-')
{
//p-q
left = string( temp.begin(), temp.begin() + con );
right = string( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re1, 0, 0, 1, 0 );
++count;
//!q-!p
temp = "!" + right + "-!" + left;
to_pos( temp );
con = temp.find( "-" );
left = string( temp.begin(), temp.begin() + con );
right = string( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re2, count - 1, 0, 1, 0 );
++count;
}
else if (temp[con] == '=')
{
//p=q
setStc( stc, temp, "", "", count, re1, 0, 0, 0, 0);
++count;
//p-q
temp[con] = '-';
left = string( temp.begin(), temp.begin() + con );
right = string( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re3, count - 1, 0, 1, 0 );
++count;
//q-p
temp = right + '-' + left;
setStc( stc, temp, right, left, count, re3, count - 2, 0, 1, 0 );
++count;
//修复1:!p-!q
temp = "!" + left + "-!" + right;
to_pos( temp );
con = temp.find( "-" );
left = string( temp.begin(), temp.begin() + con );
right = string( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re2, count - 1, 0, 1, 0 );
++count;
//修复2:!q-!p
temp = right + '-' + left;
setStc( stc, temp, right, left, count, re2, count - 3, 0, 1, 0 );
++count;
}
else if (temp[con] == '+')
{
//p+q
setStc( stc, temp, "", "", count, re1, 0, 0, 0, 0 );
++count;
//!p-q
temp[con] = '-';
temp = '!' + temp;
to_pos( temp );
con = temp.find( "-" );
left = string( temp.begin(), temp.begin() + con );
right = string( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re4, count - 1, 0, 1, 0 );
++count;
//!q-p
temp = "!" + right + "-!" + left;
to_pos( temp );
con = temp.find( "-" );
left = string( temp.begin(), temp.begin() + con );
right = string( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re4, count - 2, 0, 1, 0 );
++count;
}
}
}
}
//改进3:删除按字符串长度排序
return count;
}
//改写:void printYsh()
//解释:若推理式与目标一致,推理成功
void printStc( vector<Sentence> stc, const string& result )
{
for (size_t i = 0; i != stc.size(); ++i)
{
cout << "(" << stc[i].Pos << ")" << "\t\t" << left << setw( 10 ) << stc[i].primary + "为真" << "\t\t";
if (stc[i].basis[0]) cout << "(" << stc[i].basis[0] << ")";
if (stc[i].basis[1]) cout << "(" << stc[i].basis[1] << ")";
cout << stc[i].reason << endl;
if (stc[i].primary == result)
{
cout << endl << "推理成功!";
return;
}
}
cout << endl << "推理失败";
}
//改进4:新增函数infer(),简化main(),增强可读性
//解释:利用假言推理得到新的文字
void infer( vector<Sentence>& stc )
{
int count = stc.size() + 1;
string re5 = "假言推理";
for (size_t i = 0; i != stc.size(); ++i)
{
if (!stc[i].isLetter) continue;
for (size_t j = 0; j != stc.size(); ++j)
{
if (stc[j].isCond &&stc[j].isUsed == 0 && stc[j].left == stc[i].primary)
{
stc[j].isUsed = 1;
setStc( stc, stc[j].right, "", "", count, re5, i + 1, j + 1, 0, 1 );
++count;
}
}
}
}
int main()
{
//改进5:使用vector,方便使用且节约内存
vector<Sentence> stc;
cout << "请输入前提:" << endl;
inputPrim( stc );
infer( stc );
string result;
cout << "请输入结论:";
getline( cin, result );
cout << endl << "推理如下:" << endl;
printStc( stc, result );
system( "pause" );
return 0;
}
“消解法”版本如下:
/* std == c++11 */
#include <iostream>
#include <string>
#include <vector>
#include <iomanip>
#include <algorithm>
using namespace std;
//改进1:将字符操作改为string操作,极大地增强了可读性
struct Sentence
{
string primary; //primary string
string left, right; //left + right (isCond == true )
size_t basis[2] = { 0 }; //store the number of reasons
string reason; //how conducts to it (isPrim == false)
size_t Pos = 0; //position
bool isLetter, isCond, isUsed = 0;
};
//改进2:利用find()和replace()将!!p改成p,降低了编写难度
void to_pos( string &a )
{
size_t p;
while ((p = a.find( "!!" ) + 1)) a.erase( p - 1, 2 );
}
//改写:原函数int setNiyou();
//解释: 目标数组 初始字符串 A B 编号 推理理由 依据1 依据2 是否析取式 是否文字
void setStc( vector<Sentence>& stc, const string& pri, const string& l, const string& r, size_t p, const string& re, size_t r1, size_t r2, bool isC, bool isL )
{
Sentence tmp;
tmp.primary = pri;
tmp.left = l;
tmp.right = r;
tmp.reason = re;
tmp.Pos = p;
tmp.basis[0] = r1;
tmp.basis[1] = r2;
tmp.isCond = isC;
tmp.isLetter = isL;
stc.push_back( tmp );
}
//改写:原函数int inputPrimary();
//解释:将所有前提尽可能转化为析取式
int inputPrim( vector<Sentence>& stc )
{
int count = 1; //NO.count
size_t con; //the position of the connect-sign
string re1 = "前提条件";
string re2 = "条件式导出的析取式";
string re3 = "双条件导出的条件式";
string temp, left, right;
while (getline( cin, temp ) && temp.empty() == false)
{
//p or !p
if (temp.length() == 1 || (temp.length() == 2 && temp[0] == '!'))
{
setStc( stc, temp, "", "", count, re1, 0, 0, 0, 1 );
++count;
}
else
{
con = temp.find_first_of( "-=+" );
//others
if (con == string::npos)
{
setStc( stc, temp, "", "", count, re1, 0, 0, 0, 0 );
++count;
}
else
{
if (temp[con] == '-')
{
//p-q
setStc( stc, temp, "", "", count, re1, 0, 0, 0, 0 );
++count;
//!p+q
temp[con] = '+';
temp = "!" + temp;
to_pos( temp );
con = temp.find( "+" );
left.assign( temp.begin(), temp.begin() + con );
right.assign( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re2, count - 1, 0, 1, 0 );
++count;
}
else if (temp[con] == '=')
{
//p=q
setStc( stc, temp, "", "", count, re1, 0, 0, 0, 0 );
++count;
//p-q
temp[con] = '-';
setStc( stc, temp, "", "", count, re3, count - 1, 0, 0, 0 );
++count;
//q-p
left.assign( temp.begin() + con + 1, temp.end() );
right.assign( temp.begin(), temp.begin() + con );
temp = left + "-" + right;
setStc( stc, temp, right, left, count, re3, count - 2, 0, 0, 0 );
++count;
//!p+q
temp = "!" + right + "+" + left;
to_pos( temp );
con = temp.find( "+" );
left.assign( temp.begin(), temp.begin() + con );
right.assign( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re2, count - 2, 0, 1, 0 );
++count;
//!q+p
temp = "!" + right + "+!" + left;
to_pos( temp );
con = temp.find( "+" );
left.assign( temp.begin(), temp.begin() + con );
right.assign( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re2, count - 2, 0, 1, 0 );
++count;
}
else if (temp[con] == '+')
{
//p+q
left.assign( temp.begin(), temp.begin() + con );
right.assign( temp.begin() + con + 1, temp.end() );
setStc( stc, temp, left, right, count, re1, 0, 0, 1, 0 );
++count;
}
}
}
}
//改进3:删除按字符串长度排序
return count;
}
//改写:void printYsh()
//解释:若推理式与目标一致,推理成功
void printStc( vector<Sentence> stc, const string& result )
{
for (size_t i = 0; i != stc.size(); ++i)
{
cout << "(" << stc[i].Pos << ")" << "\t\t" << left << setw( 10 ) << stc[i].primary + "为真" << "\t\t";
if (stc[i].basis[0]) cout << "(" << stc[i].basis[0] << ")";
if (stc[i].basis[1]) cout << "(" << stc[i].basis[1] << ")";
cout << stc[i].reason << endl;
if (stc[i].primary == result)
{
cout << endl << "推理成功!";
return;
}
}
cout << endl << "推理失败";
}
//新增:判断匹配函数(!p和p匹配)
bool isMatch( const string& a, const string& b )
{
if (a == "!" + b) return true;
if (b == "!" + a) return true;
return false;
}
//改进4:新增函数infer(),简化main(),增强可读性
//解释:利用消解式得到新的文字
void infer( vector<Sentence>& stc )
{
size_t count = stc.size() + 1;
string re4 = "消解法";
for (size_t i = 0; i != stc.size(); ++i)
{
if (!stc[i].isLetter) continue;
for (size_t j = 0; j != stc.size(); ++j)
{
if (stc[j].isCond &&stc[j].isUsed == 0)
{
if (isMatch( stc[j].left, stc[i].primary ))
setStc( stc, stc[j].right, "", "", count, re4, i + 1, j + 1, 0, 1 );
else if (isMatch( stc[j].right, stc[i].primary ))
setStc( stc, stc[j].left, "", "", count, re4, i + 1, j + 1, 0, 1 );
else continue;
stc[j].isUsed = 1;
++count;
}
}
}
}
int main()
{
//改进5:使用vector,方便使用且节约内存
vector<Sentence> stc;
cout << "请输入前提:" << endl;
inputPrim( stc );
infer( stc );
string result;
cout << "请输入结论:";
getline( cin, result );
cout << endl << "推理如下:" << endl;
printStc( stc, result );
system( "pause" );
return 0;
}
如果有BUG,欢迎评论,谢谢观看~