用 Go 語言實現劉謙春晚魔術,還原尼格買提汗流浹背的尷尬瞬間-

龍年春晚如期而至,想必大家都看(沒)完(眼)了(看)吧,今年春晚最搞笑節目,當屬劉謙的魔術:《守歲共此時》,不過笑點不是節目本身,而是本場魔術的 托兒(主持人)尼格買提。

小尼:已經開始流汗了 😅。

本文將帶大家一起用 Go 語言來還原下整個魔術的過程。

在開始寫代碼前,翻車畫面必須置頂 🤣:

這是一個公式魔術,即有着固定的套路,從數學角度來看是一個「約瑟夫問題」(嚴格證明可以在網上搜到,如果你感興趣可以去看下)。

不過,咱們今天不講枯燥的數學公式,固定套路是程序的強項,今天就用 Go 語言實現這個魔術的套路。

接下來,咱們直奔主題,回憶每一個步驟的同時,用程序將其實現。

步驟一、 準備 4 張撲克牌,隨意打亂,完成洗牌。

代碼實現如下:

// 我們使用 `int` 類型的切片 `pokers` 來存儲撲克牌
var pokers []int = []int{2, 7, 6, 5}
// 1. 洗牌,隨意打亂
rand.NewSource(time.Now().UnixNano())
rand.Shuffle(len(pokers), func(i, j int) {
  pokers[i], pokers[j] = pokers[j], pokers[i]
})
fmt.Printf("1. 洗牌後的牌:%v\n", pokers)

我們以截圖中劉謙手裏持有的 4 張撲克牌爲例,用一個 int 類型的 slice 存儲這些牌。

在 Go 中可以使用 rand.Shuffle 方法來打亂 slice 的順序。

執行程序,得到洗牌後的結果如下:

[6 7 5 2]

步驟二、 對摺,然後撕開,接着疊在一起,相當於撲克牌變成了兩份,double 了,4 張變 8 張。

這一步相當於 pokers 內容 * 2,代碼實現如下:

pokers = append(pokers, pokers...)
fmt.Printf("2. 對摺後的牌:%v\n", pokers)

複製後的 pokers 如下:

[6 7 5 2 6 7 5 2]

步驟三、 問問自己名字有幾個字,就從最上面拿出對應個數的牌放到底部。

例如「劉謙」名字有 2 個字,即將 slice 前 2 個元素取出,並放到 slice 最後,代碼實現如下:

pokers = append(pokers[2:], pokers[:2]...)
fmt.Printf("3. 問問自己名字有幾個字,就從最上面拿出對應個數的牌放到底部:%v\n", pokers)

得到新 pokers:

[5 2 6 7 5 2 6 7]

步驟四、 拿起最上面的 3 張牌,插入中間任意位置。

代碼實現如下:

pokers = append(pokers[3:7], pokers[0], pokers[1], pokers[2], pokers[7])
fmt.Printf("4. 拿起最上面的 3 張牌,插入中間任意位置: %v\n", pokers)

這裏將 pokers 前 3 個元素取出,並插入最後一個元素之前,得到新 pokers:

[7 5 2 6 5 2 6 7]

步驟五、 拿出最上面的 1 張牌,藏於祕密的地方,比如屁股下。

代碼實現如下:

top := pokers[0]
pokers = pokers[1:]
fmt.Printf("5. 拿出最上面的 1 張牌:%d, %v\n", top, pokers)

這裏使用 top 變量暫存最上面的 1 張牌:

7, [5 2 6 5 2 6 7]

此時得到 top 值爲 7,pokers 還剩 7 個元素。

步驟六、 如果你是南方人,從上面拿起 1 張牌;如果你是北方人,則從上面拿起 2 張牌;假如我們不確定自己是南方人還是北方人,那就乾脆拿起 3 張牌,然後插入中間任意位置。

這一步其實同第四步操作一樣,假設我們是北方人,代碼實現如下:

pokers = append(pokers[2:6], pokers[0], pokers[1], pokers[6])
fmt.Printf("6. 從上面拿出 2 張牌: %v\n", pokers)

得到新 pokers:

[6 5 2 6 5 2 7]

步驟七、 如果你是男生,從上面拿起 1 張牌;如果你是女生,則從上面拿起 2 張牌,撒到空中(扔掉)。

假設是男生,代碼實現如下:

pokers = pokers[1:]
fmt.Printf("7. 如果你是男生,從上面拿起 1 張牌;如果你是女生,則從上面拿起 2 張牌,撒到空中(扔掉):%v\n", pokers)

