一文彻底理解卡诺图化简算法

一文彻底理解卡诺图化简算法

  • 写在前面
  • 背景
  • 算法
    • 第一步
    • 第二步
    • 第三步

本文基于Abdelrahman Elzedy的文章《A C++ Karnaugh Map Minimizer (Infinite Variables)》,在一些比较难懂的地方我加了一些自己的解释,原文链接: https://www.codeproject.com/Articles/649849/A-Cplusplus-Karnaugh-Map-Minimizer-Infinte-Variabl
大家可以在网站上可以下载exe文件和源代码

写在前面

这篇文章主要介绍了一种可以化简包含任意多个变量的卡诺图的算法。但是原文链接所提供的代码只实现了26个变量的卡诺图化简(与英文字母的个数相同)。

背景

本文假设读者具有基本的布尔代数和卡诺图知识。(译者注:大家如果忘了卡诺图是啥可以参考百度百科)

程序运行的时候长这样:
一文彻底理解卡诺图化简算法_第1张图片
程序的使用步骤如下:

  1. 输入卡诺图的类型(也就是变量的个数)
  2. 输入卡诺图中1的位置(在最后以-1结尾表示结束输入)
  3. 输入卡诺图中不关心的位置(在最后以-1结尾表示结束输入)
  4. 选择结果的类型(POS (Product of Sum)或SOP(Sum of Product))
  5. 得到卡诺图的化简结果

算法

算法主要有三步,在代码中,每一步都由一个类(class)实现。

  1. 按照用户的输入(值为1和不关心的位置)初始化卡诺图,并且将值为1和不关心的位置信息保存为二进制值
    (译者注:将位置值保存为二进制主要是为了方便后面的化简,因为使用二进制判断位置是否相邻比较方便,大家往后看就明白了~)
  2. 比较所有值为1和不关心的位置,得到所有可能的最小项
  3. 检查上一步得到的结果,去掉所有多余的最小项,得到最终的结果 (译者注:如果结果不唯一时程序会输出所有可能的结果)

一脸懵逼ing?当时我读这篇文章时到这里也是不知所云,只感觉不明觉厉。不过别担心,继续往下读,我会用栗子和图的方式让大家搞懂的(译者注)

第一步

在这一步中,我们首先得到了用户输入的卡诺图的信息(变元个数,值为1与不关心的位置)。然后我们就将这些信息转化为二进制的形式。
举个栗子:
首先我们要知道卡诺图是怎么编号的,一个典型的四元卡诺图的编号方式如下:

从图中很容易看出卡诺图的编号方式:横纵坐标都是按照格雷码的方式编号(不知道格雷码的编号方式请点击这里);处于横坐标的变量(图中的A、B)的值作为高位,处于纵坐标的变量(图中的C、D)的值作为低位,构成二进制数,这个二进制数所对应的十进制数就是该位置的编号。
如果我们需要输入一个如下图所示的卡诺图(其中空的位置都是0,X表示不关心的值):
一文彻底理解卡诺图化简算法_第2张图片
那我们首先应该输入4(卡诺图变量的个数),然后在下一行输入0 1 3 4 11 -1(其中-1表示这一行输入结束,0 1 3 4 11是值为1的位置编号),然后再另起一行,输入5 -1(5是X的位置编号,-1代表输入结束)。这样,用户的输入就完成了。
然后程序将用户输入的位置转化为二进制(因为有四个变量,所以转化为四位二进制)。值为1的位置二进制编号分别为0000,0001,0011,0100和1011;X的位置二进制编号为0101。
可能大家已经看出来了,卡诺图这样编号的好处就是将位置和二进制的编号联系在了一起。比如我们很容易推断,二进制编号为0000和0001的两个位置是相邻的。这样就为我们接下来卡诺图化简奠定了基础。

第二步

在这一步中,我们需要两两比较上一步保存的值为1和X的位置二进制编号。比较的步骤如下(设变元总个数为n):

  1. 如果有两个二进制数在n-1位上都相同,只有一位不同,则将不同的那一位设为 -1 ,相同位置数字不变,生成一个新的二进制数。
    例如上面栗子中的0000和0001,只有第四位不同,则生成新数000(-1)
  2. 将上面步骤中配过对的数字标记,在下一轮的比较中只加入新生成的数和未标记的数(即没有配对的数)。
  3. 重复以上步骤n次。(考虑最坏的情况:如果我们处理的是一个全部位置都是1的卡诺图,那我们必须重复n次)如果有一个数重复多次,则只保留一个。

接着第一步的栗子。在第一步中,我们已经得到了值为1的位置和值为X的位置的二进制编号:0000,0001,0011,0100、1011、0101。我们按照上面叙述的步骤走一遍:

Step1: 将每两个编号都进行比较,生成新数

如下图所示,红笔表示新数生成的过程,红色的√标记此数已经与其他的数配过对。
一文彻底理解卡诺图化简算法_第3张图片

Step2 将生成的新数加进序列,并剔除做过标记的数,得到第一次处理后的数列为:
000(-1), 0(-1)00, 00(-1)1, 0(-1)01, (-1)011 and 010(-1)

Step3: 重复以上步骤,得到的第二次处理后的数列为:
0(-1)0(-1), 0(-1)0(-1), 00(-1)1 and (-1)011
第三次处理后的数列为:
0(-1)0(-1), 0(-1)0(-1), 00(-1)1 and (-1)011
第四次处理后的数列为:
0(-1)0(-1), 0(-1)0(-1), 00(-1)1 and (-1)011
去掉重复的0(-1)0(-1)得到:
0(-1)0(-1), 00(-1)1 and (-1)011

其实这个过程模拟的就是我们化简卡诺图时画圈的过程。
处理完n次后,我们就可以将我们得到的二进制数列转化为字母表示,转化规则如下:

  1. 二进制中的第一位、第二位…第n位分别对应卡诺图中的第一个、第二个…第n个字母。
  2. 0表示对应的字母取非,1表示对应的字母不取非。
  3. 如果某位上的数字为(-1),则表示对应的字母应该忽略。

例如将上面的数列 0(-1)0(-1), 00(-1)1 and (-1)011 转化为字母形式应该为:
A’C’, A’B’D and B’CD(符号 ’ 表示取非,你懂的)
有两种特殊的情况需要考虑:

  1. 如果在某一步中数列出现每位都是 -1 的数,则说明对应卡诺图化简的结果为1;
  2. 如果初始的数列为空,则表示这是一个所有元素都为0的卡诺图,化简的结果为0。

第三步

在这一步中,我们将上一步得到的所有最小项进行组合,如果组合能够覆盖卡诺图中所有值为1的位置,则保留这个组合,得到所有可能的结果。最后输出所有最小项最少的组合。
例如在文章开始在这个图中:
一文彻底理解卡诺图化简算法_第4张图片
我们第二步结束后得到的结果应该是 B’D’, B’C, BD, AB’, AD and CD。当我们将这些项做排列组合时,B’D’+B’C+BD+AB’ 和B’D’+B’C+BD+AD+AB’都可以覆盖卡诺图中所有值为1的位置。但是因为前一个组合的最小项数为4,后一个为5,所以输出B’D’+B’C+BD+AB’ 而不输出B’D’+B’C+BD+AD+AB’。

以上就是卡诺图化简算法的全部内容。如果大家有什么不懂的地方,或者觉得算法有可以改进的地方,欢迎在评论区讨论~

你可能感兴趣的:(算法)