問題:輸入兩個整數m和n,並且m
方法一:
knuth著作《seminumerical algorithms》中提出的方法,順序遍歷n個數,通過隨機測試條件的元素被選擇。
以乙個例子來解釋所說的隨機測試條件,比如m=2,n=5。第乙個元素0被選擇的概率是2/5;第二個元素1被選擇的概率取決於第乙個元素有沒有被選擇,如果0被選擇,則1被選擇的概率為1/4,否則為2/4,所有1被選擇的概率為(2/5)*(1/4)+(3/5)*(2/4)=2/5;同理第三個元素2被選擇的概率取決於前兩個的選擇情況,如果都沒被選擇,則2被選擇的概率為2/3,如果前兩個有乙個被選擇,則2被選擇的概率為1/3,如果前兩個都被選擇,則2被選擇的概率為0,故2被選擇的概率為(3/5)*(3/5)*(2/3)+2*(2/5)*(3/5)*(1/3)=2/5。依次類推,每個元素被選擇的概率都為2/5。
總的來說,從剩下的r個元素中選擇s個元素,那麼下乙個元素被選中的概率為s/r,從整個資料集合角度來講,每個元素被選擇的概率都是相同的。
這個思想的為**如下:
select = m
remaining = n
for i = [0, n)
if (bigrand() % remaining) < select
print i
--select
--remaining
首先,該演算法可以保證有m個元素被選中,不會多也不會少。證明如下,首先證明不會多於m個:因為select等於0時不能選擇更多的整數;再證明不會少於m個:當select/remaining=1時,總會選中乙個元素,因為bigrand()%remaining
其次,每個元素被選擇的概率是相等的,均為m/n,證明如上舉例證明所示。
c++實現**如下,同時算出從268435455(約2.7億,int可以表示的最大整數除以8,本來準備拿int的最大整數約21.5億測試,但方法三要先new出這麼大的空間,超出程式可以分配的最大堆疊空間)個數中選出10萬個整數,測試該方法所用的時間,便於與後面的方法進行效能比較。
#include #include #include #include using namespace std;
void genknuth(int m, int n)
cout << endl;
t_end = time(null);
cout << "collapse time: " << difftime(t_end, t_start) << " s" << endl;
}int main()
該演算法的空間複雜度為o(m),時間複雜度為o(n),
用這演算法從2.7億個數中隨機找出10萬個數所用時間為4秒。
方法二:
initialize set s to empty
size = 0
while size < m do
t = bigrand() % n
if t is not in s
insert t into s
++size
print the elements of s in sorted order
c++**實現如下所示,集合s的實現採用stl提供的set,底層用紅黑樹實現,不可重複插入相同的資料,當要插入的資料在set中已經存在時,則插入無效,資料不會被插入集合中,插入的時間複雜度為o(logm):
#include #include #include #include #include using namespace std;
void gensets(int m, int n)
int main()
演算法的時間複雜度為o(mlogm),空間複雜度為o(m)。同樣從2.7億資料範圍內選10萬個數,所花的時間為2秒,可見速度會比原來的knuth方法快。
方法三:
弄亂乙個n個元素的陣列,然後排序輸出前m個元素。後來ashley shepherd和alex woronow發現,只需弄亂陣列前m個元素,關於產生隨機序列的方法可以參考我的文章《洗牌程式》和維基百科。
本方法的c++**實現如下:
#include #include #include #include #include using namespace std;
// generate a random number between i and j,
// both i and j are include.
int randint(int i, int j)
void genshuf(int m, int n)
sort(x, x + m);
for (i = 0; i != m; ++i)
cout << x[i] << " ";
cout << endl;
t_end = time(null);
cout << "collapse time: " << difftime(t_end, t_start) << " s" << endl;
delete x;
x = null;
}int main()
演算法的時間複雜度是o(n+mlogm),空間複雜度是o(n)。同樣從資料範圍內選10萬個數,所花時間為4秒,時間
和方法一差不多,其中有乙份部分時間花在初始化陣列上,如果採用《程式設計珠璣》問題1.9的方法,當用到某個數時才初始化,這樣演算法的時間複雜度可以減少到o(mlogm)。不過空間複雜度o(n)還是太大。
關於具體採用方法二還是方法三,stackflow上有個大牛用數學的方法證明了一下,當m<
參考文章:
taking random samples
a sample of brilliance,
programming perls
程式設計珠璣讀書筆記 抽樣問題
問題定義 從n個數中,等概率的抽取m個數。真心覺得自己概率論學得不咋第。第乙個和第三個函式還是沒有看懂。include include include include using namespace std void genknuth int m,int n void gensets int m,i...
《程式設計珠璣》 讀書筆記
程式設計珠璣 讀書筆記 婁雨禛pb16060356 準確的問題描述 很多時候,我們總是過度關注了解決問題所用的巧妙演算法,而將問題本身的重要性忽視。當我們拿到乙個問題時,應當反覆研讀問題的每乙個細節,因為正是這些細節的細微偏差導致了我們在解決問題時方案與技巧的重大不同。如果我們只花很少的時間研讀問題...
珠璣程式設計讀書筆記 《一》
我看這本書是這樣的,你呢?也是偶然發現這本書的,好多書都沒能堅持看完,希望這次能堅持下去。第一章 問題描述 對磁碟檔案中的10000000個 號碼 7位數字 進行排序。約束 1mb的主存,磁碟空間充足,時間最多幾分鐘,10秒為最佳。我想,如果在面試的時候給我這個問題,我肯定不能給出乙個讓面試官滿意的...