1.隨機打亂數組(洗牌算法)
分析洗牌算法正確性的准則:產生的結果必須有 n! 種可能,否則就是錯誤的。這個很好解釋,因為一個長度為 n 的數組的全排列就有 n! 種,也就是說打亂結果總共有 n! 種。算法必須能夠反映這個事實,才是正確的。
代碼:
def shuffleArr(arr):
l = len(arr)
for i in range(l):
rand = random.randint(i,l-1)
arr[i],arr[rand] = arr[rand],arr[i]
print(arr)
arr = [1,2,3,4,5]
shuffleArr(arr)
2.蓄水池算法
應用場景:長度未知的海量數據流中隨機等概率抽取一個數據
算法過程:
假設數據序列的規模為n,需要采樣的數量的為k。
首先構建一個可容納k個元素的數組,將序列的前k個元素放入數組中。
然后從第k+1個元素開始,以\(\frac{k}{n}\)的概率來決定該元素是否被替換到數組中(數組中的元素被替換的概率是相同的)。 當遍歷完所有元素之后,數組中剩下的元素即為所需采取的樣本。
證明過程:
對於第i個數(\(i≤k\))。在k步之前,被選中的概率為1。當走到第k+1步時,被k+1個元素替換的概率 = \(k+1\)個元素被選中的概率\(\times\) i被選中替換的概率,即為\(\frac{k}{k+1} \times \frac{1}{k}\)。則被保留的概率為\(1-\frac{1}{k+1}=\frac{k}{k+1}\)。依次類推,不被k+2個元素替換的概率為\(1 - \frac{k}{k + 2} \times \frac{1}{k} = \frac{k + 1}{k + 2}\)。則運行到第n步時,被保留的概率 = 被選中的概率 * 不被替換的概率,即:
\(1 \times \frac{k}{k + 1} \times \frac{k + 1}{k + 2} \times \frac{k + 2}{k + 3} \times … \times \frac{n - 1}{n} = \frac{k}{n}\)
對於第j個數(\(j>k\))。在第j步被選中的概率為\(\frac{k}{j}\)。不被j+1個元素替換的概率為\(1 - \frac{k}{j + 1} \times \frac{1}{k} = \frac{j}{j + 1}\)。則運行到第n步時,被保留的概率 = 被選中的概率 \(\times\)不被替換的概率,即: \(\frac{k}{j} \times \frac{j}{j + 1} \times \frac{j + 1}{j + 2} \times \frac{j + 2}{j + 3} \times ... \times \frac{n - 1}{n} = \frac{k}{n}\) 所以對於其中每個元素,被保留的概率都為\(\frac{k}{n}\).
代碼:
import random
def ReservoirSamplingTest(k):
N = 10
pool = [i for i in range(N)]
result = pool[:k]
for i in range(k,N):
r = random.randint(0,i+1)
if r < k:
result[r] = pool[i]
return result
res = ReservoirSamplingTest(4)
print(res)
參考文獻:
