【问题标题】:Python's pow() function and fast exponentiation gives different answersPython 的 pow() 函数和快速求幂给出了不同的答案
【发布时间】:2021-06-04 02:54:41
【问题描述】:

对于以下 C 和 python 中的两种实现,我得到了不同的答案。 在 Python 中

print(pow(15, 47413144071, 94826288143))

打印 1

但在 C 中

#include<stdio.h>
unsigned long Power(unsigned long A, unsigned long B, unsigned long X)
{
    unsigned long res = 1;
    while( B > 0 )
    {
        if( B & 1UL )
        {
            res = ( res * A ) % X;
        }
        A = ( A * A ) % X;
        B >>= 1UL;
    }
    return res;
}
int main()
{
    printf("%lu", Power(15, 47413144071, 94826288143));
    return 0;
}

打印: 893231448 任何帮助表示赞赏。

【问题讨论】:

  • 一个unsigned long 只保证能够表示04294967295 范围内的值(虽然标准允许 表示更大的范围,但它是不需要)。您传递的值 BC 都超过了这个值。如果您的实现提供 32 位 unsigned long,则传递的值将以 4294967296 为模减少。
  • 我这样做了,但它仍然提供如上所述的输出。输出:893231448
  • 47413144071 -- 你为什么假设 Python 在这里使用 unsigned long?由于您将其标记为 C++,因此您可以使用 uint64_t 类型。
  • 也使用了 uint64_t。还是不行。
  • C 没有任意精度的整数。您必须自己实现,或者使用外部库,例如 gmp

标签: python c performance pow exponentiation


【解决方案1】:

如 cmets unsigned long 中所述,仅保证高达 4294967295 的值,并且您的 B 和 X 更大,否则您将得到值 mod 2^32 mod X 而不是值 mod X。对于可以替换的输入unsigned long long

但是,您不仅需要 B 和 X 可以表示为 unsigned long long,还需要 res*AA*A,并且由于 A 可以与 X-1 一样大,因此无法使用unsigned long long 在 C 中处理这个问题。

【讨论】:

    【解决方案2】:

    这是一个证明,即使使用 uint64_t 类型,您的 C 代码也会产生溢出。

    我刚刚将 C 代码转换为 Python,添加了溢出测试。第一个不能用uint64_t 表示的值是 2**64 或 0x10000000000000000 或 18446744073709551616。所以我在 mod 操作之前测试了所有产品:

    def Power(A, B, X):
        res = 1
        step = 1
        while (B > 0):
            if B & 1:
                if (res * A) >= 0x1000000000000000:
                    print("uint64_t overflow at res step", step)
                res = (res * A) % X
            if (A * A) >= 0x1000000000000000:
                print("uint64_t overflow at A step", step)
            A = (A * A) % X
            B >>= 1
            step += 1
        return res
    
    >>> Power(15, 47413144071, 94826288143)
    uint64_t overflow at A step 4
    uint64_t overflow at A step 5
    uint64_t overflow at A step 6
    uint64_t overflow at A step 7
    uint64_t overflow at A step 8
    uint64_t overflow at A step 9
    uint64_t overflow at res step 10
    uint64_t overflow at A step 10
    uint64_t overflow at A step 11
    uint64_t overflow at res step 12
    uint64_t overflow at A step 12
    uint64_t overflow at A step 13
    uint64_t overflow at res step 14
    uint64_t overflow at A step 14
    uint64_t overflow at A step 15
    uint64_t overflow at A step 16
    uint64_t overflow at res step 17
    uint64_t overflow at A step 17
    uint64_t overflow at res step 18
    uint64_t overflow at A step 18
    uint64_t overflow at A step 19
    uint64_t overflow at res step 20
    uint64_t overflow at A step 20
    uint64_t overflow at A step 21
    uint64_t overflow at A step 22
    uint64_t overflow at A step 23
    uint64_t overflow at A step 24
    uint64_t overflow at A step 25
    uint64_t overflow at res step 26
    uint64_t overflow at A step 26
    uint64_t overflow at A step 27
    uint64_t overflow at res step 28
    uint64_t overflow at A step 28
    uint64_t overflow at A step 29
    uint64_t overflow at A step 30
    uint64_t overflow at A step 31
    uint64_t overflow at A step 32
    uint64_t overflow at res step 33
    uint64_t overflow at A step 33
    uint64_t overflow at res step 34
    uint64_t overflow at A step 34
    uint64_t overflow at A step 35
    uint64_t overflow at res step 36
    uint64_t overflow at A step 36
    1
    

    这绝对证明了算法是正确的(我们最终得到1),而且如果您尝试使用 64 位整数,也会出现许多溢出。


    长话短说,如果您需要在 C 中实现它,您需要一个具有 uint128_t 类型的系统,或者使用像 gmplib 这样的多精度库。

    【讨论】:

    • 那我该怎么办?
    猜你喜欢
    • 2020-10-03
    • 1970-01-01
    • 2015-11-14
    • 2017-01-25
    • 1970-01-01
    • 2016-12-29
    • 1970-01-01
    • 2019-09-19
    • 1970-01-01
    相关资源
    最近更新 更多