如何实现快速RNG?

时间:2014-07-11 11:34:13

标签: delphi delphi-xe6

我正在尝试从C移植基于128位XorShift的现有随机生成器。但是我在生成种子时遇到了麻烦,种子只是一次又一次地生成相同的数字。

static uint64_t s[ 2 ];

static uint64_t __inline next(void) {
    uint64_t s1 = s[ 0 ];
    const uint64_t s0 = s[ 1 ];
    s[ 0 ] = s0;
    s1 ^= s1 << 23;
    return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0;
}

uint64_t getusertime() {
    struct rusage rusage;
    getrusage( 0, &rusage );
    return rusage.ru_utime.tv_sec * 1000000ULL + ( rusage.ru_utime.tv_usec / 1000 ) * 1000;
}

int main( int argc, char* argv[] ) {
    const long long int n = strtoll( argv[1], NULL, 0 );
    uint64_t t = 0;

    for( int i = 0; i < 2; i++ ) s[ i ] = -1ULL / 3;

    const int64_t start = getusertime();

    for( long long int i = n; i-- != 0; ) t ^= next();

    const int64_t elapsed = getusertime() - start;
    const double secs = elapsed / 1E6;
    printf( "%f s, %.02f queries/s, %.02f ns/query\n", secs, n / secs, 1E9 * secs / n );
    if ( t == 0 ) putchar( 0 );
    return 0;
}
program Project1;

var
  S: Array [0..1] of UInt64;

function XorShift128: UInt64;
var
  s0, s1: UInt64;
begin
  s1 := s[0];
  s0 := s[1];
  s[0] := s0;
  s1 := s1 xor (s1 shl 23);
  s[1] := (s1 xor s0 xor (s1 shr 17) xor (s0 shr 26));
  Result := s[1] + s0;
end;

procedure GenerateSeed;
var
  I: Integer;
begin
  for I := 0 to High(S) do
  S[I] := MaxLongInt div 3;
end;

var
  I: UInt64;
begin 
  GenerateSeed;
  I := XorShift128;
end.

1 个答案:

答案 0 :(得分:5)

每次在问题中运行程序时获得相同值的原因是每次都使用相同的种子。如果我正确理解你的评论。 C和Pascal之间的另一个区别是种子 - 见下文。

但是,您的代码很好,并且是C代码的准确翻译。这个C程序的输出:

#include <stdio.h>
#include <stdint.h>

static uint64_t s[ 2 ];

static uint64_t __inline next(void) {
    uint64_t s1 = s[ 0 ];
    const uint64_t s0 = s[ 1 ];
    s[ 0 ] = s0;
    s1 ^= s1 << 23;
    return ( s[ 1 ] = ( s1 ^ s0 ^ ( s1 >> 17 ) ^ ( s0 >> 26 ) ) ) + s0;
}

int main(void) 
{
    s[ 0 ] = s[ 1 ] = 715827882; // the value of MaxLongInt div 3
    printf("%llu\n", next());
    printf("%llu\n", next());
    printf("%llu\n", next());
    return 0;
}

6004846026386057
6004846115863870
12676181551404632061

这个Delphi程序的输出:

program Project1;

{$APPTYPE CONSOLE}

var
  S: Array [0..1] of UInt64;

function XorShift128: UInt64;
var
  s0, s1: UInt64;
begin
  s1 := s[0];
  s0 := s[1];
  s[0] := s0;
  s1 := s1 xor (s1 shl 23);
  s[1] := (s1 xor s0 xor (s1 shr 17) xor (s0 shr 26));
  Result := s[1] + s0;
end;

procedure GenerateSeed;
var
  I: Integer;
begin
  for I := 0 to High(S) do
    S[I] := MaxLongInt div 3;
end;

begin
  GenerateSeed;
  Writeln(XorShift128);
  Writeln(XorShift128);
  Writeln(XorShift128);
end.

6004846026386057
6004846115863870
12676181551404632061

我注意到问题中的C代码使用了与您的翻译不同的种子。它使用-1ULL / 3为状态播种,并导致此输出:

46820872945684
46912499612351
13066320939010318272

要匹配Delphi代码中的那个,您将使用high(UInt64) div 3。这样做,你得到上面的输出。

这里一个重要的注意事项是你的Delphi代码只提供64位种子,但你的C代码提供128.我希望你应该提供128位种子。