Boolean 简化9变量布尔表达式

Boolean 简化9变量布尔表达式,boolean,logic,Boolean,Logic,我试图创建一个tic-tac-toe程序作为一种心理训练,我将董事会状态存储为布尔值,如下所示: 我想简化这个布尔表达式 (a&b&c) | (d&e&f) | (g&h&i) | (a&d&g) | (b&e&h) | (c&f&i) | (a&e&i) | (g&e&c) 我的第一个想法是使用a,但在线上没有支持9个变量的解算器 问题是: 首先,我如何知道布尔条

我试图创建一个tic-tac-toe程序作为一种心理训练,我将董事会状态存储为布尔值,如下所示:

我想简化这个布尔表达式

(a&b&c) | (d&e&f) | (g&h&i) | (a&d&g) | (b&e&h) | (c&f&i) | (a&e&i) | (g&e&c)
我的第一个想法是使用a,但在线上没有支持9个变量的解算器

问题是:

首先,我如何知道布尔条件是否已经尽可能简单


第二:上面的布尔条件简化了什么?

一个选项是手动执行卡诺图。因为你有
9
变量,这就形成了一个2^4乘2^5的网格,这个网格相当大,从方程的外观来看,可能也不太有趣


通过检查,它看起来不像卡诺图会给你提供任何有用的信息(卡诺图基本上将
((!a)&b)|(a&b)
等表达式简化为
b
),因此在这种简化意义上,你的表达式已经尽可能简单了。但是,如果您想减少计算量,可以使用AND运算符在OR上的分布来计算出一些变量。

您知道,如果没有公共子项可提取(例如,如果您在两个不同的三元组中有“a&b”),这是一个尽可能简单的方法

你知道你的tic-tac-toe解决方案必须尽可能简单,因为任何一对盒子最多只能属于一条赢线(只有一条直线可以通过两个给定点),所以(a&b)不能在你正在检查的任何其他赢线中重复使用


(此外,“简单”可以指很多事情;具体说明你的意思可以帮助你回答自己的问题。)

思考这一点的最佳方式是一个人的想法。没有人会对自己说,“a和b和c,或者如果d和e和f”,等等。他们会说“一行中的任意三个,水平、垂直或对角。”

此外,您可以只进行四次检查(三行一对角线),然后将电路板旋转90度,然后再次进行相同的检查,而不是进行八次检查(三行、三列和两条对角线)

这就是你的结局。这些函数都假设棋盘是一个三乘三的布尔矩阵,其中true表示获胜符号,false表示未获胜符号

def win?(board)
  winning_row_or_diagonal?(board) ||
    winning_row_or_diagonal?(rotate_90(board))
end

def winning_row_or_diagonal?(board)
  winning_row?(board) || winning_diagonal?(board)
end

def winning_row?(board)
  3.times.any? do |row_number|
    three_in_a_row?(board, row_number, 0, 1, 0)
  end
end

def winning_diagonal?(board)
  three_in_a_row?(board, 0, 0, 1, 1)
end

def three_in_a_row?(board, x, y, delta_x, delta_y)
  3.times.all? do |i|
    board[x + i * delta_x][y + i * deltay]
  end
end

def rotate_90(board)
  board.transpose.map(&:reverse)
end
矩阵从这里开始旋转:

虽然这段代码比较详细,但每个函数的意图都很清楚。该代码不再是一个很长的布尔表达式,而是表达了tic-tac-toe的规则。

2。简化条件: 原意

a&b&c|d&e&f|g&h&i|a&d&g|b&e&h|c&f&i|a&e&i|g&e&c
可以简化为以下内容,知道&比|

e&(d&f|b&h|a&i|g&c)|a&(b&c|d&g)|i&(g&h|c&f)
短4个字符,在最坏的情况下执行18次
&
|
评估(原来的计算为23次) 没有更短的布尔公式(见下文)。如果你愿意,也许你可以找到另一个解决办法

1.确保我们得到最小的配方 通常,很难找到最小的公式。看看你是否更感兴趣。但在我们的例子中,有一个简单的证明

对于公式大小,我们将推理公式是最小的,其中对于变量
a
size(a)=1
,对于布尔运算
size(a&B)=size(a | B)=size(a)+1+size(B)
,对于求反
size(!a)=size(a)
(因此,我们可以假设没有成本)。 关于这个尺寸,我们的公式是37号

证明你不能做得更好的证据在于,首先要指出有8行需要检查,并且总是有一对字母区分两行。由于我们可以将这8个检查与剩余变量重新组合在不少于3个的共轭中,因此最终公式中的变量数量至少应为
8*2+3=19
,从中我们可以推断出最小树大小

详细证明

让我们假设一个给定的公式
F
最小的,格式为

  • F
    不能包含像
    这样的否定变量!a
    。为此,请注意,
    F
    应该是单调的,也就是说,如果它返回“true”(有一个获胜的行),那么将其中一个变量从
    false
    更改为
    true
    不应改变该结果
    F
    可以不求反地写入。更好的是,我们可以证明我们可以消除否定。下面,我们可以转换回和DNF格式,在中间删除否定的变量,或者用<代码> >真/ <代码>替换< < /P>
  • F
    不能包含类似于两个变量析取的子树
    a | b
    。 如果此公式有用且不能与
    a
    b
    交换,则意味着存在相互矛盾的赋值,例如
    F[a | b]=true
    F[a]=false
    ,因此,由于单调性,
    a=false
    b=true
    。同样,在这种情况下,将
    b
    转到
    false
    会使整个公式
    false
    ,因为
    false=F[a]=F[a | false]>=F[a | b](b=false)
    。 因此,有一行经过
    b
    ,这是真理的原因,它不能通过
    a
    ,因此,例如
    e=true
    h=true
    。 该行的检查通过表达式
    a | b
    进行测试
    b
    。然而,这意味着当
    a、e、h
    为真且所有其他设置为假时,
    F
    仍然为真,这与公式的目的相矛盾

  • 每个看起来像
    a&b
    的子树检查一个唯一的行。因此,最后一个字母应该出现在相应的析取
    (a&b |…)&{c肯定在这里的某个地方}
    ,否则这个叶子是无用的,a或b都可以安全删除