又是一道跟概率相關的簡單問題。話說我的概率學的太差了,趁這個機會也從頭開始補習一下。
問題描述:假設有乙個陣列,包含n個元素。現在要重新排列這些元素,要求每個元素被放到任何乙個位置的概率都相等(即1/n),並且直接在陣列上重排(in place),不要生成新的陣列。用o(n) 時間、o(1)輔助空間。
演算法是非常簡單了,當然在給出演算法的同時,我們也要證明概率滿足題目要求。
先想想如果可以開闢另外一塊長度為n的輔助空間時該怎麼處理,顯然只要對n個元素做n次(不放回的)隨機抽取就可以了。先從n個元素中任選乙個,放入新空間的第乙個位置,然後再從剩下的n-1個元素中任選乙個,放入第二個位置,依此類推。
按照同樣的方法,但這次不開闢新的儲存空間。第一次被選中的元素就要放入這個陣列的第乙個位置,但這個位置原來已經有別的(也可能就是這個)元素了,這時候只要把原來的元素跟被選中的元素互換一下就可以了。很容易就避免了輔助空間。
用python來寫一段簡單的程式描述這個演算法:
1234567
fromrandom
import
random
defshuffle(li
):rand
=random
()forxin
xrange
(len(li
)-1,
0,-1
):# 逆序遍歷liy=
rand
.randint(0
,x)# 從剩餘資料中隨機選取乙個li[
x],li[
y]=li
[y],li[x
]# 將隨機選取的元素與當前位置元素互換
主要的**僅僅三行而已,淺顯易懂。
來計算一下概率。如果某個元素被放入第i(1≤
i≤n
)個位置,就必須是在前i - 1次選取中都沒有選到它,並且第i次選取是恰好選中它。其概率為:pi
=n−1
n×n−
2n−1
×⋯×n
−i+1
n−i+
2×1n
−i+1
=1n
可見任何元素出現在任何位置的概率都是相等的。
實際上python使用者一定知道,在random類中就有現成的shuffle方法,處理方法與我上面的程式是一樣的。順便也貼在這裡學習一下。以下**來自於 python 2.5 lib\random.py:
250251252
253254
255256
257258
259260
261262
defshuffle
(self,x
,random
=none
,int
=int
):"""x, random=random.random -> shuffle list x in place; return none.
optional arg random is a 0-argument function returning a random
float in [0.0, 1.0); by default, the standard random.random.
"""if
random
isnone
:random
=self
.random
fori
inreversed
(xrange(1
,len(x
))):
# pick an element in x[:i+1] with which to exchange x[i]j=
int(
random()*
(i+1
))x[i
],x[j
]=x[
j],x[
i]
等概率隨機取數演算法的幾種實現 洗牌演算法)
最近讀了專案中的工具指令碼,發現乙個隨機取數的函式,功能大概是從m個數中不重複的隨機取出n個數,算是陣列隨機排序然後取前n個值的變種。指令碼實現採取原始的方法,每隨機取乙個數就放到乙個陣列中,下次取數時遍歷結果陣列判斷是否已經取出,平均時間複雜度為o mlogm 空間複雜度o n 效率不高。想了一下...
非等概率隨機演算法
自己做了乙個非等概率的隨機演算法的封裝,然後後面人的可以借用。probability是概率,最好是兩位小數,然後返回1的概率是probability,返回0的概率是1 probability 如果是三元組或者是多元組,多返回幾個值就可以了 int notequalprobability float ...
隨機洗牌演算法
問題 給定乙個有序序列1 n,要你將其完全打亂,要求每個元素在任何乙個位置出現的概率均為1 n。解決方案 依次遍歷陣列,對第n個元素,以1 n的概率與前n個元素中的某個元素互換位置,最後生成的序列即滿足要求,1 n的概率可通過rand n實現。見如下程式 void swap int p,int q ...