zoukankan      html  css  js  c++  java
  • EXPPP

    真值表法求取主析取范式及主合取范式的实现

    +++

    1.需要了解什么

    1.栈(stack)

    • 怎么理解栈: 想象有一个圆桶,可以往桶里放东西。如果想要从桶里拿东西,只能拿出桶最上面的东西,因为之前放进去的东西在下面,拿不到。这也是栈这个容器最重要的性质:先进去的元素后出来

    • 本次实验为什么要用到栈? 我们平常碰到的式子,运算符在数字中间的,被叫做中缀表达式,我们计算时可以按照优先级情况,先算优先级高的再算优先级低的。但是计算机不可以做到挑选这个动作,只会机械化的顺序遍历整个式子。所以本实验我们要把中缀表达式通过来转化为后缀表达式(计算机方便阅读的式子形式)。

    • https://blog.csdn.net/walkerkalr/article/details/22798365 中缀转后缀方法(转载)

    • https://blog.csdn.net/xiaoniba1024/article/details/6575523 后缀表达式求值(转载)

    #include <stack>//要有这个头文件
    
    stack<int> s;//一个盛放int的栈,刚创建是栈是空的
    s.empty();//判断栈s是否为空,是空的返回1,否则返回0
    s.top();//栈顶元素,可以理解成圆桶最上面的元素
    s.pop();//删除栈顶元素,可以理解成拿走圆桶最上面的元素
    int m; s.push(m);//元素m入栈,而且在栈顶,也就是把元素m放到圆桶里,并且位于桶内元素的最上面
    

    2.映射(map)

    • 什么是映射? 举个简单的例子,我们每个人都有名字,也都有学号通过学号我们可以知道是哪个人。这就是最简单的映射,把名字映射成学号,然后通过学号就可以知道人的名字。

      在c++中,我们可以将任意两种数据类型进行映射,比如:map<char, int> m;m就是字符型和整

    • 本实验为什么要用到映射? 因为运算符(非,合取,析取,单条件,双条件)是有优先级的,直接比较字符的优先级有点麻烦,但是比较两个int的大小时就很简单,所以我们把char(运算符)映射成int,然后通过比较int的大小来判断运算符优先级

    #include <map>//要用到的头文件
    
    //我们规定数字大的优先级高
    map<char, int> m = { { '!', 5 },{ '&', 4 },{ '|', 3 },{ '>', 2 },{ '=', 1 } };//通过映射来标明运算符优先级
    
    //我们可以把char看成是m的下标来访问对应的int
    //如何访问map
    cout << m['!'];//输出结果是5
    
    string s = "!";
    cout << m[s[0]];//输出结果是5(此时不用单引号)
    

    3.集合(set)

    • 集合set的性质 : 有序(自动排序),唯一性(每种元素都不重复),如果向set中加入一个已有的元素,集合不会发生变化。

    • 本实验为什么要用到set? 因为命题变元名字和个数是不确定的,而且范式要求命题变元按照字典序输出,所以我们要通过set来收集命题变元并且按照字典序排列(方便使用)。

    #include <set>//要用到的头文件
    
    set<char> s;//盛放char元素的集合
    
    //用到的函数
    char c;
    s.insert(c);//字符c进入集合
    

    +++

    2.函数各部分代码
    • 主函数
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <string>
    #include <algorithm>
    #include <stack>
    #include <map>
    #include <set>
    //有的头文件可能没用带,习惯了就都打上去了
    
    using namespace std;
    
    map<char, int> m = { { '!', 5 },{ '&', 4 },{ '|', 3 },{ '>', 2 },{ '=', 1 } };//通过映射来标明运算符优先级
    map<char, int> mm;//存放命题变元的映射
    stack<char> temp;//临时栈,处理符号入栈优先次序
    string Nocal_Suffix_Formula;//未计算前的后缀表达式
    set<char> Set;
    string formula;//存放输入
    int num;//命题变元个数
    string element;//存放命题变元
    
    //多开一列是为了标记,如果真值情况为真则在最后一列标记为true
    bool st[260][9];//存放不同情况下的真值情况
    
    int Dfs_Cnt(int n);//返回所有情况的个数
    void In_Stack(string);//中缀转后缀,入栈
    void Dfs_Process();//运算后缀表达式
    void Print_conjunction();//打印主合取范式
    void Print_extraction();//打印主析取范式
    
    int main()
    {
    	cout << "支持格式: '!' 非 -- '&' 合取 -- '|' 析取 -- '>' 单条件 '=' 双条件" << endl;
    
    	cout << "请输入命题变元个数 (字母大写): " << endl;
    	cin >> num;
    	cout << "Input your formula : " << endl;
    	cin >> formula;
    
        //中缀表达式(输入的表达式)转后缀表达式(通过栈实现)
    	In_Stack(formula);
    
        //运算后缀表达式的值(计算真值表最后一列的真值情况)
    	Dfs_Process();
    
        //计算主析取范式并输出
    	cout << "主析取范式: " << endl;
    	Print_extraction();
    	cout << endl;
    
        //计算主合取范式并输出
    	cout << "主合取范式: " << endl;
    	Print_conjunction();
    	cout << endl;
    
    	system("pause");
    	return 0;
    }
    
    • int Dfs_Cnt(int n);//返回所有情况的个数
    int Dfs_Cnt(int n)
    {
    	return 1 << n;//本句话是计算2的n次方,位运算我之后教你
    }
    
    • void In_Stack(string);//中缀转后缀,入栈
    void In_Stack(string s)
    {
    	for (unsigned int i = 0; i < s.size(); i++)
    	{
    		char x = s[i];
    
            judge_again://goto语句
    		if (x >= 65 && x <= 90)
    		{
    			Nocal_Suffix_Formula += x;
    			Set.insert(x);
    		}
    		else if (x != '(' && x != ')')
    		{
    			if (temp.empty() || temp.top() == '(') temp.push(x);
    			else if (m[x] > m[temp.top()]) temp.push(x);
    			else(m[x] <= m[temp.top()] && !temp.empty())
    			{
    					Nocal_Suffix_Formula += temp.top();
    					temp.pop();
                    goto judge_again;//goto语句
    			}
    		}
    		else
    		{
    			if (x == '(') temp.push(x);
    			else
    			{
    				while (temp.top() != '(')
    				{
    					Nocal_Suffix_Formula += temp.top();
    					temp.pop();
    				}
    
    				temp.pop();
    			}
    		}
    	}
    
    	while (!temp.empty())//最后把栈中剩余符号压入后缀表达式
    	{
    		Nocal_Suffix_Formula += temp.top();
    		temp.pop();
    	}
    }
    
    • void Dfs_Process();//运算后缀表达式

    这里有一点你可能不懂,看到这里我打电话教你

    void Dfs_Process()
    {
    	int i = 0;
    	for (auto x : Set)
    	{
    		element += x;//从集合中读取命题变元到element中
    		mm.insert({ x, i++ });
    	}
    
    	for (int op = 0; op < Dfs_Cnt(num); op++)//所有情况
    	{
    		for (int i = 0; i < num; i++) st[op][i] = (op >> i & 1);//位运算取不同真值情况
    
    		stack<char> Cal_Suffix_Formula;//计算过的后缀表达式,只有一个元素且栈顶为'0' or '1'
    
    		for (unsigned int i = 0; i < Nocal_Suffix_Formula.size(); i++)
    		{
    			char t = Nocal_Suffix_Formula[i];
    
    			if (t >= 65 && t <= 90)//如果是命题变元,赋值后入栈
    			{
    				int j = st[op][mm[t]];
    				Cal_Suffix_Formula.push(j);
    			}
    			else if (t == '!')//非运算
    			{
    				Cal_Suffix_Formula.top() ^= 1;
    			}
    			else
    			{
    				int r = Cal_Suffix_Formula.top();
    				Cal_Suffix_Formula.pop();
    				int l = Cal_Suffix_Formula.top();
    				Cal_Suffix_Formula.pop();
    
    				if (t == '&')//合取运算
    				{
    					if (l + r == 2) Cal_Suffix_Formula.push(1);
    					else Cal_Suffix_Formula.push(0);
    				}
    				else if (t == '|')//析取运算
    				{
    					if (l + r == 0) Cal_Suffix_Formula.push(0);
    					else Cal_Suffix_Formula.push(1);
    				}
    				else if (t == '>')//单条件
    				{
    					if (l == 1 && r == 0) Cal_Suffix_Formula.push(0);
    					else Cal_Suffix_Formula.push(1);
    				}
    				else//双条件
    				{
    					if (l == r) Cal_Suffix_Formula.push(1);
    					else Cal_Suffix_Formula.push(0);
    				}
    			}
    		}
    
    		st[op][num] = Cal_Suffix_Formula.top();
    	}
    }
    
    • void Print_conjunction();//打印主合取范式

    变量flag1和flag2的作用是因为n个命题变元中间有n-1个运算符,用来控制输出格式的

    void Print_conjunction()
    {
    	int flag1 = 1;
    	for (int i = 0; i < Dfs_Cnt(num); i++)//所有情况
    	{
    		if (!st[i][num])//如果真值情况为假
    		{
    			if (flag1)
    			{
    				flag1 = 0;
    				cout << '(';
    
    				int flag2 = 1;
    				for (int j = 0; j < num; j++)//变元真值情况
    				{
    					if (flag2)
    					{
    						flag2 = 0;
    						if (st[i][j]) cout << '!';
    						cout << element[j];
    
    						continue;
    					}
    
    					cout << '|';
    					if (st[i][j]) cout << '!';
    					cout << element[j];
    				}
    				cout << ')';
    			}
    			else
    			{
    				cout << "&(";
    
    				int flag2 = 1;
    				for (int j = 0; j < num; j++)//变元真值情况
    				{
    					if (flag2)
    					{
    						flag2 = 0;
    						if (st[i][j]) cout << '!';
    						cout << element[j];
    
    						continue;
    					}
    
    					cout << '|';
    					if (st[i][j]) cout << '!';
    					cout << element[j];
    				}
    
    				cout << ')';
    			}
    		}
    	}
    }
    
    • void Print_extraction();//打印主析取范式

    变量flag1和flag2的作用是因为n个命题变元中间有n-1个运算符,用来控制输出格式的

    void Print_extraction()
    {
    	int flag1 = 1;
    	for (int i = 0; i < Dfs_Cnt(num); i++)//所有情况
    	{
    		if (st[i][num])//如果真值情况为真
    		{
    			if (flag1)
    			{
    				flag1 = 0;
    				cout << '(';
    
    				int flag2 = 1;
    				for (int j = 0; j < num; j ++ )//变元真值情况
    				{
    					if (flag2)
    					{
    						flag2 = 0;
    						if (!st[i][j]) cout << '!';
    						cout << element[j];
    
    						continue;
    					}
    					
    					cout << '&';
    					if (!st[i][j]) cout << '!';
    					cout << element[j];
    				}
    				cout << ')';
    			}
    			else
    			{
    				cout << "|(";
    
    				int flag2 = 1;
    				for (int j = 0; j < num; j++)//变元真值情况
    				{
    					if (flag2)
    					{
    						flag2 = 0;
    						if (!st[i][j]) cout << '!';
    						cout << element[j];
    
    						continue;
    					}
    
    					cout << '&';
    					if (!st[i][j]) cout << '!';
    					cout << element[j];
    				}
    
    				cout << ')';
    			}
    		}
    	}
    }
    
  • 相关阅读:
    facade 对于有很多接口的提供一个简单的接口
    UML 常用符号 转
    <html> 按钮换行
    javascript: change the content of table
    [转载]当今计算机软件开发和应用领域最重要十种关键技术
    括号匹配算法
    求出给定两日期段 之间的交集算法
    表中的数据导出为insert语句的简单方法
    投色子下注的小程序
    未来社会发展趋势__P2p时代
  • 原文地址:https://www.cnblogs.com/scl0725/p/12698170.html
Copyright © 2011-2022 走看看