【问题标题】:Is there a space efficient way to compute the gcd of high exponents?是否有一种空间有效的方法来计算高指数的 gcd?
【发布时间】:2021-11-09 14:54:36
【问题描述】:

基本上,我正在编写一个程序,它处理溢出 cpp 整数的大整数值。我正在尝试计算类似:gdc(pow(a, b), c) 其中a ^ b 是溢出整数限制的值。有没有办法在我不必依赖大整数库的情况下做到这一点?如果没有,有没有推荐的大整数库?

【问题讨论】:

  • 您需要C++Python 的解决方案吗?您可以进行模幂运算gcd(powmod(a, b, c), c),它不需要大整数。如果你告诉我使用什么语言,我会为你写一个简单的小程序。

标签: biginteger integer-overflow exponentiation


【解决方案1】:

我们可以使用greatest common divisor 的属性,即gcd(a, b) = gcd(a % b, b)。因此gcd(pow(a, b), c) = gcd(pow(a, b) % c, c) = gcd(powmod(a, b, c), c),其中powmod()modular exponentiation

在我下面的 C++ 代码中,PowMod() 是使用 exponentiation by squaring 方法实现的。

Try it online!

#include <cstdint>
#include <iostream>

using Word = uint32_t;
using DWord = uint64_t;

Word GCD(Word a, Word b) {
    Word t = 0;
    while (b != 0) {
        t = b;
        b = a % b;
        a = t;
    }
    return a;
}

Word PowMod(Word a, Word b, Word c) {
    Word r = 1;
    while (b != 0) {
        if (b & 1)
            r = (DWord(r) * a) % c;
        a = (DWord(a) * a) % c;
        b >>= 1;
    }
    return r;
}

int main() {
    Word const
        a = 2645680092U, b = 3562429202U, c = 3045001828U,
        powmod = PowMod(a, b, c), gcd = GCD(powmod, c);
    std::cout << "a = " << a << ", b = " << b
        << ", c = " << c << std::endl;
    std::cout << "PowMod(a, b, c) = "
        << powmod << std::endl; // 592284924
    std::cout << "GCD(PowMod(a, b, c), c) = "
        << gcd << std::endl; // 1892
}

输出:

a = 2645680092, b = 3562429202, c = 3045001828
PowMod(a, b, c) = 592284924
GCD(PowMod(a, b, c), c) = 1892

给出正确的结果,可以通过以下给出相同结果的简单 Python 程序进行验证:

Try it online!

import random, math
random.seed(0)
bits = 32
while True:
    c = random.randrange(1 << (bits - 1), 1 << bits)
    a = random.randrange(1 << (bits - 1), 1 << bits) % c
    b = random.randrange(1 << (bits - 1), 1 << bits)
    pm = pow(a, b, c)
    gcd = math.gcd(pm, c)
    if gcd >= 1000:
        print('a =', a, ', b =', b, ', c =', c,
            ', powmod =', pm, ', gcd =', gcd)
        break

输出:

a = 2645680092 , b = 3562429202 , c = 3045001828 ,
    powmod = 592284924 , gcd = 1892

如果您有 GCC/CLang 编译器,您可以通过更改以下代码行将 Word 设为 64 位,将 DWord 设为 128 位:

using Word = uint64_t;
using DWord = unsigned __int128;

我的代码支持 32 位输入,但在此更改之后,您可以拥有 64 位输入。


第 2 部分。使用大整数算法库GMP

如果由于某种原因您有较大的输入整数,那么您可以使用出色的库 GMP 进行大型算术(它支持整数、有理数、浮点数)。

这个库有所有mathematical operations,包括modular exponentiation(PowMod)和一些number theoretical函数(包括GCD)。此外,这个库非常受欢迎且经过高度优化。

在下面的代码中,我执行与上面代码相​​同的操作,但仅使用 GMP 的功能。作为一个例子,我使用 512 位整数来表明它可以接受大输入(它甚至可以接受数百万位数):

Try it online!

#include <iostream>
#include <cstdlib>
#include <gmpxx.h>

int main() {
    mpz_class const
        a("1953143455988359840868749111326065201169739169335107410565117106311318704164104986194255770982854472823807334163384557922525376038346976291413843761504166", 10),
        b("5126002245539530470958611905297854592859344951467500786493685495603638740444446597426402800257519403404965463713689509774040138494219032682986554069941558", 10),
        c("4396071968291195248321035664209400217968667450140674696924686844534284953565382985421958604880273584922294910355449271193696338132720472184903935323837626", 10);
    
    mpz_class powmod, gcd;
    // PowMod
    mpz_powm(powmod.get_mpz_t(), a.get_mpz_t(), b.get_mpz_t(), c.get_mpz_t()); // 1632164707041502536171492944083090257113212090861915134477312917063125646194834706890409016008321666479437224930114914370387958138698748075752168351835856
    // GCD
    mpz_gcd(gcd.get_mpz_t(), powmod.get_mpz_t(), c.get_mpz_t()); // 51842
    // Output
    std::cout << "PowMod = " << powmod.get_str() << std::endl
              << "GCD = " << gcd.get_str() << std::endl;
}

输出:

PowMod = 1632164707041502536171492944083090257113212090861915134477312917063125646194834706890409016008321666479437224930114914370387958138698748075752168351835856
GCD = 51842

要在 Linux 下使用 GMP 库,只需安装 sudo apt install libgmp-dev 并编译 clang++ -std=c++11 -O2 -lgmp -o main main.cpp

在 Windows 下使用 GMP 有点棘手。一种方法是自己构建MPIR 库,它是 GMP 的 Windows 友好克隆。另一种方法是安装MSYS 并使用我在其他答案中写的following these instructions 中的预构建GMP。

【讨论】:

  • @patrickbies 请看一下我上面答案的第 2 部分,我现在才发布。在那里,我写了如何使用 GMP 大整数库来实现与 GCD(powmod(a, b, c), c) 相同的目的,就像我在答案的第 1 部分中对小整数所做的那样。仅当您有大输入时才应使用第 2 部分,即如果 abc 都很大,例如 1024 位。单击第 2 部分中的 Try it online! 链接以查看此库的运行情况。
猜你喜欢
  • 1970-01-01
  • 2022-07-01
  • 2018-01-27
  • 2020-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多