【问题标题】:What is the simplest way of implementing bigint in C?在 C 中实现 bigint 的最简单方法是什么?
【发布时间】:2011-03-21 09:50:14
【问题描述】:

我正在计算 100! (即 100 的阶乘)。

我正在寻找使用 C 来完成此任务的最简单方法。我已经阅读过但没有找到具体的答案。

如果你必须知道,我在 Mac os X 中用 Xcode 编程。

【问题讨论】:

  • 你去吧:933[100 位删除]00000 :-)
  • @Anders:这个网站是Stack溢出,不是评论溢出! :-O
  • 大声笑,我知道我可以从 wolframalpha 中获得价值。但是,我正在学习 C 并且想知道如何做到这一点。

标签: c bigint


【解决方案1】:

如果您正在寻找一个简单的库,libtommath(来自 libtomcrypt)可能是您想要的。

如果您希望自己编写一个简单的实现(作为学习练习,或者因为您只需要非常有限的 bigint 功能子集,并且不想依赖于大型库,命名空间污染,等),那么我可能会针对您的问题提出以下建议:

由于您可以根据n 限制结果的大小,因此只需预先分配一个具有所需大小的uint32_t 数组来保存结果。我猜你会想要打印结果,所以使用10的幂(即1000000000)而不是2的幂是有意义的。也就是说,数组的每个元素都可以包含 0 到 999999999 之间的值。

要将此数字乘以(正常、非大)整数 n,请执行以下操作:

uint32_t carry=0;
for(i=0; i<len; i++) {
    uint64_t tmp = n*(uint64_t)big[i] + carry;
    big[i] = tmp % 1000000000;
    carry = tmp / 1000000000;
}
if (carry) big[len++] = carry;

如果您知道n 永远不会大于 100(或其他一些小数字)并且想要避免进入 64 位范围(或者如果您在 64 位平台上并且想要使用 @ 987654326@ 用于您的 bigint 数组),然后将基数设为 10 的较小幂,以便乘法结果始终适合类型。

现在,打印结果就像这样:

printf("%lu", (long)big[len-1]);
for(i=len-1; i; i--) printf("%.9lu", (long)big[i-1]);
putchar('\n');

如果你想使用 2 的幂作为底数,而不是 10 的幂,那么乘法会变得更快:

uint32_t carry=0;
for(i=0; i<len; i++) {
    uint64_t tmp = n*(uint64_t)big[i] + carry;
    big[i] = tmp;
    carry = tmp >> 32;
}
if (carry) big[len++] = carry;

但是,以十进制打印结果并不那么令人愉快... :-) 当然,如果您想要十六进制的结果,那很容易:

printf("%lx", (long)big[len-1]);
for(i=len-1; i; i--) printf("%.8lx", (long)big[i-1]);
putchar('\n');

希望这会有所帮助!我将把实现其他东西(如加法、2 个 bigint 的乘法等)作为练习留给你。回想一下您在小学时如何学习以 10 为底的加法、乘法、除法等,并教计算机如何做到这一点(但改为以 10 ^ 9 或 2 ^ 32 为底),您应该没问题。

【讨论】:

  • 对 32 位目标上的 64 位类型的评论:在任何健全的硬件上,32x32 乘法固有地生成 64 位结果,因此转换为大于系统字长。如果存在,编译器将生成正确的 32x32->64 乘法指令。并且至少在 i386 上,64/32->32 除法(高 32 位小于除数时)是 32 位除法指令的固有特性,不需要编译器在软件中进行仿真。不过,在智能程度较低的 32 位目标上,您可能希望使用 16 位单元......
  • 执行库调用而不是在每个实例上内联整个操作,根据我的经验,编译器总是在 32 位平台上使用 64 位内置操作执行此操作,这可能会增加代码大小;尽管内联操作很可能会更快。这是我在 32 位嵌入式 ARM 平台上多次遇到的问题。
【解决方案2】:

如果您愿意使用库实现,标准的实现似乎是GMP

mpz_t out;
mpz_init(out);
mpz_fac_ui(out,100);
mpz_out_str(stdout,10,out);

应该算100!通过查看文档。

【讨论】:

    【解决方案3】:

    您要求最简单的方法来做到这一点。所以,给你:

    #include <gmp.h>
    #include <stdio.h>
    
    int main(int argc, char** argv) {
        mpz_t mynum;
        mpz_init(mynum);
        mpz_add_ui(mynum, 100);
        int i;
        for (i = 99; i > 1; i--) {
            mpz_mul_si(mynum, mynum, (long)i);
        }
        mpz_out_str(stdout, 10, mynum);
        return 0;
    }
    

    我测试了这段代码,它给出了正确的答案。

    【讨论】:

    • 斯科特的回答中有一个。
    【解决方案4】:

    您也可以使用OpenSSL bn;它已经安装在 Mac OS X 中。

    【讨论】:

      猜你喜欢
      • 2023-03-20
      • 2018-02-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-07
      • 1970-01-01
      • 2010-11-23
      相关资源
      最近更新 更多