1. 程式人生 > >隨機生成資料

隨機生成資料

\(OI\)學習過程中,資料是必不可少的。不管是在\(OJ\)上提交狂\(Wa\)不止時,還是考場上懷疑自己的演算法但出題人給的資料又能正確\(AC\)時,資料都顯得尤為重要。所以我特地寫一篇隨機資料生成的部落格來總結一下。

隨機函式

首先我們需要認識一下\(C++\)自帶的隨機函式\(rand()\)。標頭檔案\(algorithm\)自帶\(srand\)\(rand\)兩個函式。

使用\(rand()\)函式會隨機返回一個範圍在[0,RAND_MAX]之間的整數。RAND_MAX等於\(2147483647\)也就是\(int\)能儲存的最大的正整數。你想要\(rand\)一個[0,n-1]範圍的數你可以\(rand()\)

%\(n\),如果你想要\(rand()\)出負數你可以寫成\(rand()\)%(\(2\)*\(n\))-\(n\)。這樣\(rand\)出的數都是在\([-n,n-1]\)中等概率取值的。如果你要隨機實數就寫成\((double)rand()/eps\)。令\(eps\)等於\(10\)的若干次方即可。\(srand()\)函式帶的引數是隨機種子,種子不一樣隨機出來的數也不一樣。我一般習慣寫成\(srand(time(0))\),那麼就會以當前時間為種子。第一次隨機取值會根據種子來決定,之後的隨機取值會以上一次隨機出的值為種子來隨機。這就是隨機函式的執行機制。另外,\(algorithm\)裡的random_shuffle也是根據種子來隨機的。

隨機生成排列

for(int i=1;i<=n;i++)
    a[i]=i;
random_shuffle(a+1,a+n+1);
for(int i=1;i<=n;i++)
    printf("%d ",a[i]);

隨機生成若干個區間

for(int i=1;i<=m;i++) {
    int l=rand()%n+1,r=rand()%n+1;
    if(l>r)swap(l,r);
    printf("%d %d\n",l,r);
}

隨機生成樹

下面的\(n\)全部代表點數,\(m\)全部代表邊數。

for(int i=2;i<=n;i++) {
    int i_father=rand()%(i-1)+1;
    printf("%d %d\n",i_father,i);
}

生成鏈

for(int i=2;i<=n;i++)
    printf("%d %d\n",i-1,i);

生成菊花圖

for(int i=2;i<=n;i++)
    printf("1 %d\n",i);

隨機生成聯通圖

#include <map>
#include <cstdio>
#include <algorithm>
using namespace std;

int n,m;
map<pair<int,int>,bool> g;

int main() {
    scanf("%d%d",&n,&m);
    printf("%d %d\n",n,m);
    for(int i=2;i<=n;i++) {
        int i_father=rand()%(i-1)+1;
        g[make_pair(i_father,i)]=1;
        g[make_pair(i,i_father)]=1;
        printf("%d %d\n",i_father,i);
    }
    for(int i=n;i<=m;i++) {
        int a=rand()%n+1,b=rand()%n+1;
        while(g[make_pair(a,b)])a=rand()%n+1,b=rand()%n+1;
        g[make_pair(a,b)]=1;g[make_pair(b,a)]=1;
    }
}

不要求聯通就不要先建一棵樹了。