Haskell編程解決九連環(1)— 數學建模

摘要

本文是該系列文章中的第一篇。將對中國傳統的智力玩具九連環作簡要的介紹。並從數學的角度對其建模。所謂建模就是在定義的基礎之上羅列一系列的可證實的定理和推論,從而爲該問題的解決創建堅實的理論基礎。全部這些都將在本系列的後續文章中做爲編程實現的指導和基礎。算法

緣起

記得在完成學業,開始工做後不久,偶爾在路邊攤上看到九連環,喜歡並買了回來。很快發現其背後是一個純粹乾淨的遞歸,因而能夠熟練的解開並安裝還原。彼時也未曾寫成電腦程序。要寫的話也多半是使用C/C++/Java/Python這類命令式編程語言,簡潔明瞭,沒有多少難度,也沒有什麼激動人心之處。後來東西丟了,也就多年沒再玩過。前段時間十一歲的兒子在網上爲本身淘來一個,竟然也能熟練地拆裝,這多少令我有些驚訝。因而想着是否能夠乘此機會教教他電腦編程。不成想思考的時候頭腦裏出現的都是這幾年努力學習的Haskell,發現Haskell跟數學是如此的接近,也一如數學通常優美。奈何小傢伙不願學習這個,隨他去吧,也許緣分未到呢。好歹把想明白的東西記錄成文,萬一未來有機會,有緣分,至少不須要從頭作起。編程

定義

九連環的綜合信息能夠參見維基百科中的條目。一些圖片以下,依次是完整未解的,解到一半的和徹底解開爲兩部分的九連環:segmentfault

完整未解的九連環解到一半的九連環徹底解出的九連環,分爲兩部分

能夠看到:編程語言

  • 九連環由兩個部分組成,一個「U」型的長劍。另外一部分是九個經過直杆連在一塊兒的圓環。
  • 長劍的「U」型的彎曲頂端叫作「刀口」,全部的圓環都必須從刀口裝上或取下。
  • 長劍的另外一端「U」型的雙尾被鏈接在一塊兒,稱爲「手柄」。圓環不能經過手柄端安裝或者拆卸。故而在安裝或是拆卸九連環的整個過程當中「手柄」均可以做爲握持的部位,不須要放開。
  • 在徹底裝好時,咱們指定最靠近「刀口」的圓環爲第1環,依次是第2環,第3環,直至第9環。

九連環各部位示意圖

  • 定義問題「拆卸n連環」爲:當第1,2, .. ,n環均處於裝上狀態時,經過最少的步驟使第1,2, .. ,n環均轉變爲拆下的狀態,記爲 takeOff(n)。
  • 定義問題「裝上n連環」爲:當第1,2, .. ,n環均處於拆下狀態時,經過最少的步驟使第1,2, .. ,n環均轉變爲裝上的狀態,記爲 putOn(n)。
  • 定義在可能(條件許可)的狀況下裝上或拆下一個圓環(第n環)的動做爲一個步驟,記做ON n或是OFF n。多個步驟的有序排列稱爲一個步驟序列。

基本操做

基本的操做經過實物演示比較容易理解,若是讀者有一個九連環在手,就能夠很容易地驗證這些操做。學習

  1. 第1環在任什麼時候候均可以最少以1個步驟被裝上或者拆下,記裝上的動做爲 ON 1,拆下的動做爲 OFF 1
  2. 若是第1環和第2環都處於裝上的狀態,最少用2個步驟所有拆卸下來:1)拆下第2環 2)拆下第1環,步驟序列爲 [OFF 2, OFF 1]
  3. 若是第1環和第2環都處於拆下的狀態,最少用2個步驟所有安裝上去:1)安裝第1環 2)安裝第2環,步驟序列爲 [ON 1, ON 2]
  4. 在第1,2, .. , n - 2 環均處於拆下狀態,而且第 n - 1 環處於裝上狀態時,最少以1個步驟安裝或拆下第n環,該動做爲 ON n 或者 OFF n
  5. 當n>1時,若是第4點的前提條件不成立,則第n環不能被拆下或裝上。這一點很重要,在數學概括法的基礎上能夠證實根據第4點所提供的沒有反覆的解法步驟是最少的,從而該方法獲得的解才能成爲問題takeOff(n)或是putOn(n)的解。

