🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

5回答

52334閲覧

C言語で重複しない乱数生成の仕方を教えてください!

退会済みユーザー

退会済みユーザー

総合スコア0

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

1グッド

4クリップ

投稿2016/02/28 04:56

C言語で乱数を生成するときに、例えば1~10の乱数を生成するとして、

#include<stdio.h>
#include<stdlib.h>

int main()
{
int x;
for(int i = 0; i < 10; i++)
{
x = rand()%10+1;
printf("%d",x);
}
}

のようにすると実行結果が

2 8 5 1 1 0 5 9 9 3 5

のように値が重複していまします。

それを

1 3 9 7 4 6 2 5 8 10

のように値を重複させずに乱数を生成させる方法を教えてください!
よろしくお願いします!

kobaya_c👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答5

0

ちゃんとFisher-Yatesでシャッフルするとこんな感じかと思います。

C

1#include <stdio.h> 2#include <stdlib.h> 3 4void shuffle(int array[], int size) 5{ 6 int i = size; 7 while (i > 1) { 8 int j = rand() % i; 9 i--; 10 int t = array[i]; 11 array[i] = array[j]; 12 array[j] = t; 13 } 14} 15 16int main(int argc, char **argv) 17{ 18 int values[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 19 int size = sizeof(values) / sizeof(int); 20 shuffle(values, size); 21 for (int i = 0; i < size; i++) { 22 printf("%d ", values[i]); 23 } 24 printf("\n"); 25 return 0; 26}

===={1,2,3}で10000000回試行したときの出現回数====
1 2 3: 1665490
1 3 2: 1667148
2 1 3: 1664647
2 3 1: 1666638
3 1 2: 1668634
3 2 1: 1667443

Fisher-Yatesってそんなに難解なアルゴリズムではないはずなのですが、
なぜか間違ってる例をよく見ますね。

投稿2016/02/28 09:25

YsMana

総合スコア257

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Chironian

2016/02/28 09:31 編集

私の回答のリンク先の実装がFisher-Yatesと書きながら、そのようになってなかったのですね。なるほど。 私自身はFisher-Yates自体を把握してませんでした。乱数を簡単に使ってうまいことシャッフルできるアルゴリズムなのですね。すごい。
raccy

2016/02/28 09:38

そそ、こっちこっち。ちゃんとn!の組み合わせになっているのが正しいのです。 結構間違うらしいですね。検索すると他にも間違って実装例がごろごろ転がっていました。
YsMana

2016/02/28 09:51 編集

考え方は単純で、 ・乱数で要素を選択して ・選択した要素を退避して(退避先は配列の末尾、元々末尾にあった要素と入れ替え) ・要素が1つ減ったので乱数の範囲を1つ狭めて以降繰り返し と、本当に要素を1つずつ取り出して並べてるだけなんですよね。 ただ、配列の後ろ側をシャッフル済み要素置き場にしていると思わずに swapしているだけと考えてしまうと何をやっているか理解しにくいかもしれません。
guest

0

こんにちは。

一旦配列に0~9まで入れておいて、その配列をシャッフルすると良いと思います。

以下、めめんと!「【C言語/C++】配列をシャッフルしてランダムに入れ替える」からの引用です。

C

1#include<stdlib.h> 2void shuffle(int ary[],int size) 3{ 4 for(int i=0;i<size;i++) 5 { 6 int j = rand()%size; 7 int t = ary[i]; 8 ary[i] = ary[j]; 9 ary[j] = t; 10 } 11}

投稿2016/02/28 05:04

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2016/02/28 05:15

重複しない乱数を生成する事が出来ました! ありがとうございます!
raccy

2016/02/28 07:12

この実装は偏りがありませんか? ===={1,2,3}で1000000回試行したときのそれぞれの出現確率==== 1 2 3 : 0.147903 1 3 2 : 0.184888 2 1 3 : 0.185185 2 3 1 : 0.185502 3 1 2 : 0.148641 3 2 1 : 0.147881
Chironian

2016/02/28 07:45

