web-dev-qa-db-ja.com

ゲームに適した乱数ジェネレーターとは何ですか?

C++のゲームで使用するのに適した乱数ジェネレーターとは何ですか?

私の考慮事項は次のとおりです。

  1. たくさんの乱数が必要なので、速度は良いです。
  2. プレイヤーは常に乱数について不平を言うでしょうが、私は自分が本当に自分の仕事をしたことを説明するリファレンスを参照できるようにしたいと思います。
  3. これは私があまり時間をかけない商用プロジェクトなので、アルゴリズムがa)比較的簡単に実装できるか、またはb)GPL以外の優れた実装が利用可能であれば、それは素晴らしいことです。
  4. 私はすでにかなり多くの場所でRand()を使用しているので、他のジェネレーターが必要とするすべての変更を正当化するのに適しています。

私はこの主題についてよく知らないので、私が思いつくことができる唯一の選択肢は Mersenne Twister ;です。これらの要件をすべて満たしていますか?他に何か良いことはありますか?

編集:Mersenne Twisterはコンセンサスの選択のようです。しかし、ポイント#4はどうですか?それは本当にRand()よりもはるかに優れていますか?

編集2:ポイント2について少し明確にしましょう:乱数を知ることによってプレイヤーが不正行為をする方法はありません。限目。人々(少なくともランダム性を理解している人)が文句を言わないほど十分にランダムにしたいのですが、予測については心配していません。だからスピードを最優先に考えました。

編集3:私は今、Marsaglia RNGに傾いていますが、まだもっと入力が欲しいです。したがって、私は賞金を設定しています。

編集4:ただのメモ:今日はUTCの真夜中の直前に回答を受け入れるつもりです(誰かの担当者のキャップをいじるのを避けるため)。だから、答えようと思っているなら、最後まで待ってはいけません!
また、MarsagliaのXORshiftジェネレーターの外観も気に入っています。誰かがそれらについて何か意見がありますか?

52
Michael Myers

George Marsaglia は、現在利用可能な最高で最速のRNGのいくつかを開発しました Multiply-with-carry 均一な分布で注目に値するものです。

=== 2018-09-12を更新===

私自身の作業では、現在 Xoshiro256 ** を使用しています。これは、MarsagliaのXorShiftの一種の進化/更新です。

26
redcalx

時々 ゲーム開発者は真のランダム性を望んでおらず、 シャッフルバッグ がより適切です。

ランダム性が必要な場合は、メルセンヌツイスターが要件を満たします。高速で、統計的にランダムで、期間が長く、多数の実装があります。

編集:Rand()は通常 線形合同ジェネレーター として実装されます。情報に基づいて目的に十分かどうかを選択するのがおそらく最良の方法です。

42
David Johnstone

今日、メルセンヌ・ツイスターよりもはるかに良い選択肢があります。これは、10年後に開発されたMersenneのデザイナーによって設計されたWELL512と呼ばれるRNGであり、ゲームのためのより優れた選択肢です。コードは、クリス・ロモント博士によってパブリックドメインに入れられました。彼は、この実装はメルセンヌより40%高速であり、状態に多くの0ビットが含まれている場合でも拡散やトラッピングの問題がなく、明らかにはるかに単純なコードであると主張しています。周期は2 ^ 512です。 PCは州を循環するのに10 ^ 100年以上かかるため、十分な大きさです。

これは、私がWELL512の実装を見つけたPRNGの概要を説明した論文です。 http://www.lomont.org/Math/Papers/2008/Lomont_PRNG_2008.pdf

つまり、10年後に同じデザイナーが作成した、より速く、よりシンプルで、Mersenneよりも優れた数値を生成します。どうすれば失敗しますか? :)

UPDATE(11-18-14):エラーを修正しました(上記のリンク先の論文で説明されているように、0xDA442D20ULを0xDA442D24ULに変更しました)。

/* initialize state to random bits */
static unsigned long state[16];
/* init should also reset this to 0 */
static unsigned int index = 0;
/* return 32 bit random number */
unsigned long WELLRNG512(void)
   {
   unsigned long a, b, c, d;
   a = state[index];
   c = state[(index+13)&15];
   b = a^c^(a<<16)^(c<<15);
   c = state[(index+9)&15];
   c ^= (c>>11);
   a = state[index] = b^c;
   d = a^((a<<5)&0xDA442D24UL);
   index = (index + 15)&15;
   a = state[index];
   state[index] = a^b^d^(a<<2)^(b<<18)^(c<<28);
   return state[index];
   }
38
Chris Lomont

Mersenne Twisterは、特にSIMDに適しており、超高速にすることができるため、業界では一般的です。 Knuth も人気です(ありがとう、David)。

ほとんどのゲームアプリケーションでは、速度が本当に重要な要素です。プレイヤーは、7、2、2、3、2、3、2、3、7、2、3、2、3、2、3の前に常に3を生成する傾向があるという事実よりも、フレームレートが低いことに不満を抱くからです。 9の順序で。

もちろん、お金を賭けたギャンブルは例外ですが、関連するライセンス機関が具体的に使用できるアルゴリズムをレイアウトします。

10
Crashworks

安価なウェブカメラ、電離煙検知器を購入します。両方を分解すると、煙検出器には放射性物質(ガンマ波の発生源)がほとんど含まれなくなり、ウェブカメラで光子が発射されます。それが真のランダム性の源です:)

9
Vexatus

Mersenne Twisterは非常に優れており、高速でもあります。私はそれをゲームで使用しましたが、実装または使用することはまったく難しくありません。

WELLランダムアルゴリズム は、メルセンヌツイスターの改良版として設計されました。 Game Gems 7には詳細情報があります。その上で、あなたがそれを借りたりそれを手に入れることができるなら。

