计算 ((2^n)-1)mod p 的最佳方法

7
我正在处理一个密码学练习,我想计算(2n-1)mod p,其中p是一个质数。
在这种情况下,最好的方法是什么?我使用C语言,当n很大时,2n-1变得太大而无法容纳。
我发现方程式(a*b)modp=(a(bmodp))modp,但我不确定这是否适用于此情况,因为2n-1可能是质数(或者我不知道如何因式分解)。
非常感谢您的帮助。

n和p的数值范围非常重要。 - Potatoswatter
1
P是一个10位素数(比如说1000000007),而n最多为9位数字。 - navinpai
是否存在任何小于999999999的10位质数,或者有一个特定的上限?2^32 ≈ 4*10^9,这是10位数字,所以问题是在不溢出本机64位整数类型的情况下是否可以对小于p的任何数字进行平方。 - Potatoswatter
我可以选择我的质数,比如说,我选择它为1000000007(第一个10位质数)。 - navinpai
7个回答

6
一些提示可以帮助您想出更好的方法:
  1. 不要使用 (a*b)modp=(a(bmodp))modp 来计算 2n-1 mod p,而是先计算 2n mod p,然后再进行减法运算。
  2. 费马小定理 在这里可能很有用。这样,您实际需要处理的指数将不会超过 p。

我更喜欢你的帖子,回答得很好。 - zeeshan mughal
但是我如何“之后再减去”呢?因为模数可能在其中一步骤中发生变化。例如,(100mod100)=0,而(100-1)mod100=(99mod100)=99。那么之后再减去怎么办? - navinpai
除非p=2,否则2^n mod p不会等于0。 - Dennis Meng
但在一般情况下,你可以只是测试它。 - Dennis Meng
@navinpai:-1 mod 100 = 99。 - Dietrich Epp

1
在评论中提到,np是9或10位数字。如果将它们限制为32位(unsigned long)值,则可以使用简单的(二进制)模数幂运算找到2^n mod p
unsigned long long u = 1, w = 2;

while (n != 0)
{
    if ((n & 0x1) != 0)
        u = (u * w) % p; /* (mul-rdx) */

    if ((n >>= 1) != 0)
        w = (w * w) % p; /* (sqr-rdx) */
}

r = (unsigned long) u;

而且,由于 (2^n - 1) mod p = r - 1 mod p

r = (r == 0) ? (p - 1) : (r - 1);

如果2^n mod p = 0,但实际上如果p > 2是质数,则不会发生这种情况,但我们可以考虑一般情况,那么(2^n - 1) mod p = -1 mod p
由于“公共余数”或“余数”(mod p)[0,p-1]中,因此我们添加了一些p的倍数,使其在此范围内。
否则,2^n mod p的结果将在[1,p-1]中,并且减去1将已经在此范围内。可能更好地表达为:
if (r == 0)
    r = p - 1; /* -1 mod p */
else
    r = r - 1;

谢谢。这个完美地解决了问题。但是,你能否请解释一下最后一行的作用?我不确定它在做什么。 - navinpai
@navinpai - 这是三元/条件运算符 - 我会更新答案。 - Brett Hale

0

要进行模运算,你必须拥有2^n-1,否则你将进入不同的算法方向,虽然很有趣但是与此无关。因此我建议你使用大整数概念,这样会更容易...可以制定一个结构并在小值中实现大值,例如:

  struct bigint{
    int lowerbits;
    int upperbits;
  }

语句的分解也有解决方案,例如 2^n = (2^n-4 * 2^4 )-1%p,将它们分解并分别处理,那将是相当算法化的。


0

要计算 2^n - 1 mod p,您可以在从 n 中删除 (p-1) 的任何倍数后使用平方指数法(因为 a^{p-1} = 1 mod p)。伪代码如下:

n = n % (p - 1)
result = 1
pow = 2
while n {
    if n % 2 {
        result = (result * pow) % p
    }
    pow = (pow * pow) % p
    n /= 2
}
result = (result + p - 1) % p

0

我在HackerRank上解决数学问题时,遇到了这里发布的答案,并且它已经适用于那里给出的所有测试用例。

如果将np限制为64位(无符号长)值,则以下是数学方法:

2^n - 1可以写成1*[ (2^n - 1)/(2 - 1) ]

仔细观察一下,这是GP的总和1 + 2 + 4 + .. + 2^(n-1)

我们知道(a+b)%m = ( (a%m) + (b%m) )%m

如果您对上述关系是否适用于加法感到困惑,您可以搜索一下或查看此链接:http://www.inf.ed.ac.uk/teaching/courses/dmmr/slides/13-14/Ch4.pdf

所以,现在我们可以将上述关系应用于我们的GP,你就能得到答案了! 也就是说, (2^n - 1)%p等价于(1 + 2 + 4 + .. + 2^(n-1))%p,然后应用给定的关系。

-1

首先,关注2n mod p,因为最后总是可以减去1。

考虑二的幂次方。这是通过重复乘以二产生的一系列数字。

考虑模运算。如果将数字写成p进制,你只需要获取最后一位数字。高位数字可以被丢弃。

因此,在序列中的某些点上,你会得到一个两位数(p位上为1),你的任务实际上就是在发生这种情况时摆脱第一个数字(减去p)。

概念上停留在这里,暴力方法可能是这样的:

uint64_t exp2modp( uint64_t n, uint64_t p ) {
    uint64_t ret = 1;
    uint64_t limit = p / 2;
    n %= p; // Apply Fermat's Little Theorem.

    while ( n -- ) {
        if ( ret >= limit ) {
            ret *= 2;
            ret -= p;
        } else {
            ret *= 2;
        }
    }
    return ret;
}

不幸的是,对于大的n和p,这仍然需要很长时间,我想不到任何更好的数论方法。

如果您有一个可以计算(p-1)^2而不会溢出的乘法设施,则可以使用类似的算法,在每次平方操作后使用模数进行重复平方,并在每次乘法后再次使用模数取平方残差系列的乘积。


-2

步骤1. x = 将1左移n次,然后减去1

步骤2. result = x和p的逻辑与操作


我认为问题并没有涉及操作将针对哪种类型的数据进行。因此,我只是提供了一个通用解决方案。 - Debobroto Das
1
问题在于这个不是通用的。移位和逻辑与运算是针对二进制串而不是整数的操作。通用解决方案是做出更少假设的解决方案。 - Potatoswatter
移除了变量x的int数据类型。 - Debobroto Das
1
除非使用一种奇异的平台,否则n > 62仍无法运行。同时,模数不是通过逻辑与计算的,除非p也是一个整数2^n-1。 - Potatoswatter
@Potatoswatter,现在我明白了我的答案的局限性。谢谢你的评论,我给你点赞了。 - Debobroto Das

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接