【问题标题】:Create a random BigInt for Miller-Rabin test为 Miller-Rabin 测试创建一个随机 BigInt
【发布时间】:2021-12-11 08:06:04
【问题描述】:

我正在使用 JavaScript BigInts 实现 Miller Rabin primality test

基本算法没问题 - 我已经完成了,但它需要一个 0 到(正在测试的数字 - 3)范围内的随机数。我无法使用 Math.random() 和扩展,因为我使用的是 BigInt。

这不需要加密安全,只要足够随机,所以我选择生成一串随机选择的十六进制数字并将其转换为 BigInt。

代码如下:

function getRandomBigint(lower, upper) {

    // Convert to hex strings so that we know how many digits to generate
    let hexDigits = new Array(upper.toString(16).length).fill('');
    let rand;
    let newDigits;
    do {
        // Fill the array with random hex digits and convert to a BigInt
        newDigits = hexDigits.map(()=>Math.floor(Math.random()*16).toString(16));
        rand = BigInt('0x'+newDigits.join(''));
    } while (rand < lower || rand > upper);
    return rand;
}

这里的问题是生成的数字可能超出范围。这个函数通过迭代来处理这个问题(糟糕地),直到它得到一个范围内的数字。显然,这可能需要很长时间。在实践中,它在传递一个数字之前从未迭代超过几十次,但随机性的本质意味着问题就在“外面”等着咬我!

我可以缩放或截断结果以使其在范围内,而不是迭代,但我担心这会影响随机性。我已经有一些证据表明这并不像它可能的那样随机,但这在这个应用程序中可能无关紧要。

那么,两个问题:

  • 这对米勒拉宾来说足够随机吗?
  • 如何处理超出范围的结果?

这是一个仅限 JavaScript 的项目 - 请不要使用库。

【问题讨论】:

  • 您可以使用 crypto.getRandomValues 用随机字节填充适当长度的 Uint8Array(实际上是加密安全的,即使您在这里不关心),然后只调整最后一个字节,或者使用 @ 987654325@,以获得所需的确切范围。然后将字节数组转换为 BigInt(遗憾的是目前不是一个非常有效的操作,但可能没关系)。
  • @Touffy crypto.getRandomValues 可能会改善我最初获得的值的随机性,但这是对最后一个字节的调整带来了麻烦。如果我的原始数字的最高十六进制数字是 1,那么我为该数字获得的几乎每个随机值都需要调整。我的第一次尝试(只是丢弃那个数字)使结果严重偏向值范围的低端。我有另一个想法,我正在测试它可能会简化整个问题。
  • 哦,不,getRandomValues 只是一种用随机值填充数组的更简洁的方法,而不是像你正在做的那样使用 Math.random 循环。至于最后一个字节,我的想法是将 Math.random 与您的范围的最大值 + 1 相乘,这样您就始终在您的范围内,而不会出现偏差。

标签: javascript random primes


【解决方案1】:

Miller Rabin 检验是一种概率1 检验。也就是说,确定一个数字不是素数是确定性的,但素数指示只是 - 一个数字可能是素数的指示。

原因是测试中使用的某些碱基可能导致质数指示,即使目标数不是质数。在测试中使用随机数作为基数允许每次使用不同的基数重复运行测试,从而增加结果正确指示素数或非素数的概率2

因此,选择的随机数必须小于被测试的数字。没有要求从完整范围中选择它们。

考虑到这一点,我现在有了这个:

function getRandomBigInt(upper) {
    let maxInt = BigInt(Number.MAX_SAFE_INTEGER);
    if (upper <= maxInt) {
        return BigInt((Math.floor(Math.random()*Number(upper))));
    } else {
        return BigInt((Math.floor(Math.random()*Number.MAX_SAFE_INTEGER)));
    }

}

9007199254740991 (Number.MAX_SAFE_INTEGER) 应该为此目的提供足够大的数字范围。到目前为止,它适用于我的 Miller-Rabin 实现。

1 针对一个简短的特定碱基列表测试高达 3,317,044,064,679,887,385,961,981 的数字将产生明确的素数/非素数结果。

2对于非确定性素数结果,仍然需要进行确定性测试(如试除法)来确认。

【讨论】:

    【解决方案2】:

    获取范围内随机数的方法是

      lower + rand() % (upper - lower)
    

    rand() 是返回大于(上 - 下)随机数的任何函数。可以使用 Integer 或 Bigint 进行数学运算。

    function rand16() {
        // 0 .. 2^16-1
        return BigInt(Math.floor(Math.random()*65536));
    }
    function rand() {
       // -2^63 .. 2^63-1
       return BigInt( (((rand16() * 65536) + rand16())* 65536 + rand16()) * 65536 + rand16() );
    }
    

    【讨论】:

    • rand() 是什么?
    • 我们正在询问如何编写函数rand
    猜你喜欢
    • 2019-10-04
    • 2016-02-27
    • 2013-06-09
    • 1970-01-01
    • 2012-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-05
    相关资源
    最近更新 更多