Shuffle&Sample
阿新 • • 發佈:2018-12-17
近來打算通過自己實現幾個常見模型來複習一下之前看過的,順便練習一下Python語法的熟練度。首先準備搭好除模型外的整個程式的其他部分,比如資料的匯入、劃分。
在寫劃分資料集部分的函式時,查了一下random模組中有sample(data, k)和shuffle(data)兩個函式,分別實現從一個序列中取樣k個數和將資料集內資料打亂的,其中sample函式將取樣後資料作為返回值,shuffle函式直接對data操作,無返回值。在這裡對兩個函式的實現演算法探究一下。
######shuffle
shuffle的意思是讓序列亂序。在random模組的shuffle函式中,用的是經典的Fisher-Yates shuffle演算法。其虛擬碼如下
for i = 1:N
temp = random(0, N-i)
交換 a[temp] 和 a[N-i]
end
易證,對於每個元素,其shuffle後在每個位置的概率都為
。同理,也可從前往後實現。
######sample
sample意思是從原序列中隨機選擇k個元素,不改變原序列。
最簡單的自然是每次隨機從[0, N-1]中選一個,若該元素已經選出,則重新選擇,虛擬碼為
while length(已抽)<k
temp = random(0, N-1)
if temp in 已抽
continue
已抽.append(a[temp])
end
該演算法需要一塊額外的空間,進行儲存原序列元素是否已經被抽取的標記。但是,當k較大時,每次random到已經抽到的概率越來越大,此時時間複雜度升高,接近於
。
也可以採用shuffle的思路,到第k個時停止,此時時間複雜度為
。但是,此時原序列已經被改變,或者需要另外複製一個原序列,在此序列上進行處理。
水塘抽樣演算法(Reservoir sampling)可以適用於N為一很大或者未知數量的情況,尤其是不能將所有N個專案存入記憶體中時。其虛擬碼如下
for i in range(1, k)
reservoir[i] = stream[i]
for i in range(k, N+1)
p = random(0, i)
if p < k
reservoir[p] = stream[i]
時間複雜度為 。