洗牌
計算機科學二級
Alice和Bob想為他們的撲克俱樂部制作一臺洗牌機。問題是,他們提出了不同的算法,因此無法決定應用哪一種算法:
愛麗絲的算法思路:從左到右依次,每張牌只和位于右邊的任何牌交換位置。
鮑勃的算法思路:從左到右一次,每張牌與任何其他牌交換位置。
誰的算法更好,或者這真的很重要嗎?
運用可視化直觀地看到兩種思路的算法比較:
brilliant_card_shuffleFigure_1.png
答案可能一開始是反直覺的。為什么Bob的算法不好?難道它不能生成所有的排列組合嗎?答案是肯定的,它可以生成所有的排列組合,但不是均勻的。總共有多達 n^n = n的n次方移動方式, 但問題在于,n副牌的排列也只有n!個排列組合的方式。
由于n^n不能整除n!,意味在一般情況下,并不是所有的排列組合都有同等的機會產生。
現在我們確信鮑勃的算法是壞的,但它有多壞呢?注意到了,如果一張牌被換到左邊,它就不能再往左走了!
還不服氣嗎? 讓數據來說話。
上圖左邊是Alice的算法,右圖是Bob的算法。
x軸是數字,y軸是它經過算法后最終的位置。越亮的單元格,數字最終出現在那個位置的頻率越高。正如你所看到的,大部分的數字最后都在它的左邊!
如果你對可視化是如何生成的感興趣,這里是代碼。
from random import randint
import matplotlib.pyplot as plt
n = 1000 #模擬1000次洗牌
x_data = []
y_data = []
# start simultion for distributed permutations
for simulation in range(n):
# perfectly sorted array
arr = [x + 1 for x in range(n)]
for i in range(n):
r = randint(0, i) # correct way
# swap two elements
arr[r], arr[i] = arr[i], arr[r]
# collect result
for i in range(n):
x_data.append(arr[i])
y_data.append(i+1)
# end simulation
# plot
fig, axs = plt.subplots(ncols = 2, sharey=True, figsize=(14, 6))
fig.subplots_adjust(hspace=0.5, left=0.07, right=0.93)
hb = axs[0].hexbin(x_data, y_data, gridsize=50, cmap='inferno')
axs[0].axis([0, 1000, 0, 1000])
axs[0].set_title("Distributed Permutation")
cb = fig.colorbar(hb, ax=axs[0])
cb.set_label('frequency')
x_data = []
y_data = []
# start simultion for biased permutations
for simulation in range(n):
# perfectly sorted array
arr = [x + 1 for x in range(n)]
for i in range(n):
r = randint(0,n-1) # wrong way
# swap two elements
arr[r], arr[i] = arr[i], arr[r]
# collect result
for i in range(n):
x_data.append(arr[i])
y_data.append(i+1)
# end simulation
# plot
fig.subplots_adjust(hspace=0.5, left=0.07, right=0.93)
hb = axs[1].hexbin(x_data, y_data, gridsize=50, cmap='inferno')
axs[1].axis([0, 1000, 0, 1000])
axs[1].set_title("Biased Permutation")
cb = fig.colorbar(hb, ax=axs[1])
cb.set_label('frequency')
plt.show()