洗牌演算法擴充(從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的所有可能陣列
- 統計整數區間[N,M](N,M<100000)中所以非偶數的合數個數,並輸出這個數。
- 改進,從一個陣列中找出 N 個數,其和為 M 的所有可能陣列
- 【隨機演算法】洗牌隨機演算法
- js隨機數生成器的擴充套件JS隨機套件
- matlab 從某個範圍內隨機取出一個整數Matlab隨機
- 從一個無序,不相等的陣列中,選取N個數,使其和為M實現演算法(javascript實現)陣列演算法JavaScript
- Matlab 隨機生成兩個數值之間的隨機數Matlab隨機
- 給定一個n,輸出從1到n的整數
- java 生成一個隨機整數,範圍從 1 到 10Java隨機
- 對N個數進行從大到小排序排序
- 輸入N,再輸入N個數,N
- 一個隨機數的類c++隨機C++
- 生成某個範圍的隨機數隨機
- n個骰子的點數
- 報數:有n個人做成一圈,按順序1到n編好號。從第一個人開始報數,報到m(m<n)的人退出圈子。如此下去,直到留下最後一個人,輸入整數n和m,並按退出順序輸出退出圈子的人的編號。
- 演算法:利用分治演算法求解N個元素中的第M大元素演算法
- 【JAVA習題六】輸入兩個正整數m和n,求其最大公約數Java
- Nth Digit 第N個數字Git
- 指標-n個數的排序指標排序
- 3070 n個整數“打擂臺”
- 寫一個方法,傳入數字x,從一個一維陣列裡找到兩個數字符合“n1 + n2 = x”陣列
- C++實現10個數前m個數和後10-m轉換位置C++
- Python中存放10000個6位隨機數字的驗證碼Python隨機
- CUDA 的隨機數演算法 API隨機演算法API
- L1-009 N個數求和
- 3069 求n個整數的和
- 實現陣列的隨機排序(含洗牌演算法)陣列隨機排序演算法
- 本題要求統計給定整數M和N區間內素數的個數並對它們求和。
- 演算法 1~n中1的次數演算法
- 隨機數隨機
- random隨機生成10個數,然後氣泡排序random隨機排序
- php生成一個可選位數的隨機碼PHP隨機
- Python中如何生成隨機數?Python隨機
- GoLang 中的隨機數 tipsGolang隨機
- 如何擴充套件一個網站以支援數百萬使用者?套件網站
- n個人圍成一圈,第一個人從1開始數數,數到3的倍數的人離開,最後剩的人是第幾個
- 【原創】開源Math.NET基礎數學類庫使用(12)C#隨機數擴充套件方法C#隨機套件