リンクしたWELLページでは、数字はアルゴリズムの期間です。つまり、再シードが必要になる前に、2 ^ N-1の数値を取得できます。Nは、512、1024、19937、または44497です。メルセンヌツイスターの周期は、N = 19937、つまり2 ^ 19937-1です。これが非常に大きな数であることを見てください:)

他に指摘できることは、 boostランダムライブラリ があることです。

あなたの編集に応じて、はいTwisterまたはWELLはRand()よりもはるかに優れています。また、古いモジュラストリックは、数値の分布に悪影響を及ぼします。ブーストを使用するさらに多くの理由:)

6
GManNickG

私は Isaac のファンです。mersenseツイスターとは異なり、暗号的に安全です(*ロールを観察して期間をクラックすることはできません)。

[〜#〜] ibaa [〜#〜] (rc4?)も Blizzardで使用 であり、ルートロールに使用される乱数を予測できないようにします。バトルネットサーバーでプレイしているときに、ディアブロIIでも同様のことが行われると思います。

*妥当な期間内にできない(数世紀?)

4
Ape-inago

リアルタイムゲームでは、「良い」ジェネレータと「悪い」ジェネレータの違いをプレイヤーが判断する方法はありません。ターン制のゲームでは、あなたの言う通りです。少数の狂信者が文句を言うでしょう。彼らはあなたが悪い乱数発生器でどのように彼らの人生を台無しにしたかについての話を、耐え難いほど詳細に伝えます。

真正な乱数の束が必要な場合(そしてオンラインゲームの場合)は、 Random.org で入手できます。ターンベースのゲームに、またはリアルタイムゲームのシードとして使用します。

4
Nosredna

Ian C. Bullardによる乱数ジェネレータに基づく:

_// utils.hpp
namespace utils {
    void srand(unsigned int seed);
    void srand();
    unsigned int Rand();
}

// utils.cpp
#include "utils.hpp"
#include <time.h>

namespace {
    static unsigned int s_Rand_high = 1;
    static unsigned int s_Rand_low = 1 ^ 0x49616E42;
}

void utils::srand(unsigned int seed)
{
    s_Rand_high = seed;
    s_Rand_low = seed ^ 0x49616E42;
}

void utils::srand()
{
    utils::srand(static_cast<unsigned int>(time(0)));
}

unsigned int utils::Rand()
{
    static const int shift = sizeof(int) / 2;
    s_Rand_high = (s_Rand_high >> shift) + (s_Rand_high << shift);
    s_Rand_high += s_Rand_low;
    s_Rand_low += s_Rand_high;
    return s_Rand_high;
}
_

どうして?

  • とても、とても速い
  • ほとんどの標準のRand()実装よりも高いエントロピー
  • わかりやすい
3
Armin Ronacher

考慮すべき追加の基準はスレッドセーフです。 (そして、今日のマルチコア環境ではスレッドを使用する必要があります。)複数のスレッドからRandを呼び出すだけで、その決定論的な動作が混乱する可能性があります(ゲームがそれに依存している場合)。少なくとも、Rand_rに切り替えることをお勧めします。

2
geowar

GameRandはここに投稿されたアルゴリズムを実装します http://www.flipcode.com/archives/07-15-2002.shtml

これはもともと80年代後半に開発したものです。数値的品質の面でRand()を簡単に上回り、副次的な利点として、可能な限り最速のランダムアルゴリズムになるという利点があります。

1
sschaem

メルセンヌツイスターにも投票します。実装は広く利用可能であり、2 ^ 19937 -1という非常に長い期間を持ち、かなり高速であり、Marsagliaによって開発されたDiehardテストを含むほとんどのランダム性テストに合格しています。 LCGであるRand()とCo.は品質の低い偏差を生成し、それらの連続する値は簡単に推測できます。

ただし、注意点の1つは、MT=をランダム性テストに合格する状態に適切にシードすることです。通常、drand48()などのLCGがその目的で使用されます。

MTは設定したすべての要件を満たしていると思います)MWCG imoのようなものに行くのはやり過ぎです。

1

ターゲットOSによっては、/ dev/randomを使用できる場合があります。それは実際には実装を必要とせず、Linux(そしておそらく他のいくつかのオペレーティングシステム)では本当にランダムです。十分なエントロピーが得られるまで読み取りがブロックされるため、ファイルを読み取り、別のスレッドを使用してそれをバッファーまたは何かに格納することができます。ブロッキング読み取り呼び出しを使用できない場合は、/ dev/urandomを使用できます。/dev/randomとほぼ同じようにランダムデータを生成しますが、一部のランダムデータを再利用して出力を即座に提供します。安全性はそれほど高くありませんが、それをどのように使用するかによっては問題なく動作する可能性があります。

0

あのね?この答えがまったく駄目だと思ったら許してください...しかし、私は乱数を取得する方法としてDateTime.Now.Millisecondsを使用してきました(神はどんな理由かしか知りません...)。完全にランダムではないことはわかっていますが、それは...

私は乱数を取得するだけで、タイピングに煩わ​​されることはありませんでした! :P

0
jay_t55

人々(少なくともランダム性を理解している人)が文句を言わないほど十分にランダムにしたいのですが、予測については心配していません。

あはは!

あなたの本当の要件があります!

このアプリケーションでMersenne Twisterを使用することで、誰もあなたを非難することはできません。

0
Marsh Ray

どうやら(私はそれを読んだ場所を忘れたように、私はカレーがアルツハイマを防ぐのに良いことを忘れたように)新しく生成されたGUID数が多く、そのモジュロを使用して縮小できます。

したがって、SQL(私の領域)では、これはABS(CHECKSUM(NEWID()))%1000です。

ロブ

0
Rob Farley