用 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