用 Mathematica 搜索生命游戲中的靜物(二)

之前寫的那篇《用 Mathematica 搜索生命游戲中的靜物》谜嫉,由于自己寫的部分多了一點(雖然關鍵的一步還是用 Mathematica 自帶的 FindPath 函數(shù)),特別慢谓形,還特別耗內存泡躯。果然對我這種完全不懂算法的人,就應該把所有的事情都交給 Mathematica 才對阱佛。


生命游戲里的每個細胞的狀態(tài)可以看成一個布爾值台丛,一個(大小有限的)圖樣則可以看成一個由布爾值組成的二維數(shù)組耍缴。于是,要尋找滿足某些條件圖樣挽霉,就相當于要求這些布爾值滿足某個方程防嗡。于是,這是一個布爾可滿足性問題(SAT)侠坎。Mathematica 里有個叫 SatisfiabilityInstances 的函數(shù)就是干這個的蚁趁。


我們先把要滿足的條件用 Mathematica 代碼寫出來。

假設我們要找的圖樣在一個 xy 的長方形里邊实胸,它對應的數(shù)組可以用 Array[b, {x, y}] 來表示他嫡,這里每個 b[i,j] 表示一個細胞。

靜物(Still life)指的是穩(wěn)定的圖樣庐完,它要滿足下面兩個條件:

  • 每個活細胞周圍的活細胞個數(shù)必須是2或者3钢属,
  • 每個死細胞周圍的活細胞個數(shù)不能是3。

我們先寫一個函數(shù)來數(shù)一個細胞周圍的活細胞個數(shù):

NeighborCount[k_, {i_, j_}] :=
  BooleanCountingFunction[{k},
   Delete[Catenate[Array[b, {3, 3}, {i, j} - 1]], 5]];

然后每個細胞要滿足的條件可以寫成這樣:

StillLifeCondition[i_, j_] :=
  (b[i, j] && NeighborCount[{2, 3}, {i, j}]) ||
   (! b[i, j] && ! NeighborCount[{3}, {i, j}]);

此外门躯,這個圖樣是有限的淆党,邊界之外的細胞總是死的。于是讶凉,整個圖樣要滿足的條件可以寫成:

