3

我完全被这个问题所困扰,所以我正在寻求任何帮助。

我想每个人都知道基本的 GCD 计算算法,比如二进制或欧几里得 GCD。实现这样的方法来计算两个单精度数是没有问题的。实际上,这只是大约几笔。

我需要为多精度数字(超过 10^5 位)实现此方法(用 C 语言)。有几个可用的 GNU 库(GNU MP、MPFR、MPIR),它们可以定义多精度数字并对它们进行操作。它看起来像一个多精度数字存储在内存中,作为一对单精度部分,也就是“肢体”。

实现了一些方法来查找 gcd(a, b),但实际上它们很难满足我的需要。仅当 a 和 b 正好包含两个肢体时才使用 GCD 计算的二进制方法。当 min(a,b) 包含多于(即 630 个)肢体等时使用的 HGCD 方法。我发现很难弄清楚,如何扩展这些方法中的任何一个以用于任何长度的 a 和 b。我还发现不同版本的 GNU 库包含不同版本和方法的 GCD 算法。

问题:我想知道是否有可能使二进制 GCD 算法与“肢体”方面的任何长度的多精度整数一起工作,如果可能的话 - 获得任何帮助或想法如何在 C 中实现它。有没有人有任何想法或代码部分如何实现它?

我想考虑任何建议或任何其他解决方案来解决该问题。

如果有人愿意看一下,这是(a = b = 2 个肢体)的 GNU MP 二进制 GCD 方法的一部分:

/* Use binary algorithm to compute G <-- GCD (U, V) for usize, vsize == 2.
   Both U and V must be odd. */
static inline mp_size_t
gcd_2 (mp_ptr gp, mp_srcptr up, mp_srcptr vp)
{
  printf("gcd_2 invoked\n");
  mp_limb_t u0, u1, v0, v1;
  mp_size_t gn;

  u0 = up[0];
  u1 = up[1];
  v0 = vp[0];
  v1 = vp[1];

  ASSERT (u0 & 1);
  ASSERT (v0 & 1);

  /* Check for u0 != v0 needed to ensure that argument to
   * count_trailing_zeros is non-zero. */
  while (u1 != v1 && u0 != v0)
    {
      unsigned long int r;
      if (u1 > v1)
  {
    sub_ddmmss (u1, u0, u1, u0, v1, v0);
    count_trailing_zeros (r, u0);
    u0 = ((u1 << (GMP_NUMB_BITS - r)) & GMP_NUMB_MASK) | (u0 >> r);
    u1 >>= r;
  }
      else  /* u1 < v1.  */
  {
    sub_ddmmss (v1, v0, v1, v0, u1, u0);
    count_trailing_zeros (r, v0);
    v0 = ((v1 << (GMP_NUMB_BITS - r)) & GMP_NUMB_MASK) | (v0 >> r);
    v1 >>= r;
  }
    }

  gp[0] = u0, gp[1] = u1, gn = 1 + (u1 != 0);

  /* If U == V == GCD, done.  Otherwise, compute GCD (V, |U - V|).  */
  if (u1 == v1 && u0 == v0)
    return gn;

  v0 = (u0 == v0) ? ((u1 > v1) ? u1-v1 : v1-u1) : ((u0 > v0) ? u0-v0 : v0-u0);
  gp[0] = mpn_gcd_1 (gp, gn, v0);

  return 1;
}

以上代码粘贴

4

1 回答 1

3

只需为这个问题专门编写你自己的代码,为什么不呢?(10^9)^2适合 64 位 int,因此您可以使用base - (10^9)digits,每个都保存在 64 位 int 中。要表示2^(10^5)-bits 值,2^(10^5) ~= 10^30103即具有 ~ 30103 个十进制数字的值,对于所涉及的两个数字中的每一个,您只需要 30103/9 ~= 3350ints ,它是内存中的一个 ~ 27 kB 数组。

根据 WP,对于二进制 GCD 算法,您只需要minus并且/2通过将每个数字减半来实现它是微不足道的,偶尔进位5*10^8到较低的数字(94 / 2 = 47 = {4,5+2})。最终乘以2 k可以使用简单算法完成,因为它只需要执行一次。

在 base- 中打印10将是微不足道的。如果您不关心最终结果的打印,那么您将不需要最终乘法(或者如果您将结果报告为2^k*x),然后您可以使用10^18基数,将使用的位数减半.

您只需要通常的 C 整数算术来处理数字。

于 2013-02-23T21:19:26.683 回答