で、標準関数のrand()を使って要素数1000個とか10000個とかの配列をでっち上げたりしていたわけですが、ここで問題にぶち当たりました。
C言語のrand()は0以上RAND_MAX以下の擬似乱数を発生させるわけですが、mingw-gccだとこのRAND_MAXの値が0x7fff(32767)しかありません。
これではもっと大きい数を扱いたい場合に色々と不便なので、「もっとよい乱数生成関数はないかいな」と探してみたところ、xorshiftというのがなかなか良さそうです。
参考:良い乱数・悪い乱数
なんでもやたら速いうえに周期が128bitもあるそうなので、uint64_tな範囲でも問題なく扱えそう。
というわけで書いてみました。
/* xorshift.h*/ #ifndef XOR_SHIFT_H #define XOR_SHIFT_H #include <stdint.h> uint64_t *srand_xs64(void); uint64_t rand_xs64(uint64_t *x); void close_xs64(uint64_t *x); #endif
/* xor128.c*/ #include <stdlib.h> #include <stdint.h> #include <time.h> #include "xorshift.h" uint64_t *srand_xs64(void) { uint64_t *x = (uint64_t *)malloc(sizeof(uint64_t) * 4); if (!x) return NULL; uint64_t s; do { s = (uint64_t)(time(NULL)); } while (!s); *x = (s << 32) | s; *(x + 1) = (*x << 8) | ((*x & 0xff00000000000000) >> 56); *(x + 2) = (*(x + 1) << 8) | ((*(x + 1) & 0xff00000000000000) >> 56); *(x + 3) = (*(x + 2) << 8) | ((*(x + 2) & 0xff00000000000000) >> 56); return x; } uint64_t rand_xs64(uint64_t *x) { uint64_t t = (*x ^ (*x << 11)); *x = *(x + 1); *(x + 1) = *(x + 2); *(x + 2) = *(x + 3); *(x + 3) = (*(x + 3) ^ (*(x + 3) >> 19)) ^ (t ^ (t >> 8)); return *(x + 3); } void close_xs64(uint64_t *x) { if (x) { free(x); x = NULL; } }
#include <stdio.h> #include <inttypes.h> #include "xorshift.h" int main(void) { uint64_t *seed = srand_xs64(); if (!seed) return -1; for (int i = 0; i < 100000) printf("%"PRIu64"\n", rand_xs64(seed)); close_xs64(seed); return 0; }
とりあえず10万個程度の乱数を発生させてみてもダブりがひとつも出ないし(当たり前なんだろうけど)、スピードも速いみたい。
素晴らしい!
0 件のコメント:
コメントを投稿