C中线性同余生成器的快速模乘法模数

Sue*_*Sue 6 c random primes modular-arithmetic

我正在尝试用Mersenne prime(2 31 -1)作为模数来实现随机数生成器.以下工作代码基于几个相关的帖子:

  1. 如何在C中提取32位无符号整数的特定"n"位?
  2. 快速乘法和减法模数为素数
  3. 快速乘法模2 ^ 16 + 1

然而,

它不起作用uint32_t hi, lo;,这意味着我不理解问题的签名与未签名方面.

基于上面的#2,我期待答案是(hi + lo).这意味着,我不明白为什么需要以下声明.

   if (x1 > r)
        x1 += r + 2; 
Run Code Online (Sandbox Code Playgroud)
  • 有人可以澄清我的困惑的来源吗?

  • 代码本身可以改进吗?

  • 发电机应该避免0或2 31 -1作为种子吗?

  • 如何为一个素数(2 p -k)改变代码?

原始代码

#include <inttypes.h>
// x1 = a*x0 (mod 2^31-1)
int32_t lgc_m(int32_t a, int32_t x)
{
    printf("x %"PRId32"\n", x);
    if (x == 2147483647){
    printf("x1 %"PRId64"\n", 0); 
        return (0);
    }
    uint64_t  c, r = 1;
    c = (uint64_t)a * (uint64_t)x;
    if (c < 2147483647){
        printf("x1 %"PRId64"\n", c); 
        return (c);
    }
    int32_t hi=0, lo=0;
    int i, p = 31;//2^31-1
    for (i = 1; i < p; ++i){
       r |= 1 << i;
    }
   lo = (c & r) ;
   hi = (c & ~r) >> p;
   uint64_t x1 = (uint64_t ) (hi + lo);
   // NOT SURE ABOUT THE NEXT STATEMENT
   if (x1 > r)
        x1 += r + 2; 
   printf("c %"PRId64"\n", c);
   printf("r %"PRId64"\n", r);
   printf("\tlo %"PRId32"\n", lo);
   printf("\thi %"PRId32"\n", hi);
   printf("x1 %"PRId64"\n", x1); 
   printf("\n" );
   return((int32_t) x1);
}

int main(void)
{
    int32_t r;
    r = lgc_m(1583458089, 1);
    r = lgc_m(1583458089, 2000000000);
    r = lgc_m(1583458089, 2147483646);
    r = lgc_m(1583458089, 2147483647);
    return(0);
}
Run Code Online (Sandbox Code Playgroud)

nwe*_*hof 2

下面的if语句

if (x1 > r)
    x1 += r + 2;
Run Code Online (Sandbox Code Playgroud)

应该写成

if (x1 > r)
    x1 -= r;
Run Code Online (Sandbox Code Playgroud)

两个结果都以 2^31 为模:

x1 + r + 2 = x1 + 2^31 - 1 + 2 = x1 + 2^31 + 1
x1 - r = x1 - (2^31 - 1) = x1 - 2^31 + 1
Run Code Online (Sandbox Code Playgroud)

第一个解决方案溢出 anint32_t并假设从uint64_t到的转换int32_t是模 2^31。虽然许多 C 编译器以这种方式处理转换,但这并不是 C 标准所强制的。实际结果是实现定义的。

第二种解决方案避免溢出并适用于int32_tuint32_t

您还可以使用整数常量r

uint64_t r = 0x7FFFFFFF; // 2^31 - 1
Run Code Online (Sandbox Code Playgroud)

或者简单地

uint64_t r = INT32_MAX;
Run Code Online (Sandbox Code Playgroud)

编辑:对于 2^pk 形式的素数,您必须使用带有 p 位的掩码并使用以下命令计算结果

uint32_t x1 = (k * hi + lo) % ((1 << p) - k)
Run Code Online (Sandbox Code Playgroud)

如果k * hi + lo可以溢出 a uint32_t(即(k + 1) * (2^p - 1) >= 2^32),则必须使用 64 位算术:

uint32_t x1 = ((uint64_t)a * x) % ((1 << p) - k)
Run Code Online (Sandbox Code Playgroud)

根据平台的不同,后者可能会更快。