[解決済み】C++で乱数を生成する方法は?
質問
サイコロを使ったゲームを作ろうとしているのですが、その中に乱数を入れる必要があります(サイコロの面を模したもの。 1から6の間にする方法は知っています)。 使用方法
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
int main()
{
srand((unsigned)time(0));
int i;
i = (rand()%6)+1;
cout << i << "\n";
}
というのも、このプログラムを何度か実行すると、次のような出力が得られるからです。
6
1
1
1
1
1
2
2
2
2
5
2
を生成するコマンドが欲しいのです。 異なる 5回連続で同じ乱数ではなく、毎回同じ乱数です。 これを実行するコマンドはありますか?
解決方法は?
テスト・アプリケーションの最も根本的な問題は、あなたが
srand
を一度呼び出し、その後
rand
を1回実行して終了します。
のポイントは
srand
関数は
擬似乱数列
をランダムシードとする。
を渡すと
同じ値
を
srand
を2つの異なるアプリケーションで(同じ
srand
/
rand
の実装) であれば
という文字列が表示されます。
の
rand()
の後に読み込まれる値は、どちらのアプリケーションでも同じです。
しかし、この例のアプリケーションでは、擬似乱数列は1つの要素だけで構成されています。
1 sec
の精度です。では、どのような出力が期待できるのでしょうか?
もちろん、同じ秒数でアプリケーションを実行する場合、同じシード値を使用するため、結果はもちろん同じです(Martin Yorkが質問に対するコメントですでに述べているとおりです)。
実際には
srand(seed)
を1回呼び出した後
rand()
何度も
その配列を解析すると、ランダムに見えるはずです。
AMENDMENT 1 - コード例です。
わかったよ。 どうやら口頭での説明では不十分なようです(言葉の壁か何かでしょうか... :))。
をベースにした昔ながらのC言語のコード例です。
srand()/rand()/time()
の関数は、問題で使用されたものです。
#include <stdlib.h>
#include <time.h>
#include <stdio.h>
int main(void)
{
unsigned long j;
srand( (unsigned)time(NULL) );
for( j = 0; j < 100500; ++j )
{
int n;
/* skip rand() readings that would make n%6 non-uniformly distributed
(assuming rand() itself is uniformly distributed from 0 to RAND_MAX) */
while( ( n = rand() ) > RAND_MAX - (RAND_MAX-5)%6 )
{ /* bad value retrieved so get next one */ }
printf( "%d,\t%d\n", n, n % 6 + 1 );
}
return 0;
}
^^^ あれ の配列はランダムに見えるはずです。
ご注意ください
を使用することはお勧めしません。
rand
/
srand
を使用することはお勧めしません。
time
というのも、この理由はすでに明らかなはずです。これらは教育的な目的や、時々ポイントを説明するのには良いのですが、真剣に使うにはほとんど役に立ちません。
AMENDMENT 2 - 詳細な説明。
今現在は、以下のようなものがあることを理解することが重要です。 なし CやC++の標準機能(ライブラリ関数やクラス)は、実際にランダムなデータを決定的に生成します(つまり、実際にランダムであることが標準によって保証されています)。この問題にアプローチする唯一の標準機能は std::random_device が、残念ながらまだ実際のランダム性を保証するものではありません。
アプリケーションの性質によっては、本当にランダムな(予測不可能な)データが必要なのかどうかを最初に判断する必要があります。主な事例 真のランダム性が最も必要な場合 対称鍵、非対称秘密鍵、ソルト値、セキュリティトークンなどの生成などです。
しかし、セキュリティグレードの乱数は、別の記事に値する別業界のものです。私は、この乱数について この回答 私の
ほとんどの場合 擬似乱数発生器 科学的なシミュレーションやゲームなどでは、これで十分です。場合によっては、一貫して定義された疑似乱数列が必要なこともあります。例えば、ゲームでは、配布物に多くのデータを保存するのを避けるために、実行時に全く同じマップを生成することを選択することができます。
元の質問と、それに類似した多くの質問(およびそれに対する多くの誤った回答)から、何よりもまず、乱数と疑似乱数を区別すること、そもそも疑似乱数列とは何かを理解すること、疑似乱数発生器は本当の乱数発生器と同じようには使われないことを認識することが重要であることがわかります。
直感的に 乱数を要求したとき、返される結果は以前に返された値に依存してはいけません。 また、どのような瞬間に、どのようなものを要求したかに依存すべきではない。 どのようなプロセスで、どのようなコンピュータで、どのような発電機から、どのような どの銀河系で要求されたのか。それが ランダム というのは 結局のところ、予測不可能であり、何からも独立している - 。 そうでなければ、もはやランダムではないでしょう?この直感があれば を得るための魔法の呪文をウェブで検索するのは当然のことです。 このような乱数は、どのような状況でも発生します。
^^^ そのような直感的な期待は、非常に間違っており、有害である。 を含むすべてのケースで 疑似乱数発生器 - 真の乱数には合理的であるにもかかわらず。
乱数という概念は(一応)存在しますが、擬似乱数というものは存在しません。A 擬似乱数発生器 実際に擬似乱数を発生させる シーケンス .
擬似乱数列は、実際には常に 決定論的 (アルゴリズムと初期パラメータによってあらかじめ決まっている)、つまり、実際には何もランダムなものはないのです。
専門家がPRNGの品質について語るとき、実際には生成されるシーケンス(とその注目すべきサブシーケンス)の統計的特性について話しているのです。例えば,2つの高品質のPRNGを交互に使って組み合わせた場合,それぞれ良いシーケンスを生成しているにもかかわらず,結果として悪いシーケンスを生成してしまうかもしれません(その2つの良いシーケンスは単に互いに相関しているため,悪い組み合わせになってしまうかもしれません).
具体的には
rand()
/
srand(s)
は、実装で定義されたアルゴリズムで生成された、プロセス単位の非スレッドセーフな(!)疑似乱数列を提供します。関数
rand()
の範囲にある値を生成します。
[0, RAND_MAX]
.
C11規格(ISO/IEC 9899:2011)より引用。
<ブロッククオート
は
srand
関数は、引数を新しい一連の
を呼び出すと、擬似乱数が返されます。
rand
. もし
srand
が同じシード値で呼び出された場合、一連の
擬似乱数を繰り返すものとする。もし
rand
が呼び出される前に
を呼び出します。
srand
と同じシーケンスが生成されるものとする。
時
srand
が最初に呼ばれ、シード値は1である。
多くの人は、合理的に考えて
rand()
の範囲にある半独立の一様分布数列を生成します。
0
から
RAND_MAX
. まあ、確かにそうすべきなのですが(そうでなければ意味がない)、残念ながら標準がそれを要求していないだけでなく、次のような明確な免責事項さえあるのです。
生成される乱数列の品質については保証されません。
.
歴史的な事例では
rand
/
srand
の実装は、実に質の悪いものでした。最近の実装では十分な品質である可能性が高いのですが、信頼は失われており、回復するのは簡単ではありません。
さらに、スレッドセーフでないため、マルチスレッドアプリケーションでの安全な使用は厄介で制限されています(それでも可能です - 1つの専用スレッドから使用するだけかもしれません)。
新しいクラステンプレート
std::mersenne_twister_engine<>
(とその便利な型定義 -)。
std::mt19937
/
std::mt19937_64
をテンプレートパラメータとうまく組み合わせて)提供します。
オブジェクト単位
C++11標準規格で定義された擬似乱数生成器です。同じテンプレート パラメータと同じ初期化パラメータを使用すれば、C++11 準拠の標準ライブラリで構築されたアプリケーションのどのコンピュータ上でも、異なるオブジェクトがまったく同じオブジェクト単位の出力列を生成することになります。このクラスの利点は、予測可能な高品質の出力シーケンスと、実装間での完全な一貫性にあります。
また、C++11 標準では、さらに多くの PRNG エンジンが定義されています。
std::linear_congruential_engine<>
(歴史的に公正な品質として使用される
srand/rand
アルゴリズム)、および
std::subtract_with_carry_engine<>
. また、完全に定義されたパラメータ依存のオブジェクトごとの出力シーケンスが生成されます。
上記の時代遅れのCコードに代わる、現代のC++11の例。
#include <iostream>
#include <chrono>
#include <random>
int main()
{
std::random_device rd;
// seed value is designed specifically to make initialization
// parameters of std::mt19937 (instance of std::mersenne_twister_engine<>)
// different across executions of application
std::mt19937::result_type seed = rd() ^ (
(std::mt19937::result_type)
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()
).count() +
(std::mt19937::result_type)
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()
).count() );
std::mt19937 gen(seed);
for( unsigned long j = 0; j < 100500; ++j )
/* ^^^Yes. Generating single pseudo-random number makes no sense
even if you use std::mersenne_twister_engine instead of rand()
and even when your seed quality is much better than time(NULL) */
{
std::mt19937::result_type n;
// reject readings that would make n%6 non-uniformly distributed
while( ( n = gen() ) > std::mt19937::max() -
( std::mt19937::max() - 5 )%6 )
{ /* bad value retrieved so get next one */ }
std::cout << n << '\t' << n % 6 + 1 << '\n';
}
return 0;
}
を使用する以前のコードのバージョンです。 std::uniform_int_distribution<>
#include <iostream>
#include <chrono>
#include <random>
int main()
{
std::random_device rd;
std::mt19937::result_type seed = rd() ^ (
(std::mt19937::result_type)
std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch()
).count() +
(std::mt19937::result_type)
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()
).count() );
std::mt19937 gen(seed);
std::uniform_int_distribution<unsigned> distrib(1, 6);
for( unsigned long j = 0; j < 100500; ++j )
{
std::cout << distrib(gen) << ' ';
}
std::cout << '\n';
return 0;
}
関連
-
[解決済み】 != と =! の違いと例(C++の場合)
-
[解決済み] JavaScriptでランダムな文字列/文字を生成する
-
[解決済み] JavaScriptで特定の範囲のランダムな整数を生成する?
-
[解決済み] 乱数(int)を生成する方法を教えてください。
-
[解決済み] JavaScriptで2つの数値の間の乱数を生成する
-
[解決済み] 英数字のランダムな文字列を生成する方法
-
[解決済み] ランダムな文字列を使用するこのコードは、なぜ "hello world" と表示されるのですか?
-
[解決済み] 0から9までのランダムな整数を生成する
-
[解決済み] ランダムな英数字の文字列を生成するにはどうすればよいですか?
-
[解決済み] Intel CPU の _mm_popcnt_u64 で、32 ビットのループカウンターを 64 ビットに置き換えると、パフォーマンスが著しく低下します。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C-stringを使用すると警告が表示される。"ローカル変数に関連するスタックメモリのアドレスが返される"
-
[解決済み】C++コンパイルタイムエラー:数値定数の前に期待される識別子
-
[解決済み】C++でランダムな2倍数を生成する
-
[解決済み】C++エラー:の初期化に一致するコンストラクタがありません。
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み】エラー:不完全な型へのメンバーアクセス:前方宣言の
-
[解決済み] [Solved] インクルードファイルが開けません。'stdio.h' - Visual Studio Community 2017 - C++ Error
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み】1つ以上の多重定義されたシンボルが見つかる
-
[解決済み】'std::cout'への未定義の参照