在C中找到具有非常大的指数和除数的模数

时间:2018-11-29 07:55:36

标签: c modulo unsigned-long-long-int

我需要用C语言计算一个#localhost <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot "C:/xampp/htdocs/" ServerName localhost ServerAlias www.localhost ErrorLog "logs/localhost-error.log" CustomLog "logs/localhost-access.log" common </VirtualHost> #subdomain <VirtualHost *:80> ServerAdmin webmaster@localhost DocumentRoot "C:/xampp/htdocs/subdomain" ServerName subdomain.localhost ServerAlias www.subdomain.localhost ErrorLog "logs/localhost-error.log" CustomLog "logs/localhost-access.log" common </VirtualHost> 函数,其中除数q被确定为非常大(15,383,399,235,709,406,497),指数n 可能也是如此。 / p>

基于模乘的属性a^n mod q,我的尝试如下:

(a * b) mod n = ((a mod n) * (b mod n)) mod n

从上面的主要函数中可以看出,我使用基数3,指数(189 + 50 * 223)和除数15383399235709406497测试了modExp函数。它为输出#include <stdio.h> unsigned long long int modExp(unsigned long long int base, unsigned long long int expo, unsigned long long int mod) { unsigned long long int out = 1; unsigned long long int cnt; for (cnt=expo; cnt>0; cnt--) { out = (out * base) % mod; } return out; } int main() { printf("%llu", modExp(3, (189 + 50 * 223), 15383399235709406497)); return 0; } 给出了一些警告,

3915400295876975163

为验证此答案,我将结果(由C函数提供)与通过评估用Haskell编写的表达式In function ‘findxA’: findxA.c:17:32: warning: integer constant is so large that it is unsigned [enabled by default] unsigned long long int h = 12036625823877237123; ^ findxA.c:17:5: warning: this decimal constant is unsigned only in ISO C90 [enabled by default] unsigned long long int h = 12036625823877237123; ^ findxA.c:18:32: warning: integer constant is so large that it is unsigned [enabled by default] unsigned long long int q = 15383399235709406497; ^ findxA.c:18:5: warning: this decimal constant is unsigned only in ISO C90 [enabled by default] unsigned long long int q = 15383399235709406497; ^ findxA.c: In function ‘main’: findxA.c:34:48: warning: integer constant is so large that it is unsigned [enabled by default] printf("%llu", modExp(3, (189 + 50 * 223), 15383399235709406497)); ^ findxA.c:34:5: warning: this decimal constant is unsigned only in ISO C90 [enabled by default] printf("%llu", modExp(3, (189 + 50 * 223), 15383399235709406497)); ^ 给出的输出进行了比较。该Haskell表达式的计算结果为不同的十进制3^(189 + 50 * 223) `mod` 15383399235709406497。我认为这是我的C函数错了,因为我相信Haskell在处理如此大的小数方面做得更好。

如何改善C函数modExp?

编辑:我尝试使用the first answer of this question.选项,但是由于我试图处理unsigned long long int的小数,因此我将每个输入和返回类型都从int切换了为unsigned long long int。这导致了细分错误。

Edit2 :我以错误的方式使用了上面链接中描述的功能。它不会产生细分错误;但它仍无法输出正确的小数。

2 个答案:

答案 0 :(得分:1)

要减少溢出的机会,可以依靠unsigned long long int modExp(unsigned long long int base, unsigned long long int expo, unsigned long long int mod) { unsigned long long int baseMod = base%mod; unsigned long long int out = 1; unsigned long long int cnt; for (cnt=expo; cnt>0; cnt--) { out = ( (out%mod) * baseMod) % mod; } return out; }

例如(未试用):

x ** 5 == 1*(x**4) * 0*(x**2) * 1*(x**1)

还请注意,幂运算可以作为“平方积”更有效地完成。例如,5 == 1*4 + 0*2 + 1*1是因为5 == 101b(或unsigned long long int modExp(unsigned long long int base, unsigned long long int expo, unsigned long long int mod) { unsigned long long int out = 1; unsigned long long int temp = base%mod; while(exp0 > 0) { if( (exp0 & 1) != 0) { out = (out * temp) % mod; } temp = (temp*temp) % mod; exp0 >>= 1; } return out; } )。

例如(未试用):

modExp()

对于大指数,这应该会在性能上产生巨大差异(例如,如果指数为123456,则该循环将具有17次迭代,而不是123456次迭代)。

也;如果这是用于某种加密技术(并非不可能);那么您应该清楚地说明这一点,因为您可能希望使用“恒定时间”(以减少计时副通道的机会-例如,从执行unsigned long long所需的时间中推断出有关指数的信息)。

最后;即使进行了更改,对于mod * mod <= ULLONG_MAX来说,最大数量为15,383,399,235,709,406,497的数字可能仍然太大(您需要确保typedef struct { uint32_t digit[4]; } MY_NUMBER_TYPE);这意味着您可能需要使用/实现“大整数”运算(例如,Table 1 id details 1 abc 2 def 3 xyz Table 2 id details 1 rst 1 uvw 东西可以处理128位整数,并具有乘法和模运算功能)。

答案 1 :(得分:-1)

这将永远不会这样!

仅在这里乘法的结果

out = (out * base) % mod;

可能需要比基础数据类型的64位更多的位。而且,如果发生整数溢出(即最高有效位被切除),则mod操作结果是错误的!

使用较大的数据类型,或使用其他方法:-)

顺便说一句,顺便说一句,请使用以下测试代码来查看输入中实际上发生了两次整数溢出:

#include <stdio.h>

unsigned long long int modExp(unsigned long long int base, unsigned long long int expo, unsigned long long int mod)
{
    unsigned long long int out = 1;
    while(expo>0)
    {
        if(expo % 2 == 1)
            out = (out * base) % mod;
        expo = expo >> 1;
        if (base * base < base) printf("WARNING! INTEGER OVERFLOW!!!!\n");
        base = (base * base) % mod;
    }
    return out;
}

int main()
{
    printf("%llu", modExp(3, (189 + 50 * 223), 15383399235709406497));
    return 0;
}