原文链接: 15迷问题的证明(15 puzzle)
上一篇: 汇编 斐波那契数列
下一篇: vue 重新挂载 解决数据刷新问题
在线demo
https://github.com/imshubhamsingh/15-puzzle
https://clatterrr.com/archives/1736
首先我们来回顾一下《计算机算法基础》这本书上对15-迷问题叙述:在一个分成16格的方形棋盘上放有15个遍了号的牌(如上图)。对这些牌给定一种初始排列图(a),要求通过一系列合法的移动将这初始排列转换成图(b)所示的那样的目标排列(只有领接于空格的牌移动到空格才是合法的移动)。
逆序对的数目计算是没有16的!!!!!!!!!
易于看出棋盘上这些牌有16!种不同的排列。对于任意给定的初始状态能否达到图(b)所示的目标状态?实际上任意初始状态只能达到它所有状态的一半,也就是说不是任意的初始状态都可能达到目标状态!我们先给棋盘的方格编上1-16的号码。位置 i 是在图(b)所示的目标排列中放 i 号牌的方格位置,位置16是空格的位置。假设 POSITION( i )是编号为 i 的那块牌在初始状态下的编号,1<=i<16;POSITION( 16 )表示空格的位置。对于任意一种状态,设 LESS(i) 是使牌 j 小于牌 i 且 POSITION( j ) > POSITION( i )的数目(j 为任意满足条件的数,1<= j < i)。例如,对于图 7.2(a) 所示的状态,有 LESS(1) = 0,LESS(4) = 1 和 LESS(12) = 6。在初始条件下,如果空格在图(c)的阴影位置中的某一格处,则令 X = 1;否则令 X = 0。于是有如下定理。
定理: 当且仅当 是偶数,图(b)所示的目标状态可能由此初始状态到达。
用通俗一定的话来说,LESS( i ) 就是数字比 i 小,但是位置在 i 后的牌的总和。如果初态可解,那么 1 至 16 的 LESS( i )的加和加上 X 必为偶数。下面我用递归的方法来证明这个问题。证明的过程不是由初始态向目标态推演,而是由目标态向初始态转变。因为如果给定初始态可转变成目标状态,那么目标状态在有限的步长下也可以转变成初始状态。所以对定义的证明可以转化为证明从目标态出发,任意步移动后的状态都满足定理所述的条件。
如果从目标态出发经过 K 步可以转变成初始状态,那么如果第1 步和第 K-1 成立,那么第 K 步就一定成立。
证明:
设 p = LESS(I) 的加和 + X(定理公式); 则目标态 p = 0;
-
第 1 步可以选择 15 移动到空位,也可以选择 12 移动到空位。如果选择 15 移动,那么LESS(15)=0, LESS(16) = 1, X = 1; p = 2 公式成立;如果选择 12 移动到空位,那么 LESS(12) = 3,LESS(16) = 4; X = 1; p = 8 公式仍然成立。(空格的牌号为16,为最大值)
-
假设第 K-1 步成立,那么对于第 K 步也必定要成立。此时空格可以位于任意位置,假设处于 i,它可以选择上、下、左、右任意的位置移动(如果范围允许)。在左移的情况下,假设左边数字为 j ,LESS( 16) = LESS(16)+1;LESS( j )不变,X = X +1 or X = X -1,其它任意值的LESS值也不会变化;所以 p = p 或者 p = p+2;因此空格左移成立,右移证明与左移相似。在上移的情况下,假设上一格的数字为 j,位于空格和 j 之间的牌共有三张。当空格移上之后 LESS(16) = LESS(16)+4;p = p + (+/-)1+(+/-)1+(+/-)1;X = X +1 or X = X -1;可以看出这三个数相加仍为偶数。取p = p + (+/-)1+(+/-)1+(+/-)1的原因是 j 后移三格后与这三个数字肯定分别存在大小关系,因此就必定有+1或-1的操作。下移与上移类似。
证毕。
该证明可以进一步拓广为 N-迷 (N puzzle) 问题。
更新后的H函数可以求解1000步以内的问题
// H 函数,计算与起始状态的距离
function H(items) {
return items.reduce(
(pre, item, index) => {
if (item == 16)
return pre
let x1 = Number.parseInt((item - 1) / 4)
let y1 = (item - 1) % 4
let x2 = Number.parseInt(index / 4)
let y2 = index % 4
// console.log(x1, y1, x2, y2)
return pre + Math.abs(x1 - x2) + Math.abs(y1 - y2)
}, 0
)
}