洗牌演算法擴充(從n個數中隨機m個數)
一般洗牌演算法達到的效果就是打亂序列,可以理解為從n個數中隨機m個數。
當n==m的時候,就是全打亂,m<n的時候,有兩種做法。
①全打亂,然後取前m項。
②每次從n中取出一個數,然後從n中刪除這個數,重複m次。
全打亂(n等於m)
1、random_shuffle
一般洗牌就是全打亂,這個時候可以直接用stl的 random_shuffle 函式。原始碼如下:
template <class RandomAccessIterator>
inline void random_shuffle(RandomAccessIterator first, RandomAccessIterator last) {
if(first != last)
for(RandomAccessIterator i = first + 1; i != last; ++i)
iter_swap(i, first + (rand() % ((i - first) + 1)));
}
random_shuffle 的基本思想是這樣的:把第i個元素和前i個隨機一個元素進行對調,執行n次,達到全隨機的效果。
(如果原數列是有序數列,如1,2,3,4....50,進行5次對調操作之後,前5項已經隨機,後45項依舊不變,就不是一個我想要的隨機的效果,不太符合例如50個球裡取5個球的場景,所以一般還是要全部隨機後才具有完整隨機性)
2、Fisher-Yates Shuffle演算法
void Fisher_Yates_Shuffle(vector<int>& arr,vector<int>& res)
{
srand((unsigned)time(NULL));
int k;
int len = arr.size();
for (int i=0;i<len;++i)
{
k=rand()%arr.size();
res.push_back(arr[k]);
arr.erase(arr.begin()+k);
}
}
Fisher-Yates 洗牌演算法的基本思想是:從n中取出一個數,然後從n中刪除這個數,重複n次,達到全隨機的效果。
這部分程式碼是我從別的博主那拷貝過來的,我之前也是拿來就用,但是當n數值比較大的時候,這個程式碼效率就比較低了。
倒不是基本思想的問題,問題在於這份程式碼vector大量使用了刪除,我們知道vector是一塊連續的記憶體,中間刪除操作,需要移動後面的元素,導致效率低下。
半打亂(n大於m)
1、random_shuffle改
直接random_shuffle,然後取前m項倒也可以,效率不低。但是存在效率浪費。
void randVec(std::vector<int> vecAll, int m, vector<int> &vecRet)
{
random_shuffle(vecAll.begin(),vecAll.end());
vecRet.insert(vecRet.begin(), vecAll.begin(), vecAll.begin()+m);
}
2、Fisher-Yates Shuffle演算法
void randVecMine(std::vector<int> vecAll, int m, vector<int> &vecRet)
{
srand(time(NULL));
int k = 0;
for (int i = 0; i < m; ++i) {
if (vecAll.size() > 0) {
k = i + rand() % (vecAll.size()-i);
int tmp = vecAll[k];
vecAll[k] = vecAll[i];
vecAll[i] = tmp;
}
}
vecRet.insert(vecRet.begin(), vecAll.begin(), vecAll.begin()+m);
}
這份程式碼相較於全打亂的Fisher-Yates Shuffle演算法程式碼,主要是修改了vector刪除元素部分,第i次隨機的時候,是從後面n-i+1個數中隨機取出。取出m個數隨機m次即可。沒有效率浪費。
效率比拼測試
對於random_shuffle和我寫的Fisher-Yates實現,我設計了一個效率測試。計算不同引數對n、m情況下,得出隨機結果需要的時間對比。以下是測試程式碼。
#include <iostream>
#include <vector>
#include<algorithm>
#include<bits/stdc++.h>
using namespace std;
#include<stdio.h>
#include <windows.h>
void randVec(std::vector<int> vecAll, int m, vector<int> &vecRet)
{
DWORD start, end;
start = GetTickCount();
random_shuffle(vecAll.begin(),vecAll.end());
vecRet.insert(vecRet.begin(), vecAll.begin(), vecAll.begin()+m);
end = GetTickCount()-start;
cout<<"random_shuffle time="<<end<<endl;
}
void randVecMine(std::vector<int> vecAll, int m, vector<int> &vecRet)
{
DWORD start, end;
start = GetTickCount();
srand(time(NULL));
int k = 0;
for (int i = 0; i < m; ++i) {
if (vecAll.size() > 0) {
k = i + rand() % (vecAll.size()-i);
int tmp = vecAll[k];
vecAll[k] = vecAll[i];
vecAll[i] = tmp;
}
}
vecRet.insert(vecRet.begin(), vecAll.begin(), vecAll.begin()+m);
end = GetTickCount()-start;
cout<<"myrand time="<<end<<endl;
}
int main()
{
int runindex = 0;
while(runindex<10)
{
runindex++;
vector<int>vec_all;
int s;
cin>>s;
for(int i=0;i<s;++i)
{
vec_all.push_back(i+1);
}
vector<int>vec_part;
int len;
cin>>len;
vec_part.clear();
randVec(vec_all,len,vec_part);
vec_part.clear();
randVecMine(vec_all,len,vec_part);
cout<<endl;
}
}
輸入的n、m值分別是:
10000 10000
100000 100000
1000000 1000000
10000000 10000000
100000000 100000000
1000000 100000
1000000 10000
1000000 1000
1000000 500000
1000000 900000
結果截圖:
資料整理(單位:ms)
n | m | random_shuffle | myshuffle |
10000 | 10000 | 0 | 0 |
100000 | 100000 | 16 | 15 |
1000000 | 1000000 | 63 | 47 |
10000000 | 10000000 | 624 | 436 |
100000000 | 100000000 | 6224 | 4332 |
1000000 | 100000 | 46 | 0 |
1000000 | 10000 | 47 | 0 |
1000000 | 1000 | 47 | 0 |
1000000 | 500000 | 62 | 15 |
1000000 | 900000 | 62 | 46 |
資料可能存在一定誤差,基本相對準確。
相關文章
- 隨機數擴充隨機
- 從陣列中找出N個數,其和為M的所有可能陣列
- js生成m-n之間的隨機數JS隨機
- 生成k個不同的隨機數,從m到n,並輸出最大值的程式程式碼隨機
- 改進,從一個陣列中找出 N 個數,其和為 M 的所有可能陣列
- 【隨機演算法】洗牌隨機演算法
- js隨機數生成器的擴充套件JS隨機套件
- 從一個無序,不相等的陣列中,選取N個數,使其和為M實現演算法(javascript實現)陣列演算法JavaScript
- javascript實現的m到n的隨機數程式碼例項JavaScript隨機
- matlab 從某個範圍內隨機取出一個整數Matlab隨機
- Matlab 隨機生成兩個數值之間的隨機數Matlab隨機
- matlab中怎樣隨機生成一個最大值為N的正整數??Matlab隨機
- 排序演算法-N個正整數排序排序演算法
- [隨機數詳解]生成一個隨機數,生成指定範圍的隨機數及隨機陣列去重隨機陣列
- C# scrollView個數隨機UI從上部顯示C#View隨機UI
- 大量輸入流中,隨機求m個記錄隨機
- 獲取兩個數之間的隨機數-java隨機Java
- java 生成一個隨機整數,範圍從 1 到 10Java隨機
- POJ 2442-Sequence(優先佇列-m組n個數每組取一個求n個最小值)佇列
- 對N個數進行從大到小排序排序
- 生成某個範圍的隨機數隨機
- 一個隨機數的類c++隨機C++
- 從oracle表中隨機取記錄,產生隨機數和隨機字串Oracle隨機字串
- **PHP隨機數演算法PHP隨機演算法
- 編寫一個程式,獲取10個1至20的隨機數,要求隨機數不能重複。隨機
- 《Cracking the Coding Interview程式設計師面試金典》----從0到n中某個數字的個數View程式設計師面試
- 【康擴充開】及其在求全排列第k個數中的應用
- 【JAVA習題六】輸入兩個正整數m和n,求其最大公約數Java
- 演算法:利用分治演算法求解N個元素中的第M大元素演算法
- 從面試題中學演算法(2)---求陣列中唯一n個出現1次的數字(n=1,2,3)面試題演算法陣列
- 指標-n個數的排序指標排序
- 對N個數進行排序排序
- INFORMIX表的預設初始擴充套件、下一個擴充套件資料塊以及一個表允許的最大擴充套件數。ORM套件
- C++實現10個數前m個數和後10-m轉換位置C++
- 演算法幾個數之和是某個數演算法
- CUDA 的隨機數演算法 API隨機演算法API
- vc中產生隨機數隨機
- Swift 中隨機數的使用Swift隨機