【问题标题】:How to implement a fast RNG?如何实现快速 RNG?
【发布时间】:2014-07-11 11:34:12
【问题描述】:

我正在尝试从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.

【问题讨论】:

  • 您的代码无法编译。真正的代码在哪里?你期望什么输出?你观察到什么输出?如果你有所谓的等效 C 代码,为什么你可以通过调试找到差异出现的地方?
  • @DavidHeffernan 我观察到相同的数字,我希望随机数..
  • @DavidHeffernan 在变量I 中一遍又一遍地重复相同的数字。
  • 程序总是产生相同的数字也就不足为奇了。如果你的种子没有改变,你会期待什么?在GenerateSeed 中包含一些随机/熵。
  • 你不是一直在打GenerateSeed吗?你应该调用一次。此外,正如@gammatester 所说,它应该是一个随机值。

标签: delphi delphi-xe6


【解决方案1】:

每次运行问题中的程序时获得相同值的原因是每次都使用相同的种子。如果我正确理解您的 cmets。 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 位种子。

【讨论】:

猜你喜欢
  • 2015-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多