私はサイコロを使ってゲームを作ろうとしています、そしてそれには乱数を入れる必要があります(ダイの側面をシミュレートするために。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
を1回呼び出してからRand
を1回呼び出して終了することです。
srand
関数の最大のポイントは、乱数シードを使って疑似乱数のシーケンスを初期化することです。つまり、(同じsrand
/srand
の実装で)2つの異なるアプリケーションで同じ値をRand
に渡すと、それ以降はまったく同じRand()
値のシーケンスが読み取られることになります。しかし、あなたの擬似乱数列は1つの要素だけで構成されています - あなたの出力は1秒の精度の時間でシードされた異なる擬似乱数列の最初の要素で構成されています。それで、あなたは何を見ることを期待しますか?あなたが同じ秒にアプリケーションを実行することが起こるときあなたの結果はもちろん同じです(Martin Yorkがすでに答えへのコメントで述べたように)。
実際にはsrand(seed)
を一度呼び出してからRand()
を何度も呼び出してそのシーケンスを分析するべきです - それはランダムに見えるべきです。
モジュロを使用すると、乱数ジェネレータによっては、乱数に偏りが生じる可能性があります。 詳細については、この質問を参照してください。 もちろん、ランダムな順序で繰り返し数値を取得することは完全に可能です。
より良い配布のためにいくつかのC++ 11機能を試してみてください。
#include <random>
#include <iostream>
int main()
{
std::random_device dev;
std::mt19937 rng(dev());
std::uniform_int_distribution<std::mt19937::result_type> dist6(1,6); // distribution in range [1, 6]
std::cout << dist6(rng) << std::endl;
}
C++ 11乱数の詳細については、この質問/回答を参照してください。 これが唯一の方法ではありませんが、1つの方法です。
boost libsを使っているのなら、このようにしてランダムジェネレータを得ることができます。
#include <iostream>
#include <string>
// Used in randomization
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
using namespace std;
using namespace boost;
int current_time_nanoseconds(){
struct timespec tm;
clock_gettime(CLOCK_REALTIME, &tm);
return tm.tv_nsec;
}
int main (int argc, char* argv[]) {
unsigned int dice_rolls = 12;
random::mt19937 rng(current_time_nanoseconds());
random::uniform_int_distribution<> six(1,6);
for(unsigned int i=0; i<dice_rolls; i++){
cout << six(rng) << endl;
}
}
ここで、関数current_time_nanoseconds()
は、シードとして使用される現在の時間をナノ秒単位で示します。
ランダムな整数と範囲内の日付を取得するためのより一般的なクラスは次のとおりです。
#include <iostream>
#include <ctime>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_int_distribution.hpp>
#include <boost/random/variate_generator.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/date_time/gregorian/gregorian.hpp"
using namespace std;
using namespace boost;
using namespace boost::posix_time;
using namespace boost::gregorian;
class Randomizer {
private:
static const bool debug_mode = false;
random::mt19937 rng_;
// The private constructor so that the user can not directly instantiate
Randomizer() {
if(debug_mode==true){
this->rng_ = random::mt19937();
}else{
this->rng_ = random::mt19937(current_time_nanoseconds());
}
};
int current_time_nanoseconds(){
struct timespec tm;
clock_gettime(CLOCK_REALTIME, &tm);
return tm.tv_nsec;
}
// C++ 03
// ========
// Dont forget to declare these two. You want to make sure they
// are unacceptable otherwise you may accidentally get copies of
// your singleton appearing.
Randomizer(Randomizer const&); // Don't Implement
void operator=(Randomizer const&); // Don't implement
public:
static Randomizer& get_instance(){
// The only instance of the class is created at the first call get_instance ()
// and will be destroyed only when the program exits
static Randomizer instance;
return instance;
}
bool method() { return true; };
int Rand(unsigned int floor, unsigned int ceil){
random::uniform_int_distribution<> Rand_ = random::uniform_int_distribution<> (floor,ceil);
return (Rand_(rng_));
}
// Is not considering the millisecons
time_duration Rand_time_duration(){
boost::posix_time::time_duration floor(0, 0, 0, 0);
boost::posix_time::time_duration ceil(23, 59, 59, 0);
unsigned int Rand_seconds = Rand(floor.total_seconds(), ceil.total_seconds());
return seconds(Rand_seconds);
}
date Rand_date_from_Epoch_to_now(){
date now = second_clock::local_time().date();
return Rand_date_from_Epoch_to_ceil(now);
}
date Rand_date_from_Epoch_to_ceil(date ceil_date){
date Epoch = ptime(date(1970,1,1)).date();
return Rand_date_in_interval(Epoch, ceil_date);
}
date Rand_date_in_interval(date floor_date, date ceil_date){
return Rand_ptime_in_interval(ptime(floor_date), ptime(ceil_date)).date();
}
ptime Rand_ptime_from_Epoch_to_now(){
ptime now = second_clock::local_time();
return Rand_ptime_from_Epoch_to_ceil(now);
}
ptime Rand_ptime_from_Epoch_to_ceil(ptime ceil_date){
ptime Epoch = ptime(date(1970,1,1));
return Rand_ptime_in_interval(Epoch, ceil_date);
}
ptime Rand_ptime_in_interval(ptime floor_date, ptime ceil_date){
time_duration const diff = ceil_date - floor_date;
long long gap_seconds = diff.total_seconds();
long long step_seconds = Randomizer::get_instance().Rand(0, gap_seconds);
return floor_date + seconds(step_seconds);
}
};
#include <iostream>
#include <cstdlib>
#include <ctime>
int main() {
srand(time(NULL));
int random_number = std::Rand(); // Rand() return a number between 0 and Rand_MAX
std::cout << random_number;
return 0;
}
ここから乱数を生成するための完全なRandomer
クラスコードを入手できます!
プロジェクトのさまざまな部分に乱数が必要な場合は、すべてのRandomer
をその中にカプセル化するための個別のクラスrandom
を作成できます。
そんな感じ:
class Randomer {
// random seed by default
std::mt19937 gen_;
std::uniform_int_distribution<size_t> dist_;
public:
/* ... some convenient ctors ... */
Randomer(size_t min, size_t max, unsigned int seed = std::random_device{}())
: gen_{seed}, dist_{min, max} {
}
// if you want predictable numbers
void SetSeed(unsigned int seed) {
gen_.seed(seed);
}
size_t operator()() {
return dist_(gen_);
}
};
そのようなクラスは後で便利になるでしょう:
int main() {
Randomer randomer{0, 10};
std::cout << randomer() << "\n";
}
ランダムな文字列を生成するには、 このリンクは例としてこのリンクを使用します このようなRandomer
クラスを確認できます。必要に応じてRandomer
を使用することもできます。
毎回6回同じではなく、毎回異なる乱数を生成します。
ユースケースシナリオ
私はPredictabilityの問題を、それぞれ0から5の値が書かれた6ビットの紙の袋に例えました。新しい値が必要になるたびに、一枚の紙が袋から引き出されます。袋が空の場合は、番号が袋に戻されます。
これから、ある種のアルゴリズムを作成できます。
アルゴリズム
バッグは通常Collection
です。バッグの役割を果たすためにbool[]
(別名ブール配列、ビットプレーン、またはビットマップ)を選びました。
私がbool[]
を選んだのは、各項目のインデックスがすでに各紙の値になっているからです。論文がそれらに書かれた何か他のものを必要とするならば、私はその代わりにDictionary<string, bool>
を使用したでしょう。ブール値は、数値がまだ描画されているかどうかを追跡するために使用されます。
RemainingNumberCount
というカウンターは、乱数が選択されるとカウントダウンする5
に初期化されます。これにより、新しい数字を描くたびに何枚の紙が残っているかを数える必要がなくなります。
次のランダムな値を選択するために、私はfor..loop
を使用してインデックスの集まりをスキャンし、index
がfalse
がNumberOfMoves
と呼ばれるときにカウントダウンするカウンターを使用しています。
NumberOfMoves
は次に利用可能な番号を選択するために使用されます。 NumberOfMoves
は、最初は0
と5
の間のランダムな値に設定されています。これは、バッグを介して実行できるステップが0..5あるためです。次回の反復では、NumberOfMoves
は0
と4
の間のランダムな値に設定されています。これは、バッグを介して実行できるステップが0..4になったためです。数が使われるにつれて、利用可能な数は減るので、代わりにRand() % (RemainingNumberCount + 1)
を使ってNumberOfMoves
の次の値を計算します。
NumberOfMoves
カウンタがゼロに達すると、for..loop
は次のようになります。
for..loop
のインデックスと同じになるように設定します。false
に設定します。for..loop
から抜けてください。コード
上記の解決策のコードは次のとおりです。
(次の3つのブロックをメインの.cppファイルに順番に入れます)
#include "stdafx.h"
#include <ctime>
#include <iostream>
#include <string>
class RandomBag {
public:
int Value = -1;
RandomBag() {
ResetBag();
}
void NextValue() {
int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);
int NumberOfMoves = Rand() % (RemainingNumberCount + 1);
for (int i = 0; i < BagOfNumbersLength; i++)
if (BagOfNumbers[i] == 0) {
NumberOfMoves--;
if (NumberOfMoves == -1)
{
Value = i;
BagOfNumbers[i] = 1;
break;
}
}
if (RemainingNumberCount == 0) {
RemainingNumberCount = 5;
ResetBag();
}
else
RemainingNumberCount--;
}
std::string ToString() {
return std::to_string(Value);
}
private:
bool BagOfNumbers[6];
int RemainingNumberCount;
int NumberOfMoves;
void ResetBag() {
RemainingNumberCount = 5;
NumberOfMoves = Rand() % 6;
int BagOfNumbersLength = sizeof(BagOfNumbers) / sizeof(*BagOfNumbers);
for (int i = 0; i < BagOfNumbersLength; i++)
BagOfNumbers[i] = 0;
}
};
コンソールクラス
このConsoleクラスを作成するのは、出力のリダイレクトが簡単になるからです。
以下のコードでは...
Console::WriteLine("The next value is " + randomBag.ToString());
...に置き換えることができます...
std::cout << "The next value is " + randomBag.ToString() << std::endl;
...そして必要ならばこのConsole
クラスを削除することができます。
class Console {
public:
static void WriteLine(std::string s) {
std::cout << s << std::endl;
}
};
主な方法
使用例は次のとおりです。
int main() {
srand((unsigned)time(0)); // Initialise random seed based on current time
RandomBag randomBag;
Console::WriteLine("First set of six...\n");
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
Console::WriteLine("\nSecond set of six...\n");
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
Console::WriteLine("\nThird set of six...\n");
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
randomBag.NextValue();
Console::WriteLine("The next value is " + randomBag.ToString());
Console::WriteLine("\nProcess complete.\n");
system("pause");
}
出力例
プログラムを実行したところ、次のような出力が得られました。
First set of six...
The next value is 2
The next value is 3
The next value is 4
The next value is 5
The next value is 0
The next value is 1
Second set of six...
The next value is 3
The next value is 4
The next value is 2
The next value is 0
The next value is 1
The next value is 5
Third set of six...
The next value is 4
The next value is 5
The next value is 2
The next value is 0
The next value is 3
The next value is 1
Process complete.
Press any key to continue . . .
閉会文
このプログラムはVisual Studio 2017を使って書かれていて、Visual C++ Windows Console Application
を使って.Net 4.6.1
プロジェクトにすることにしました。
ここでは特に特別なことはしていませんので、コードは以前のバージョンのVisual Studioでも機能するはずです。
これが解決策です。乱数を返す関数を作成し、それをメイン関数の外側に配置してグローバルにします。これが役に立つことを願っています
#include <iostream>
#include <cstdlib>
#include <ctime>
int rollDie();
using std::cout;
int main (){
srand((unsigned)time(0));
int die1;
int die2;
for (int n=10; n>0; n--){
die1 = rollDie();
die2 = rollDie();
cout << die1 << " + " << die2 << " = " << die1 + die2 << "\n";
}
system("pause");
return 0;
}
int rollDie(){
return (Rand()%6)+1;
}
すべてのRUNファイルをランダムに
size_t randomGenerator(size_t min, size_t max) {
std::mt19937 rng;
rng.seed(std::random_device()());
//rng.seed(std::chrono::high_resolution_clock::now().time_since_Epoch().count());
std::uniform_int_distribution<std::mt19937::result_type> dist(min, max);
return dist(rng);
}