Array[StillLifeCondition[##] /.
   b[i_, j_] /; i < 1 || i > x || j < 1 || j > y :> False &,
 {x, y} + 2, 0, And]

然后我們可以用 SatisfiabilityInstances 函數(shù):

SearchStillLife[x_, y_] :=
 ArrayReshape[
  SatisfiabilityInstances[
    Array[StillLifeCondition[##] /.
       b[i_, j_] /; i < 1 || i > x || j < 1 || j > y :> False &,
     {x, y} + 2, 0, And],
    Catenate[Array[b, {x, y}]]][[1]], {x, y}];

然后我們就可以用這個 SearchStillLife 函數(shù)來找靜物染乌。不過,它出奇地慢懂讯,找個16乘16大小的靜物也要花上44秒:

ArrayPlot[Boole@SearchStillLife[16, 16], Mesh -> All] // AbsoluteTiming

能不能快一些荷憋?

我不清楚這個 SatisfiabilityInstances 函數(shù)用的是什么算法。不過看了一下維基百科褐望,SAT 問題最常用的好像是一種叫 DPLL 的算法勒庄。我完全不懂算法串前,就不管它的細節(jié)了」Γ總之,它要求輸入的是一個合取范式(CNF)减宣。

于是盐须,我們可以試試把條件轉換成 CNF,也就是說漆腌,把前面的 StillLifeCondition 函數(shù)改寫成:

StillLifeCondition[i_, j_] :=
 BooleanConvert[
  (b[i, j] && NeighborCount[{2, 3}, {i, j}]) ||
   (! b[i, j] && ! NeighborCount[{3}, {i, j}]), "CNF"];

后面的 SearchStillLife 函數(shù)定義不變≡舻耍現(xiàn)在再找一遍16乘16的靜物,果然快了很多闷尿,只花了2.2秒塑径。不過找出來的靜物……

居然是空的……

看來 SatisfiabilityInstances 函數(shù)在處理 CNF 時用的是和別的形式不同的算法。滿足條件的靜物有很多填具,但 SatisfiabilityInstances 只會輸出其中的第一個统舀。而在輸入 CNF 的時候,不巧空靜物就是這“第一個”劳景。

只能找到空靜物的代碼并沒有什么用誉简。我們可以試試給原來的代碼引入一點隨機的因素,讓這個空靜物不再是“第一個”盟广。比如說闷串,把原來數(shù)組異或上一個隨機的數(shù)組,把前面的 SearchStillLife 函數(shù)改寫成:

SearchStillLife[x_, y_] := 
 Block[{r = RandomChoice[{True, False}, {x, y}]},
  MapThread[Xor, {r,
    ArrayReshape[
     SatisfiabilityInstances[
       Array[StillLifeCondition[##] /. 
           b[i_, j_] /; i < 1 || i > x || j < 1 || j > y :> False /.
                   b[i_, j_] :> Xor[b[i, j], r[[i, j]]] &,
        {x, y} + 2, 0, And],
       Catenate[Array[b, {x, y}]]][[1]], {x, y}]}, 2]]

現(xiàn)在完整的代碼變成了:

NeighborCount[k_, {i_, j_}] :=
  BooleanCountingFunction[{k},
   Delete[Catenate[Array[b, {3, 3}, {i, j} - 1]], 5]];

StillLifeCondition[i_, j_] :=
 BooleanConvert[
  (b[i, j] && NeighborCount[{2, 3}, {i, j}]) ||
   (! b[i, j] && ! NeighborCount[{3}, {i, j}]), "CNF"];

SearchStillLife[x_, y_] := 
 Block[{r = RandomChoice[{True, False}, {x, y}]},
  MapThread[Xor, {r,
    ArrayReshape[
     SatisfiabilityInstances[
       Array[StillLifeCondition[##] /. 
           b[i_, j_] /; i < 1 || i > x || j < 1 || j > y :> False /.
                   b[i_, j_] :> Xor[b[i, j], r[[i, j]]] &,
        {w, h} + 2, 0, And],
       Catenate[Array[b, {w, y}]]][[1]], {x, y}]}, 2]]

這次能找到好看的靜物了筋量,花的時間時間在2.7秒左右:

SeedRandom[233];
ArrayPlot[Boole@SearchStillLife[16, 16], Mesh -> All] // AbsoluteTiming

再試試大一點的靜物烹吵,比如說64乘64的〗拔洌花了大概46秒肋拔。


找完了靜物,再來找振蕩子(Oscillator)呀酸。振蕩子是隨時間周期變化的圖樣只损。于是,我們可以給那個布爾值的數(shù)組增加一個時間的維度七咧,寫成 Array[b, {p, w, h}]跃惫,這里 p 是它的周期。它們滿足的條件也要作出相應的修改:

NeighborCount[k_, {t_, i_, j_}] := 
  BooleanCountingFunction[{k}, 
   Delete[Flatten[Array[b, {1, 3, 3}, {t, i - 1, j - 1}]], 5]];

OscillatorCondition[t_, i_, j_] := 
  BooleanConvert[
   (b[t, i, j] && (b[t + 1, i, j] ? 
        NeighborCount[{2, 3}, {t, i, j}])) ||
    (! b[t, i, j] && (b[t + 1, i, j] ? 
        NeighborCount[{3}, {t, i, j}])), "CNF"];

SearchOscillator[p_, w_, h_] :=
 Block[{r = RandomChoice[{True, False}, {p, w, h}]},
  MapThread[
   Xor, {r, 
    ArrayReshape[
     SatisfiabilityInstances[
       Array[OscillatorCondition[##] /.
           {b[t_, i_, j_] /; i < 1 || i > w || j < 1 || j > h :> False, 
            b[0, i_, j_] :> b[p, i, j]} /.
          b[t_, i_, j_] :> Xor[b[t, i, j], r[[t, i, j]]] &,
        {p, w + 2, h + 2}, 0, And],
       Flatten[Array[b, {p, w, h}]]][[1]], {p, w, h}]}, 3]]

(代碼里的這個 ? 符號表示的是等價艾栋,Mathematica 里顯示為 ?爆存。)

可能因為靜物比較稀少,速度比找靜物時慢了很多蝗砾,找一個周期2的16乘16的靜物花了11秒:

SeedRandom[233];
ArrayPlot[#, Mesh -> All] & /@ 
  Boole@SearchOscillator[2, 16, 16] // AbsoluteTiming

周期3的就更慢了先较,找下面這個振蕩子花了14分鐘携冤。

更高的周期我就不敢試了。

應該會有更快的辦法闲勺。有好的建議歡迎評論曾棕。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市菜循,隨后出現(xiàn)的幾起案子翘地,更是在濱河造成了極大的恐慌,老刑警劉巖癌幕,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衙耕,死亡現(xiàn)場離奇詭異,居然都是意外死亡勺远,警方通過查閱死者的電腦和手機橙喘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胶逢,“玉大人厅瞎,你說我怎么就攤上這事〕踝梗” “怎么了磁奖?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長某筐。 經(jīng)常有香客問我比搭,道長,這世上最難降的妖魔是什么南誊? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任身诺,我火速辦了婚禮,結果婚禮上抄囚,老公的妹妹穿的比我還像新娘霉赡。我一直安慰自己,他們只是感情好幔托,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布穴亏。 她就那樣靜靜地躺著,像睡著了一般重挑。 火紅的嫁衣襯著肌膚如雪嗓化。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天谬哀,我揣著相機與錄音刺覆,去河邊找鬼。 笑死史煎,一個胖子當著我的面吹牛谦屑,可吹牛的內容都是我干的驳糯。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼氢橙,長吁一口氣:“原來是場噩夢啊……” “哼酝枢!你這毒婦竟也來了?” 一聲冷哼從身側響起悍手,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤帘睦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后谓苟,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體官脓,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡协怒,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年涝焙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孕暇。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡仑撞,死狀恐怖,靈堂內的尸體忽然破棺而出妖滔,到底是詐尸還是另有隱情隧哮,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布座舍,位于F島的核電站沮翔,受9級特大地震影響,放射性物質發(fā)生泄漏曲秉。R本人自食惡果不足惜采蚀,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望承二。 院中可真熱鬧榆鼠,春花似錦、人聲如沸亥鸠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽负蚊。三九已至神妹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間家妆,已是汗流浹背灾螃。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留揩徊,地道東北人腰鬼。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓嵌赠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親熄赡。 傳聞我的和親對象是個殘疾皇子姜挺,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354