raccyさん。 乱数生成関数を適切に選べば偏りは減らせます。どこまで偏りをなくすべきかは課題毎に異なりますので、必要に応じて選べば良いと思います。 今回はまあ、そこまでいらないのでは? 話題がそれてしまいますし。
raccy

2016/02/28 08:16

いえ、rand()実装での偏りではなくて、shuffleの実装についてです。rand()のかわりにxorshiftで試して見ましたが、同じぐらい偏っていました。 https://ja.wikipedia.org/wiki/フィッシャー_-_イェーツのシャッフル にある、「実装上の誤り」をまさにやっているように思えます。n!の組み合わせのはずが、n^nの組み合わせで選んでしまっているというところです。
ozwk

2016/02/28 08:20

よく見るミスですね
Chironian

2016/02/28 09:04

raccyさん。 あ、それも含めて乱数の偏りと表現しました。 RAND_MAXで割ってsizeをかければ元のrand()近くまで改善することは分かってますが、オーバーフロー対策が必要ですし、サンプルが見つからなかったし、今回はそこまでしなくて良いかな?と思ってます。 rand()%sizeでは偏りがあることを書いといた方が良かったかも知れませんね。 フォローありがとうございます。
YsMana

2016/02/28 09:32 編集

論点はそこではないですよ。 「rand()%size」を0以上size未満の整数を返すまったく偏りのない乱数源に置き換えたとしても、 実装しているアルゴリズム自体に問題があるのでシャッフル結果は偏ります。 [追記] あ、私の回答の方見て気付かれたようですね。 コード投稿しておいて良かった~。
guest

0

配列に
1 3 9 7 4 6 2 5 8 10
と入れて順番に取り出せばいいです。

投稿2022/01/12 10:48

jmdajmw

総合スコア349

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

私が使っていた教本(「C入門と基本アルゴリズム」西東社、1992)には、こんなアルゴリズムが載っていました。

C

1struct SHUFFLE 2{ 3 int key; 4 int order; 5}randomizer; 6 7int compfunc( const void* a, const void* b ) 8{ 9 struct SHUFFLE* p = (struct SHUFFLE*)a; 10 struct SHUFFLE* q = (struct SHUFFLE*)q; 11 12 if( p->order < q->order ) return -1; 13 if( p->order > q->order ) return 1; 14 return 0; 15} 16 17void shuffle(){ 18 int i; 19 for( i = 0; i < sizeof( randomizer ) / sizeof( randomizer[0] ); i++ ){ 20 randomizer[i].key = i; 21 randomizer[i].order = rand(); 22 } 23 qsort( randomizer, sizeof( randomizer ) / sizeof( randomizer[0] ), 24 sizeof( randomizer[0]), compfunc ); 25}

乱数の質次第ですが、結構均質ですよ。

投稿2016/02/28 09:34

majiponi

総合スコア1722

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

YsMana

2016/02/28 10:06

これはシュワルツ変換を利用したシャッフルですね。 rubyだと sort_by{rand} と凄く簡単に書けたりします。 割と良い結果を返すのですが、 rand()が異なる要素に対して同じ値を返したときの並び順はソートアルゴリズム次第で確定するので、完全に均質には混ざらないです。 あとは、乱数格納とソートが必要なので、Fisher-Yatesにくらべるとメモリ使用量と実行時間は不利ですね。
guest

0

配列に予め 1 から 10 までの値を入れておき、その中身をランダムに並び替えてから表示したほうが良いでしょう。

lang

1#include <stdio.h> 2#include <stdlib.h> 3 4void shuffle(int array[], int size) 5{ 6 for (int i = 0; i < size; i++) { 7 int j = rand() % size; 8 int t = array[i]; 9 array[i] = array[j]; 10 array[j] = t; 11 } 12} 13 14int main(int argc, char **argv) 15{ 16 int values[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; 17 int size = sizeof(values) / sizeof(int); 18 shuffle(values, size); 19 for (int i = 0; i < size; i++) { 20 printf("%d ", values[i]); 21 } 22}

投稿2016/02/28 05:14

chitoku

総合スコア1610

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問