得到新 pokers:

[5 2 6 5 2 7]

步驟八、 魔法時刻,在遙遠的魔術的歷史上,流傳了一個七字真言「見證奇蹟的時刻」,可以帶給我們幸福。現在,我們每念一個字,從上面拿一張放到最底部,即需要完成 7 次同樣的操作。

我們可以用 for loop 來完成重複操作,代碼實現如下:

for range []string{"見", "證", "奇", "跡", "的", "時", "刻"} {
    pokers = append(pokers[1:], pokers[0])
}
fmt.Printf("8. 見證奇蹟的時刻:%v\n", pokers)

得到新 pokers:

[2 6 5 2 7 5]

步驟九、 最後一個環節,叫「好運留下來,煩惱丟出去」,在唸到「好運留下來」時,從上面拿起 1 張牌放入底部;在唸到「煩惱丟出去」時,從上面拿起 1 張牌扔掉,女生需要完成 4 次同樣的操作,男生需要完成 5 次同樣的操作。

[好運留下來]

[煩惱丟出去]

因爲第七步中我們選擇了男生,所以現在要完成 5 次同樣的操作,同樣適用 for loop 來完成,代碼實現如下:

for range []int{1, 2, 3, 4, 5} {
    // 好運留下來
    pokers = append(pokers[1:], pokers[0])
    // 煩惱丟出去
    pokers = pokers[1:]
}
fmt.Printf("9. 好運留下來,煩惱丟出去:%v\n", pokers)

最終得到的 pokers:

[7]

切片 pokers 中僅存一個元素,即手中只剩一張牌。

見證奇蹟

接下來就是見證奇蹟的時刻,拿出藏於屁股下的牌和手裏唯一剩下的一張牌對比,正是同一張牌:

打印 top 值和 pokers 中僅存的元素。

fmt.Printf("見證奇蹟:%d == %d", top, pokers[0])

結果毫無懸念:

見證奇蹟:7 == 7

程序完整日誌輸出如下:

1. 洗牌後的牌:[6 7 5 2]
2. 對摺後的牌:[6 7 5 2 6 7 5 2]
3. 問問自己名字有幾個字,就從最上面拿出對應個數的牌放到底部:[5 2 6 7 5 2 6 7]
4. 拿起最上面的 3 張牌,插入中間任意位置: [7 5 2 6 5 2 6 7]
5. 拿出最上面的 1 張牌:7, [5 2 6 5 2 6 7]
6. 從上面拿出 2 張牌: [6 5 2 6 5 2 7]
7. 如果你是男生,從上面拿起 1 張牌;如果你是女生,則從上面拿起 2 張牌,撒到空中(扔掉):[5 2 6 5 2 7]
8. 見證奇蹟的時刻:[2 6 5 2 7 5]
9. 好運留下來,煩惱丟出去:[7]
見證奇蹟:7 == 7

還原「尼格買提」神操作

在此必須回顧一下尼格買提尷尬瞬間,反覆尷尬 🤣:

仔細觀看回放就能發現,其實小尼錯在了第六步,小尼是北方人,所以應該從上面拿起 2 張牌,插入中間任意位置,但由於操作失誤,錯將拿起的第 2 張牌插入到最底部。

神操作在此:

即第六步代碼原本應該如下:

pokers = append(pokers[2:6], pokers[0], pokers[1], pokers[6])

到小尼這裏變成了:

pokers = append(pokers[2:6], pokers[0], pokers[6], pokers[1])

讀者可以自行修改代碼後嘗試運行下,我就不展示結果了,怕尷尬 😅,哈哈哈。

彩蛋

不知道大家有沒有注意到,在魔術表演結束時,觀衆席鏡頭左下角那個手裏拿着 AJ 的姐姐!

放大特寫:

AJ 姐姐滿臉洋溢着尷尬的笑容 🤣。

本文僅供大家消遣娛樂,順便學點編程 😄。

本文完整代碼示例我放在了 GitHub,歡迎查看。

聯繫我

微信:jianghushinian

郵箱:jianghushinian007@outlook.com

博客地址:https://jianghushinian.cn

引用

首發地址:https://jianghushinian.cn/2024/02/10/use-go-to-realize-liu-qian-s-2024-spring-festival-gala-magic/

本文示例代碼:https://github.com/jianghushinian/blog-go-example/blob/main/2024-spring-festival-gala-magic/main.go

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/qyko1om7wL1F9XAl0wLNJA