C ++ rand()が同じ桁の数値のみを生成するように見えるのはなぜですか?


146

C / C ++で書かれた小さなアプリケーションで、rand関数とおそらくシードの問題に直面しています:

次数が異なる、つまり対数の値が異なる(2を底とする)一連の乱数を生成したい。しかし、生成される数値はすべて同じ次数であり、2 ^ 25と2 ^ 30の間で変動しているようです。

rand()これは、現在比較的大きな数値であるUnix時間がシードされているためですか?何を忘れてるの?rand()の初めに1回だけ播種しmain()ます。


7
FWIWそうですか、それはCまたはC ++ですか?C / C ++によって実際にC ++を使用できることを意味し、Cについての言及がランダムであった場合、おそらくこのen.cppreference.com/w/cpp/numeric/random/binomial_distributionが役立ちます。
R.マルティーニョフェルナンデス

9
残念ながら、あなたは間違った馬に賭けていました。種子はあなたの問題ではないはずです。あなたの問題は間違った予想される分布でした。公平なプログラマーはrand()均一に分散された数値を返すことを期待するので(Googleの高いランキングのドキュメントはそれを明確に述べています)、この質問は将来の読者にとって有用だとは思いません。そのため、反対票を投じますが、SOの使用を思いとどまらせないでください。
Orionii皇帝2013年

12
@ doug65536「...番号が繰り返されることはありません」-ランダムではありません!私のrand()ダイスがすべての可能な数が返されるまで同じ数を2度返さなかった場合、クラップステーブルでの退職金を賄うことができました。
Chris Gregg 2013年

6
@GalacticCowboy周期性を個々の数値の繰り返しと間違えないでください。あなたが引用したWikipediaの記事から:「内部状態がその出力よりも大きくなる可能性があるため、繰り返される結果は期間の終わりに達したことを意味しません。」PRNGが値を生成し、すべての値が返されるまでその値を再び生成しないことが保証されているとしたら、それは非常に悪いことです。
Chris Gregg 2013年

12
Doug65536、誰も戦いを選んでいない。彼らはあなたが間違っていると正しく述べているだけです。1と10の間のRANDが必要な場合、PRNGは次のように非常に喜んでチャーンできます。2 4 7 2 8 1 5 9 7 3 2と7が複数ある場合でも、それは完全に有効です。iPhoneのシャッフル機能とPRNGが混同されていると思います。
キプロスでリラックス13年

回答:


479

1から2 30の間の数値は3%しかなく、2 25から2 30の間ではありません。だから、これはかなり正常に聞こえます:)

2ため、25 /2 30 = 2 -5 = 1/32 = 0.03125 = 3.125パーセント


36
ええ、良い点!2 ^ 25から2 ^ 30までの数値は、1から2 ^ 25までの数値の31倍です:) その時、私はプログラムを再考する必要があります。質問に答えました。
Tallaron Mathias

1
@TallaronMathiasビット>>シフトで数値を切り捨てることを検討してください。これにより、数値が小さくなります。(または%。で係数をとります。)
Sean Allred

13
これはほとんどのプログラマーにとって明白であると思います。2^ 25未満の符号なし整数は、最初の7ビットが0-でなければならず、すべてのビットがランダムであれば...
BlueRaja-Danny Pflughoeft

118
@ BlueRaja-DannyPflughoeft-確率が明らかな場合、カジノは廃業します。
Brett Hale

26
@BrettHale-プログラマーはカジノのターゲット人口統計であるとは思いません。
EkoostikMartin 2013年

272

明るい緑は、0と2 25の間の領域です。濃い緑色は、2 25と2 30の間の領域です。ティックは2の累乗です。

分布


42

より正確にする必要があります。異なる2の対数値が必要ですが、これにはどの分布が必要ですか?標準のrand()関数は一様分布を生成します。この出力を変位値を使用して変換する必要があります関連付けられ値関数ます。

分布を教えていただければ、quantile必要な機能を教えてくれます。


13
+1、配布は重要な用語です。分布について何も知られていない場合、乱数について話すことは実際には意味がありません。ユニフォームは重要なものですが、単なる特殊なケースです。C ++ 11標準ライブラリからさまざまなディストリビューションを指摘するのに適した場所かもしれません。
leftaroundabout 2013年

18

異なる桁数が必要な場合は、単純に試してみpow(2, rand())ませんか?あるいは、ハロルドが示唆したように、注文を直接rand()として選択しますか?


3
良い考えですが、^(C言語では、パワーではなく論理xor演算子です)ではなく、powを使用して回答を修正する必要があります。
クリス

6
以来rand()まで行くことができるRAND_MAX、あなたは本当に結果がオーバーフロー...しないように、あなたの乱数を拡張する必要がある
フロリス

@Floris:しかし、非常に大きな範囲で小さな可算範囲をスケーリングすると、多くの穴ができます。これは、おそらくOPが期待していることではありません。
アンドレ・キャノン

