【问题标题】:How to work on big integers that don't fit into any of language's data structures如何处理不适合任何语言数据结构的大整数
【发布时间】:2011-04-04 21:00:54
【问题描述】:

我正在尝试解决编程竞赛的初步问题,其中 2 个问题我必须计算并打印一些非常大的整数(如 100!、2^100)。

我还需要一种快速的方法来计算这个大整数的幂。

你能给我一些算法或数据结构的建议吗?(顺便说一句,我阅读了 C 接口和实现的“任意精度算术”部分,但它对 pow() 没有帮助)

编辑:我认为通过平方方法和位移的幂运算可以提高功率,但我还需要一种快速的方法来计算这个整数的阶乘。谢谢。

EDIT2:对于那些感兴趣的人;

找到包含长度为 N 的所有位串的最短位串长度(对不起我的英语,我会举个例子)。 N

例如,包含所有长度为 2(00, 01, 10, 11) 的位串的最短位串长度为 5(11001)。

我对这个问题的解决方案是 2^n + n - 1。(所以我应该计算 2 的幂,我想我会使用位移)

另一个问题是,给定 2 个长度,找出有多少种不同的方法可以达到长度 N。例如,输入是 10、2、3。那么你应该用 2 和 3 达到 10(例如, 2+2+2+2+2, 2+2+3+3, 3+2+2+3, 3+3+2+2...)。 1

我的解决方案是 2x + 3y = N,所以 x = (N - 3y) / 2 。对于从 0 到 2*N / 3 的 y,如果 x 是整数,那么我应该计算这个 X 和 Y 的广义排列,总计 += (x+y)! / (x!*y!)。

【问题讨论】:

  • 什么是最大参数(100 个或更多?)以及计算答案需要多长时间?
  • 问题不同,但我必须计算 2^10000 和 100!解决。时间限制为 1 秒,内存限制为 256mb。如果你有兴趣,我可以翻译这个问题。可能有另一种解决方案,但它写在问题文本中,答案大于 64 位。
  • 约束非常小。因此,您可以通过平方进行求幂,如下所述,并找到阶乘,然后乘以 1、2、...、100。如果您以数字方式存储数字,那么您需要的只是将长数乘以 int和两个数字的乘法。您可以在我的答案中看到代码。

标签: c algorithm data-structures biginteger arbitrary-precision


【解决方案1】:

对于带有整数的powexponentiation by squaring

【讨论】:

  • 对于 2^100,带进位的左移是更明智的选择。打印结果是困难的部分......
  • 基数二肯定是个特例!
  • @larsmans 你能给我一些关于以 2 为底和左移方法计算幂的例子或链接吗?
  • 左移 1 表示乘以 2。左移 n 表示乘以 2^n。 2^100 只是一个 uint64_t 设置了第 36 位,后跟一个为 0 的 uint64_t
  • @larsmans,谢谢,我不知道uint64_t,但我认为通过平方方法进行移位或取幂会起作用..
【解决方案2】:

要计算幂,请使用二分法算法,该算法使用指数的二进制表示并减少结果乘法次数。 数据结构只是一个整数数组

【讨论】:

  • 这是一个编程竞赛,所以我必须编写我的解决方案(我不能使用 gmplib)。二分算法是什么意思?我查看了维基百科,发现了二分法搜索。
  • 在下一个答案中,Doug Currie 给出了链接 en.wikipedia.org/wiki/Exponentiation_by_squaring 和该算法的专有名称
【解决方案3】:

你可能想看看加密程序的实现(尤其是我首先想到的 GnuPG)。原因是加密函数也使用非常大的整数(所谓的 MultiPrecision Integers - MPI)。这些 MPI 的存储方式是,前 2 个字节说明整数的大小,后面的字节说明如何存储值。

GPG 是开源的,看看就知道了 :)

【讨论】:

  • 谢谢,我认为像 GPG 这样的库对我来说太先进了,但我会给它一个机会。
【解决方案4】:

使用GMP 处理这些问题。它内置了阶乘支持和强大的功能等。它具有 C 和 C++ 接口等。您将需要 mpz_t 作为包含非常大整数的类型。

【讨论】:

    【解决方案5】:

    对于 C 语言,this 之类的东西可以使用,或者使用 int 或 char 数组自行滚动,数组中的一个点代表一个数字。 [1 | 0 | 1]['1'|'0'|'1'] 用于 101 等。

    【讨论】:

      【解决方案6】:

      您可以按以下格式存储数字:该数字的位数和数字数组。这是编程竞赛中处理大量数字的常用方法。

      这是一个提供数字和乘法存储的类。可以添加数字的输入和输出,这是微不足道的。

      class huge {
      public:
          int size;
          int data[1000];
      
          friend void mul(const huge &a, int k, huge &c) {
              c.size = a.size;
              int r = 0;
              for (int i = 0; i < a.size; i++) {
                  r += a.data[i] * k;
                  c.data[i] = r % 10;
                  r = r / 10;
              }
              if (r > 0) {
                  c.size++;
                  c.data[c.size - 1] = r;
              }
              while (c.size > 1 && c.data[c.size - 1] == 0)
                  c.size--;
          }
      
          friend void mul(const huge &a, const huge &b, huge &c) {
              c.size = a.size + b.size;
              memset(c.data, 0, c.size * sizeof(c.data[0]));
              for (int i = 0; i < a.size; i++) {
                  int r = 0;
                  for (int j = 0; j < b.size; j++) {
                      r += a.data[i] * b.data[j] + c.data[i + j];
                      c.data[i + j] = r % 10;
                      r /= 10;
                  }
                  if (r > 0)
                      c.data[i + b.size] = r;
              }
              while (c.size > 1 && c.data[c.size - 1] == 0)
                  c.size--;
          }
      };
      

      【讨论】:

      • +1,但您确实意识到“这个数字的位数和位数的数组”。不仅仅是为了编程竞赛……这也是 GMP 存储数字的方式。
      • 当然,这是一种普遍的方法。但如果我没记错的话,GMP 将数字存储在 2 的幂的基数中。因此,内存使用率和速度得到了提高,但十进制格式的输入和输出变得复杂。在这里,使用以 10 为底,这使得输出非常简单。
      【解决方案7】:

      基础数学可以做任何双精度与双精度的乘法...

      def biginteger(num1, num2):
      result = []
      lnum1 = len(num1)
      lnum2 = len(num2)
      
      k = x = remainder = 0
      while len(result) < lnum1:
          result.append(0)
      for i in range(lnum1, 0, -1):
          multiplier = int(num1[i - 1])
          for j in range(lnum2, 0, -1):
              temp = (int(num2[j - 1]) * multiplier) + remainder + int(result[k])
              result[k] = str(temp % 10)
              remainder = temp / 10
              k += 1
          result.append(str(remainder))
          if remainder != 0:
              remainder = 0
          x += 1
          k = x
      
      return ''.join([result[i - 1] for i in range(len(result), 0, -1)])
      
      num1 = '37234234234234'
      num2 = '43234234234232'
      print biginteger(num1, num2)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-14
        • 2010-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-19
        相关资源
        最近更新 更多