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

时间:2015-06-24 21:10:05

标签: c random primes modular-arithmetic

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

  1. How do I extract specific 'n' bits of a 32-bit unsigned integer in C?
  2. Fast multiplication and subtraction modulo a prime
  3. Fast multiplication modulo 2^16 + 1
  4. 然而,

    它不适用于uint32_t hi, lo;,这意味着我无法理解问题的已签名和未签名方面。

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

       if (x1 > r)
            x1 += r + 2; 
    
    • 有人可以澄清我混淆的根源吗?

    • 代码本身可以改进吗?

    • 发电机应该避免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);
    }
    

2 个答案:

答案 0 :(得分:2)

以下if语句

if (x1 > r)
    x1 += r + 2;

应该写成

if (x1 > r)
    x1 -= r;

两个结果都是相同的模2 ^ 31:

x1 + r + 2 = x1 + 2^31 - 1 + 2 = x1 + 2^31 + 1
x1 - r = x1 - (2^31 - 1) = x1 - 2^31 + 1

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

第二种解决方案可以避免溢出,同时适用于int32_tuint32_t

您还可以使用r的整数常量:

uint64_t r = 0x7FFFFFFF; // 2^31 - 1

或者只是

uint64_t r = INT32_MAX;

编辑:对于格式为2 ^ p-k的素数,您必须使用带有p位的掩码并使用

计算结果
uint32_t x1 = (k * hi + lo) % ((1 << p) - k)

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

uint32_t x1 = ((uint64_t)a * x) % ((1 << p) - k)

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

答案 1 :(得分:1)

苏提供了这个解决方案:

  

通过一些实验(底部的新代码),我能够使用   uint32_t,这进一步暗示我不明白是怎么回事   有符号整数与位操作一起工作。

     

以下代码使用hi作为输入以及lo #include <inttypes.h> // x1 = a*x0 (mod 2^31-1) uint32_t lgc_m(uint32_t a, uint32_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); } uint32_t hi=0, lo=0; int i, p = 31;//2^31-1 for (i = 1; i < p; ++i){ r |= 1 << i; } hi = c >> p; lo = (c & r) ; uint64_t x1 = (uint64_t ) ((hi + lo) ); // NOT SURE ABOUT THE NEXT STATEMENT if (x1 > r){ printf("x1 - r = %"PRId64"\n", x1- r); x1 -= r; } 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((uint32_t) x1); } int main(void) { uint32_t r; r = lgc_m(1583458089, 1583458089); r = lgc_m(1583458089, 2147483645); return(0); }

r = 2^31-1
     

问题是我的假设是减少将完成   一次过后。如果(x> 2 31 -1),那么按照定义   没有发生减少,需要第二次通过。减法   2 31 -1,在这种情况下就可以了。在第二次尝试   以上,x -= r因此是模数。 printf()实现了   最后的减少。

     

也许是具有随机数或模块化减少专业知识的人   可以更好地解释它。

     

没有uint32_t lgc_m(uint32_t a, uint32_t x){ uint64_t c, x1, m = 2147483647; //modulus: m = 2^31-1 if (x == m) return (0); c = (uint64_t)a * (uint64_t)x; if (c < m)//no reduction necessary return (c); uint32_t hi, lo, p = 31;//2^p-1, p = 31 hi = c >> p; lo = c & m; x1 = (uint64_t)(hi + lo); if (x1 > m){//one more pass needed //this block can be replaced by x1 -= m; hi = x1 >> p; lo = (x1 & m); x1 = (uint64_t)(hi + lo); } return((uint32_t) x1); } 的清除功能。

try {
    $conn = new PDO("sqlsrv:Server=$pdoServer;Database=$pdoDatabase;", $pdoUID, $pdoPWD);
    $conn->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );  
} catch (PDOException $e) {
    die(error_log(print_r("Could not connect to SQL Server".$e->getMessage(), TRUE), 0));
}

if (isValidUsername($username)) {
    $res_sites = $res_PR = $res_TA = array();

    try {
        $stmt_PR = $conn->prepare("SELECT Count(*) AS PRcounter FROM tbl1 WHERE usr_username=:username");
        $stmt_TA = $conn->prepare("SELECT Count(*) AS TAcounter FROM tbl2 WHERE usr_username=:username");

        $stmt_PR->bindParam(':username',$username,PDO::PARAM_STR);
        $stmt_TA->bindParam(':username',$username,PDO::PARAM_STR);

        $stmt_PR->execute();
        $stmt_TA->execute();

        $res_PR = $stmt_PR->fetch();
        $res_TA = $stmt_TA->fetch();

        $stmt_sites = $conn2->query("SELECT site_ID,site_name FROM tbl3 WHERE isDeleted IS NULL ORDER BY site_name ASC");
        while ($row = $stmt_sites->fetch(PDO::FETCH_ASSOC)){
            $res_sites[$row['site_ID']]['site_name']=$row['site_name'];
        }
    } catch (PDOException $e) {
        echo "Error occurred. Error message: ". $e->getMessage();
    }

    /* do more stuff... */
}
相关问题