13

@ C4storは素晴らしいポイントを作りました。ただし、より一般的なケースで、人間(base 10)の方が理解しやすい場合:1から10 ^ nの範囲では、数値の〜90%は10 ^(n-1)から10 ^ nであるため、数値の〜99%は10 ^(n-2)から10 ^ nです。必要なだけ小数を追加してください。

面白い数学、これをnに対して続ければ、1から10 ^ n、99.9999 ...%= 100%の数値がこのメソッドで10 ^ 0から10 ^ nであることがわかります。

次にコードについて、0から10 ^ nまでのランダムな桁の乱数が必要な場合は、次のようにします。

  1. 0からnまでの小さな乱数を生成する

  2. nの範囲がわかっている場合は、次数10 ^ kの大きな乱数を生成します(k> max {n})。

  3. 長い乱数をカットして、この大きな乱数のn桁を取得します。


46
あなたは完全に正しいですが、答えを本当に理解しやすくするには、OPは1〜100の乱数の90%が2桁である理由を自問する必要があります。
モニカについて質問する

13

基本的な(そして正しい)答えはすでに上で与えられ、受け入れられました。0から9までの10の数字、10から99までの90の数字、100から999までの900などです。

ほぼ対数分布の分布を得る計算上効率的な方法として、乱数を乱数で右シフトします。

s = rand() & 31; // a random number between 0 and 31 inclusive, assuming RAND_MAX = 2^32-1
r = rand() >> s; // right shift

完璧ではありませんが、コンピューティングよりもはるかに高速です pow(2, rand()*scalefactor)。係数2内の数値の分布が均一になるという意味で「塊」になります(128〜255では均一、256〜1023では半分の密度など)。

以下は、0から31までの数値(1Mサンプル)の頻度のヒストグラムです。

ここに画像の説明を入力してください


nitpick:これは、予想よりも非常に小さい数を奨励します。ゼロを取得する確率が10よりも有意に高い
ダックMooing

まあ-これの全体のポイントは少数を奨励することなので、私はそれがうまくいってうれしいです!モンテカルロシミュレーションを実行しました。これにより、対数分布とは異なり、数値が2倍になるため、確率が2倍低下します。回答を写真で更新しました。
フローリス

いいえ、rand()>>(rand()&31);つまり、では、直感的に、1/32の数値は32ビットであり、1/32の数値は31ビットであり、1/32の数値は30ビットであると予想します。しかし、それはではないあなたは、ほぼ半分のあなたの測定と私の精神的な数学の不一致であるため0でなければなりませんが、私は数字に自分の測定を行う必要があるでしょう、数字だけの1 /第64回は、32ビットにつながる比べて約、取得している結果これ。
Mooing Duck 2013

2
私はあなたのコードが間違っていると言うつもりはありません。それはおそらく私がすることです。それだけで結果がされていないという警告に値する非常に 1が予想されるとして配布します。
Mooing Duck 2013

1
私は、問題を0を1ビットの数として考えることから来ていると思います...これは、整数と対数を混合するときに遭遇する一種の難問です。それは良い練習でしたが、あなたは私に何か考えることを与えました。「アルゴリズムの限界をテストする」-古くなることはありません。
フローリス2013年

5

0と2 ^ 29の間、2 ^ 29と2 ^ 30の間では、まったく同じ数の数があります。

問題の別の見方:生成した乱数の2進数表現を考えてください。最上位ビットが1である確率は1/2に等しいため、半分のケースでは29番目になります。2 ^ 25未満の数値を表示する必要がありますが、これは、上位5ビットがすべて0であることを意味し、1/32の低い確率で発生します。長時間実行しても、15未満の順序はまったく表示されない可能性があります(確率は、連続して6 6回ローリングするようなものです)。

さて、種子についてのあなたの質問の一部。いいえ、シードは、数値が生成される範囲を決定することはできません。最初の初期要素を決定するだけです。rand()は、範囲内のすべての可能な数値のシーケンス(所定の順列)と考えてください。シードは、シーケンスから番号の描画を開始する場所を決定します。これが(疑似)ランダム性が必要な場合、現在の時間を使用してシーケンスを初期化する理由です。開始位置が均一に分散されていなくてもかまいません。重要なことは、同じ位置から開始しないことです。



2

そのためにwgetを使用できるオンラインサービスの乱数を使用したい場合は、random.orgなどのサービスを使用して乱数を生成できることを確認したい場合があります。wgetを使用してそれらをキャッチし、番号を読み取ることができます。ダウンロードしたファイル

wget -q https://www.random.org/integers/?num=100&min=1&max=100&col=5&base=10&format=html&rnd=new -O new.txt

http://programmingconsole.blogspot.in/2013/11/a-better-and-different-way-to-generate.html


SOへようこそ。回答としてリンクを投稿することはご遠慮ください。回答の詳細なスケッチを提供して、詳細をリンク経由で読むことができます。
シャイ2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.