命题逻辑之自然推理

离散数学中有这样一种证明题:!B,A→B,C→D,A∨B => D

如何用C++编码的方法导出推理过程呢?

①我们先看结果:



②程序的食用说明:

1.前提中只能出现析取(+),单条件(-),双条件(=)和文字(p或!p),且均为简单式

2.结论中只能够是文字(p或!p)

3.推理的某些步骤可能是不必要的,但还是输出了

③程序的原理:

有两个版本,一个基于“假言推理”,一个基于“消解法”

假言推理:A(A→B)=> B

消解法:A∧(!AB) => 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,欢迎评论,谢谢观看~

猜你喜欢

转载自blog.csdn.net/leelitian3/article/details/79948422