シャッフルアルゴリズムは、重複しない乱数を生成できるアルゴリズムの一つです。たとえば、10 個の乱数を生成し、そのすべてが「重複しない」必要があるとします。多くの人は、まず rand 関数で乱数を 1 つ取得して配列に格納し、配列にすでにある数字と比較する方法を思いつくと思います。この方法は簡単ですが、性能がとても悪くなります。ここで紹介するシャッフルアルゴリズムは、性能の面でその方法とは天と地ほどの差があります。シャッフルアルゴリズムの原理は実はとても単純で、まず配列に数字を入れ、その後に数字を打ち散らす(シャッフルする)というものです。

解説:

たとえば、10 個の重複しない乱数を生成したいとします。

まず、配列を作成して 1~10 を格納します。

int x[10]={0};

for(int i=0;i<=9;i++){
	x[i]=i;		//配列 x の 1~10 の要素に順番にデータを入れる(例:x[1]=1, x[2]=2)
}

つまり、x[0]=0、x[1]=1、x[2]=2……となります。

次に、交換法を使って配列のデータを打ち散らします。

time_t xx;
    srand((unsigned)time(NULL));			//現在の時刻で乱数表を初期化する

    for(int ii=0;ii<=1000;ii++){
        int n1=rand()%10;		//0~9 の乱数を生成する
        int n2=rand()%10;
        //配列のデータを交換する(打ち散らす)
		int temp=x[n1];
        x[n1]=x[n2];
        x[n2]=temp;

    }

0~9 の乱数を生成して n1、n2 に代入し、x[n1] と x[n2] のデータを入れ替えます。

この動作をある回数(ここでは 1000 回と仮定します)繰り返すと、配列 x のデータは完全にばらばらになります。

つまり、x[0] に入っているのが必ずしも 0 とは限らず、x[1] に入っているのも必ずしも 1 とは限らなくなります。

これでアルゴリズムの説明は完了です。

配列の中身を出力すると、それは実質的に乱数になっていて、しかも数字が重複しません。

詳細なソースコード:

/*
	洗牌演算法(シャッフルアルゴリズム)
	重複しない乱数を生成する
	座席の割り当てに使える
*/

#include #include<stdlib.h> //乱数関数に必要 #include<time.h>

using namespace std;

int main() { int x[10]={0};

for(int i=0;i<=9;i++){
    x[i]=i;		//配列 x の 1~10 の要素に順番にデータを入れる(例:x[1]=1, x[2]=2)
}

time_t xx;
srand((unsigned)time(NULL));			//現在の時刻で乱数表を初期化する

for(int ii=0;ii<=1000;ii++){
    int n1=rand()%10;		//0~9 の乱数を生成する
    int n2=rand()%10;
    //配列のデータを交換する(打ち散らす)
	int temp=x[n1];
    x[n1]=x[n2];
    x[n2]=temp;

}

for(int i=0;i<=9;i++){
    cout<<x[i]<<endl;		//出力
}
system("pause");
return 0;

}

プロジェクトファイルのダウンロード:Box.net|4Shared|Google|Dropbox

シャッフルアルゴリズムの原理:

原理はそれほど難しくありません。最初、配列 x の中身は 1~10 の順に並んでいます。打ち散らした後も x には 1~10 の数字が入ったままですが、元の並び順はなくなっています。そのあとに x[0] から x[9] を順番に出力すると、その出力結果が乱数になるのです。

あとがき:

シャッフルアルゴリズムはどんなときに使うのでしょうか。いちばん身近な例は、座席の割り当てでしょう。各座席に番号を振り、配列の添字を生徒の出席番号に対応させます。そしてシャッフルアルゴリズムを使えば、どの生徒も「ランダムな」座席を得られ、同じ座席が二度割り当てられることもありません。