定理與推論

定理1takeOff(1)的解法步驟序列爲[OFF 1]putOn(1)的解法步驟序列爲[ON 1]
根據基本操做1,定理1顯而易見spa

定理2takeOff(2)的解法步驟序列爲[OFF 2, OFF 1]putOn(2)的解法步驟序列爲[ON 1, ON 2]
根據基本操做2和3,定理2顯而易見。code

定理3:當n>2時,takeOff(n)的解法依次由如下幾個部分組成:1) takeOff(n-2) 2) OFF n 3) putOn(n-2) 4) takeOff(n-1);而putOn(n) 依次由如下幾個部分組成 1) putOn(n-1) 2) takeOff(n-2) 3) ON n 4) putOn(n-2)
定理3能夠經過數學概括法證實,其中的起始步驟爲定理1和定理2,遞推步驟爲基本操做4blog

推論1takeOff(n)的解法步驟序列和putOn(n)的解法步驟序列互爲逆反序列,步驟序列A的逆反序列能夠經過如下步驟獲得: 1) 將序列A反序獲得序列A' 2) 對A'中的每一個步驟取其反動做,反動做的定義爲ON nOFF n互爲反動做。能夠看出若是B是A的逆反序列,那麼在B的基礎上取逆反序列,其結果就等於A。
推論1一樣能夠用數學概括法證實,當n<=2時,經過定理1和定理2顯而易見。當n>2時,經過定理3提供的步驟能夠遞推獲得。遞歸

推論2takeOff(n)的解法步驟序列和putOn(n)的解法步驟序列含有的步驟數目相等。
根據推論1和步驟序列的逆反序列定義和算法,推論2顯而易見。圖片

推論3:對於任何整數m, n,若是m>n,那麼第m環的狀態(裝上或是卸下)不影響takeOff(n)或者putOn(n)的解,同時解決takeOff(n)或者putOn(n)問題也不會改變第m環的狀態。
這條推論仍然能夠用數學概括法證實,當n<=2時,經過定理1和定理2顯而易見。當n>2時,定理3提供的步驟不受第m環的影響而且不會操做第m環。

遞歸模型

至此咱們已經擁有建立一個遞歸模型所須要的所有理論基礎。定理1和定理2肯定了遞歸結束的基本條件;定理3描述了怎樣把一個較大的問題拆分紅幾個較小的問題,從而一步步拆分直至到達遞歸結束的基本條件;推論3事實上明確了咱們能夠在整個過程當中放心地把任何一個較大的問題拆分紅多個較小的問題;而推論1和推論2使得咱們在某些狀況下能使用等價的替代算法,從而簡化編寫的實現代碼。
下圖顯示了takeOff(5)怎樣一步步被拆分紅更小的問題,最終達到基本條件的過程。
takeOff(5)的求解過程
能夠看到該求解過程造成了一棵樹,在樹中將全部葉結點所包含的動做依次鏈接成一個序列,就是根節點所表明的問題takeOff(5)的解法步驟序列。能夠看到這個序列是[OFF 1, OFF 3, ON 1, OFF 2, OFF 1, OFF 5, ON 2, ON 1, OFF 1, ON 3, ON 1, OFF 2, OFF 1, OFF 4, ON 2, ON 1, OFF 1, OFF 3, ON 1, OFF 2, OFF 1],一共21個步驟。
觀察解法樹的最左側分支直到末端的葉結點,咱們看到經過不斷的拆分takeOff(n)takeOff(n-2)再到takeOff(n-4)最終到達基本條件takeOff(1)或是takeOff(2),特別地,當n爲奇數的時候將最終拆分到takeOff(1),爲偶數時將最終拆分到takeOff(2)。這樣就獲得一個有趣的推論:拆解n連環時,若是n爲奇數,則第一步是OFF 1,爲偶數時第一步爲OFF 2。該推論對於自頂向下地編程實現沒有什麼特殊的意義,但在實際拆卸九連環的操做中,因爲9是奇數,記得第一步是OFF 1,也就是拆下第1環。

下一篇:《Haskell編程解決九連環(2)— 多少步驟?》

相關文章
相關標籤/搜索