1. 程式人生 > >朋友圈“錦鯉”盛行,如何抽取“錦鯉”?Python放大招了~

朋友圈“錦鯉”盛行,如何抽取“錦鯉”?Python放大招了~

近幾個月,網路上最火的就是錦鯉女孩,是國慶假期某支付平臺官微搞了個帶有錦鯉圖的轉發抽獎活動,一名網名叫@信小呆的呆萌小姐姐抽中了這個鉅額大獎,小姐姐也因此又被廣大錦鯉愛好者們當成了“中國活體錦鯉”,瘋狂轉發。

學習Python中有不明白推薦加入交流裙
                號:735934841
                群裡有志同道合的小夥伴,互幫互助,
                群裡有免費的視訊學習教程和PDF!

朋友圈“錦鯉”盛行,如何抽取“錦鯉”?Python放大招了~

 

 

自此之後,朋友圈,以及各大媒體論壇盛行各種“錦鯉”。作為程式設計師的我們,自然應該想一想,如何完成“錦鯉”的抽取?

實際這就是一個抽樣問題。可以抽象為如下問題:

  • 隨機的選取容量為N的陣列中的k個元素,要求是不能重複選取,並且不能刪除陣列中的元素,只能夠進行交換。其中 k≤N 。

 

看到這個問題,你想到了什麼?

我先想到的就是抽籤演算法。當由 k 個人抽 K 張籤,無論先後順序,每個人抽中的概率都是1/K 。同理, k 個人抽 N 張籤,無論先後順序,每個人抽中的概率都是1/N 。

可以簡單說明一下:

1、當k=1時,由於是從 N 張籤中抽取,所以抽中的概率是1/N,成立。

2、當k=2時,在剩下的 N-1 箇中隨機選:1/(N-1),由於第1次沒有選中它, 而是在另外N-1箇中選:(N-1)/N,因此概率為:(N-1)/N * 1/(N-1) = 1/N。

3、當k=3時,概率為 (N-1)/N * (N-2)/(N-1) * 1/(N-2) = 1/N。

4、重複上述流程,到k=N。

既然如此,我們可以對N個數,進行k次抽籤操作,演算法程式碼如下:

import random
def SelectRandomK(L, N, k):
 for i in range(0, k):
 # 產生i到N-1間的隨機數
 j = random.randint(i, N - 1)
 L[i], L[j] = L[j], L[i]

這個演算法實現的缺點就是依賴了資料總數N。如果不知道N有沒有辦法呢?

那就是蓄水池演算法。蓄水池演算法是大資料抽樣常用的演算法,在不知道資料總數目的情況下可以完成隨機抽樣。

先從最簡單的蓄水池抽樣演算法說起,即蓄水池中資料的數目為1。先把第一個資料以概率1/1放入蓄水池,第二個資料以1/2的概率替換蓄水池中資料,第三個資料以1/3的概率替換蓄水池中資料,第k個數據以1/k的概率替換蓄水池中資料,如此重複,直至遍歷完所有的資料。

這樣完成後,每個資料被抽中的概率是相等的,即使不知道資料總數目。下面,就用數學歸納法完成證明:

只需要證明當遍歷至第k行時,前面k行中的任意一行被抽取的概率均為1/k。

證明:(1)當i=1時,第一行被抽取的概率為1/1,成立。

(2)假設當i=k時成立,則前面k行中的任意一行被抽取的概率為1/k。現證明i=k+1時成立。

當i=k+1時,第k+1行替換原有樣本的概率為1/(k+1),所以第k+1行被抽取的概率是1/(k+1)。

前面k行任意一行被抽取的概率為 1/k*k/(k+1)=1/(k+1),

即當i=k+1時成立。證畢。

Python程式碼實現如下:

def SelectRandom1(L):
 # 計數器
 num = 2
 for item in L[1:]:
 if random.random() < 1.0/num:
 L[0], L[num - 1] = L[num - 1], L[0]
 num += 1

那麼,如何以等概率選擇k個數呢?跟單個數類似,實現方法如下:

先把前k個數據放入蓄水池,對第k+1個數據,我們以 k/(k+1)概率決定是否要把它放入蓄水池,放入時隨機的選取一個作為替換項。對第n個數據,我們以 k/n概率決定是否要把它放入蓄水池,放入時隨機的選取一個作為替換項。這樣一直做下去,直至遍歷完所有的樣本空間。可以證明,對於任意的樣本空間N,對每個資料的選取概率都為k/N。

也可以通過數學歸納法完成證明:

只需要證明當遍歷至第n(n>=k)行時,前面n行中的任意一行被抽取的概率均為k/n。

證明:(1)當i=k時,前面k行被抽取的概率為k/k=1,成立。

(2)假設當i=n時成立,則前面n行中的任意一行被抽取的概率為k/n。現證明i=n+1時成立。

當i=n+1時,第n+1行替換原有樣本的概率為k/(n+1),所以第n+1行被抽取的概率是k/(n+1)。

前面n行任意一行被抽取的概率為

k/n*(k/(n+1)*(k-1)/k+(n+1-k)/(n+1))=k/n*(n/(n+1))=k/(n+1)

即當i=n+1時成立。證畢。

Python程式碼實現如下:

def SelectRandomK(L, k):
 # 計數器
 num = k + 1
 for item in L[k:]:
 if random.random() < float(k/num):
 # 產生0到k-1之間的隨機數
 j = random.randint(0, k - 1)
 L[num - 1], L[j] = L[j], L[num - 1]
 num += 1

關於隨機抽樣演算法,你深入理解了嗎? 你明白朋友圈“錦鯉”該如何抽取了嗎?