質問
各数値を生成する前にシードが与えられた場合に高速に動作するように特化した擬似乱数ジェネレーターを探しています。私がこれまでに見たジェネレーターのほとんどは、シードを 1 回設定してから、長い一連の数値を生成することを前提としていました。私がこれまでに見たものと若干似ている唯一のものは Perlin Noise ですが、生成されるデータが「滑らかすぎる」ため、同様の入力に対しては同様の結果が生成される傾向があります。
ジェネレーターの宣言は次のようになります。
int RandomNumber1(int seed);
または:
int RandomNumber3(int seedX, int seedY, int seedZ);
入力をハッシュして結果を RandomNumber1 に渡すことで RandomNumber3 を実装できるため、適切な RandomNumber1 があれば十分だと思いますが、実装によっては独立した入力を使用できる場合に備えて 2 番目のプロトタイプを作成しました。
このジェネレーターの使用目的は、木をグリッドに配置し、ランダムな樹種と各場所のランダムな空間オフセットを決定することによってフォレストを生成するなど、手続き型コンテンツ ジェネレーターに使用することです。
プロシージャル コンテンツはレンダリング中にリアルタイムで大量に作成されるため、ジェネレーターは非常に効率的 (500 CPU サイクル未満) である必要があります。
解決
PRNG ではなくハッシュ関数を求めているようです。「高速ハッシュ関数」でグーグル検索すると、有望そうな結果がいくつか得られます。
例えば:
uint32_t hash( uint32_t a)
a = (a ^ 61) ^ (a >> 16);
a = a + (a << 3);
a = a ^ (a >> 4);
a = a * 0x27d4eb2d;
a = a ^ (a >> 15);
return a;
}
編集: はい、一部のハッシュ関数は他のハッシュ関数よりも適切に見えます。
目的のためには、関数に注目して、入力の 1 ビットの変更が多数の出力ビットに伝播することを確認するだけで十分です。
他のヒント
はい、PRNGではなく高速整数ハッシュアルゴリズムを探しています。
このページにはいくつかのアルゴリズムがあります。正しい検索用語を知っていれば、さらに多くを見つけることができます。
編集:元のページは削除されました。ライブバージョンはになります。 GitHubで見つかりました。
これは、George Marsagliaによって開発された小さな乱数ジェネレータです。彼はこの分野の専門家なので、ジェネレーターには優れた統計特性があると確信できます。
v = 36969*(v & 65535) + (v >> 16);
u = 18000*(u & 65535) + (u >> 16);
return (v << 16) + u;
ここで、uとvは符号なし整数です。それらをゼロ以外の値に初期化します。乱数を生成するたびに、uとvをどこかに保存します。上記の署名に一致するように関数でこれをラップできます(intが署名されていないことを除く)。
std :: tr1 :: ranlux3
、または標準C ++ライブラリへのTR1追加の一部である他の乱数ジェネレーターを参照してください。最初にmt19937を提案しましたが、非常に高速である必要があることに注意してください。 TR1は、 Microsoft VC ++ およびGCCで入手できます。さらに多くのコンパイラをサポートするブーストライブラリにあります。
ブーストドキュメントからの例:
#include <random>
#include <iostream>
#include <iterator>
#include <functional>
#include <algorithm>
#include <ctime>
using namespace std;
using namespace std::tr1;
int main(){
random_device trueRand;
ranlux3 rng(trueRand); // produces randomness out of thin air
// see pseudo-random number generators
uniform_int<> six(1,6); // distribution that maps to 1..6
// see random number distributions
variate_generator<ranlux3&, uniform_int<> >
die(rng, six); // glues randomness with mapping
// simulate rolling a die
generate_n( ostream_iterator<int>(cout, " "), 10, ref(die));
}
出力例:
2 4 4 2 4 5 4 3 6 2
任意のTR1乱数ジェネレーターは、任意のその他乱数ジェネレーターをシードできます。より高品質の結果が必要な場合は、mt19937の出力(低速ですが高品質)を高速ジェネレーターであるminstd_randまたはrandlux3に供給することを検討してください。
メモリが実際に問題ではなく、速度が最も重要な場合、乱数の大きな配列を事前に構築し、実行時にそれを反復処理することができます。たとえば、個別のプログラムで100,000個の乱数を生成し、
のような独自のファイルとして保存しますunsigned int randarray [] = {1,2,3、....}
次に、そのファイルをコンパイルに含めます。実行時に、乱数関数はその配列から数値を取得し、最後に到達したときに最初にループバックするだけです。
Java乱数ライブラリで次のコードを使用しています-これは非常にうまく機能しています。手続き型コンテンツの生成にも使用します。
/**
* State for random number generation
*/
private static volatile long state=xorShift64(System.nanoTime()|0xCAFEBABE);
/**
* Gets a long random value
* @return Random long value based on static state
*/
public static long nextLong() {
long a=state;
state = xorShift64(a);
return a;
}
/**
* XORShift algorithm - credit to George Marsaglia!
* @param a initial state
* @return new state
*/
public static final long xorShift64(long a) {
a ^= (a << 21);
a ^= (a >>> 35);
a ^= (a << 4);
return a;
}