xorshift乱数ジェネレータ

高速で簡単

周期は2^128-1と十分
実装は以下のように簡素
そんでもって速いらしい

uint32_t xor128(void) { 
  static uint32_t x = 123456789;
  static uint32_t y = 362436069;
  static uint32_t z = 521288629;
  static uint32_t w = 88675123; 
  uint32_t t;
 
  t = x ^ (x << 11);
  x = y; y = z; z = w;
  return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8)); 
}

乱数の種は x,y,z,w を適当に設定するみたい
これは/dev/urandomあたりから引っ張ってくればいいかな

テスト

自宅のatomネットブックで速度チェックしてみる
IOでのオーバーヘッドを減らすため
ある程度まとめて計算してみた

#include <stdint.h>
#include <stdio.h>
#define N 1024

static uint32_t _data[N];

static uint32_t x = 123456789;
static uint32_t y = 362436069;
static uint32_t z = 521288629;
static uint32_t w = 88675123;

uint32_t xor128(void) {
  uint32_t t;

  t = x ^ (x << 11);
  x = y; y = z; z = w;
  return w = (w ^ (w >> 19)) ^ (t ^ (t >> 8));
}

void xor128seed(uint32_t seed[])
{
  x = seed[0];
  y = seed[1];
  z = seed[2];
  w = seed[3];
}

void main()
{
  int i;
  FILE* fp;

  fp = fopen("/dev/urandom","r");
  fgets((void*)_data,sizeof(uint32_t) * 4, fp);
  fclose(fp);
  xor128seed(_data);

  while(1)
  {
    for(i=0;i<N;++i)
    {
      _data[i] = xor128();
    }
    fwrite(_data, sizeof(uint32_t), N, stdout);
  }
}

これをコンパイル

$ gcc xorshift.c

結果

速度チェックのためdd経由で/dev/nullに出力
適当にCtrl+Cで停止させた

$ ./a.out | dd of=/dev/null bs=1M
^C0+49840 記録始め
0+49839 記録終わり
204533760 バイト (205 MB) コピー終了, 2.01939 s, 101 MB/s

101MB/s !
/dev/urandomの10倍
前にやったMTの2倍だね