Python 是如何实现内置函数 pow() 的?

How did Python implement the built-in function pow()?

提问人:wong2 提问时间:3/9/2011 最后编辑:wong2 更新时间:11/20/2023 访问量:42283

问:

我必须编写一个程序来计算 where 和 都是非常大的数字。如果我只是使用 ,它真的很慢。然后我发现内置函数可以通过调用 非常快地做到这一点。
我很想知道Python是如何实现这一点的?或者在哪里可以找到实现此功能的源代码文件?
a**b % cbca**b % cpow()pow(a, b, c)

Python 算法 数学

评论

4赞 Wooble 3/9/2011
cpython 源码库处于 hg.python.org/cpython
2赞 smci 7/20/2011
...在 Objects/longobject.c:long_pow() 下(正如 JimB 已经评论的那样)。

答:

-1赞 cmaynard 3/9/2011 #1

Python 将 C 数学库用于一般情况,并将自己的逻辑用于某些概念(例如无穷大)。

-1赞 Andrew Wilkinson 3/9/2011 #2

该文件的第 1426 行显示了实现 math.pow 的 Python 代码,但基本上它归结为它调用标准 C 库,该库可能具有该函数的高度优化版本。

Python 对于密集的数字运算来说可能很慢,但 Psyco 可以给你带来相当大的速度提升,但它不如调用标准库的 C 代码好。

评论

7赞 JimB 3/9/2011
math.pow()没有模参数,并且与内置函数不同。仅供参考,Psyco 变得非常陈旧,并且不支持 64 位。NumPy 非常适合严肃的数学。pow()
0赞 Jonno_FTW 3/9/2011 #3

我不了解python,但如果你需要快速的幂,你可以通过平方来使用幂:

http://en.wikipedia.org/wiki/Exponentiation_by_squaring

这是一种简单的递归方法,它使用指数的交换特性。

49赞 Sven Marnach 3/9/2011 #4

如果 和 是整数,则可以通过二进制幂和每个步骤中的约简模来提高实现效率,包括第一步(即在开始之前约简模)。这就是 long_pow() 的实现所做的。该函数有两百多行代码,因为它必须处理引用计数,并且它处理负指数和一大堆特殊情况。abccac

不过,从本质上讲,该算法的想法相当简单。假设我们要计算正整数 和 ,并且有二进制数字。然后我们可以写成a ** babbb_ib

b = b_0 + b1 * 2 + b2 * 2**2 + ... + b_k ** 2**k

ans 作为a ** b

a ** b = a**b0 * (a**2)**b1 * (a**2**2)**b2 * ... * (a**2**k)**b_k

本产品中的每个因素都是 的形式。如果为零,我们可以简单地省略该因子。如果为 1,则因数等于 ,并且可以通过重复平方来计算所有这些幂。总的来说,我们需要平方和乘以乘法,其中 是 的二进制位数。(a**2**i)**b_ib_ib_ia**2**iiakkb

如上所述,我们可以在每一步中减少模,无论是在平方之后还是在乘法之后。pow(a, b, c)c

评论

1赞 Ben Sandler 8/31/2015
为什么我们可以在每一步中减少模 c?
2赞 Sven Marnach 8/31/2015
@BenSandler:因为 a ≡ a' (mod c) 和 b ≡ b' (mod c) 意味着 ab ≡ a'b' (mod c),或者换句话说,你是先减少 a b 模 c 然后乘以它们,还是先将它们相乘再约化 c,这并不重要。请参阅维基百科上关于模块化算术的文章
0赞 JohanC 12/9/2019
请注意,现在在该文件的另一行中定义:github.com/python/cpython/blob/master/Objects/...long_pow
1赞 Sven Marnach 12/9/2019
@JohanC我已经更新了链接以包含提交哈希,因此它不再过时。
39赞 Noctis Skytower 5/11/2012 #5

您可以考虑以下两种实现来快速计算。(x ** y) % z

在 Python 中:

def pow_mod(x, y, z):
    "Calculate (x ** y) % z efficiently."
    number = 1
    while y:
        if y & 1:
            number = number * x % z
        y >>= 1
        x = x * x % z
    return number

在 C 语言中:

#include <stdio.h>

unsigned long pow_mod(unsigned short x, unsigned long y, unsigned short z)
{
    unsigned long number = 1;
    while (y)
    {
        if (y & 1)
            number = number * x % z;
        y >>= 1;
        x = (unsigned long)x * x % z;
    }
    return number;
}

int main()
{
    printf("%d\n", pow_mod(63437, 3935969939, 20628));
    return 0;
}

评论

0赞 stackuser 5/7/2013
@Noctis,我尝试运行您的 Python 实现并得到这个:TypeError: ufunc 'bitwise_and' not supported for the input types, and the inputs cannot be safely forced to any supported types according to the cast rule ''safe'' ---- 由于我现在正在学习 Python,我想您可能对此错误有所了解(搜索表明它可能是一个错误,但我认为有一个快速的解决方法)
0赞 Noctis Skytower 5/7/2013
@stackuser:在以下演示中,它似乎工作正常:ideone.com/sYzqZN
5赞 kilojoules 5/24/2015
谁能解释一下为什么这个解决方案有效?我无法理解此算法背后的逻辑。
2赞 Fabiano 6/8/2016
@NoctisSkytower,考虑到原生 Python 内置函数也支持这一点并且看起来更快,这样做有什么好处?pow()>>> st_pow = 'pow(65537L, 767587L, 14971787L) >>> st_pow_mod = 'pow_mod(65537L, 767587L, 14971787L)' >>> timeit.timeit(st_pow) 4.510787010192871 >>> timeit.timeit(st_pow_mod, def_pow_mod) 10.135776996612549
7赞 Noctis Skytower 6/8/2016
@Fabiano 我的函数不应该被使用。它只是对 Python 如何在幕后工作的解释,而不参考其在 C 中的源代码。我试图回答 wong2 关于如何实现的问题。pow
-1赞 RookRaven 7/12/2018 #6

在 Python 中实现 pow(x,n)

def myPow(x, n):
        p = 1
        if n<0:
            x = 1/x
            n = abs(n)

        # Exponentiation by Squaring

        while n:
            if n%2:
                p*= x
            x*=x
            n//=2
        return p

在 Python 中实现 pow(x,n,m)

def myPow(x,n,m):
            p = 1
            if n<0:
                x = 1/x
                n = abs(n)
            while n:
                if n%2:
                    p*= x%m
                x*=x%m
                n//=2
            return p

查看此链接以获取说明

-3赞 RARE Kpop Manifesto 11/18/2023 #7

只想添加一个关于Python 3.11.6内置的数据点:pow(-mod-)

我列出了 12 个相当大的素数,跨越大约 9,154 个 ASCII 字节,每个十进制数字 1 个字节(已经排除了 s),并组成了 1,320 种组合\n

 a ** b % c   =>  pow(a, b, c) 

 python3 -c 'import sys
 
 sys.set_int_max_str_digits(8**9 - 1)

 [ print(__.strip(), str(pow(int((_ := __.split())[0]),
                             int(_[1]),
                             int(_[2])))) for __ in sys.stdin ]' 

并以 GNU GMP(过度 gawk)为基准,我用一个准系统拍了拍:powmod()

function powmod(__, ___, ____, _,
          _____, ______, _______) {
    
    if (_ = !+____)
        return "NAN_POWMOD_ZERO"

    ___ %= (____ += _++) - (_____ = \
            (__  %= ____)^(______ = !++_))

    while (_+_ <= ___)
        ___%_ && (___ = --___/_) || ______ * (___ /= _) \
      ? \
        (_____ = _____*__%____) < (__ = __*__%____) \
                                :  __ = __*__%____
    return \
        ___<_ ? _____*__ % ____ : \
        _<___ ?     __*__ % ____*__*_____%____ : \
               __*_____*__ % ____
}

通过散列确认它们的输出是相同的。

   out9:  112KiB 0:00:00 [ 468KiB/s] [ 347KiB/s] [<=> ]
 lines9:   1.32k 0:00:10 [ 121 /s] [ <=> ]
   out9: 3.85MiB 0:00:10 [ 362KiB/s] [ 362KiB/s] [ <=> ]
 
  ( echo "${aaaaaaaaaaaaaaaaaaa}" | mawk 'NF = / is prime$/' | mawk2  FS='\n' |)  

 10.79s user 0.07s system 99% cpu 10.873 total

 1  2eed768a72e116b4b50afa74afc53067  stdin

gmp-via-awk (英语) |蟒蛇 3.11.6


 lines9:   1.32k 0:02:27 [8.95 /s] [<=> ]
   out9: 3.85MiB 0:02:27 [26.7KiB/s] [26.7KiB/s] [  <=> ]

 ( echo "${aaaaaaaaaaaaaaaaaaa}" | mawk 'NF = / is prime$/' | mawk2  FS='\n' |)

 146.46s user 0.74s system 99% cpu 2:27.55 total

 1  2eed768a72e116b4b50afa74afc53067  stdin

由于我不清楚的原因,Python 3 在其他地方花费了不到 11 秒的时间完成一项任务,大约需要 2 分 27 秒。我第二次运行它,它仍然是 10.6 秒,而不是 2 分 24.7 秒。

评论

2赞 possum 11/20/2023
这并不能提供问题的答案。要批评或要求作者澄清,请在其帖子下方发表评论。 - 来自评论
1赞 Chris 11/20/2023
请不要滥用反引号来强调。它们不是用于产品名称或任何您认为重要的随机事物。它们仅适用于内联代码和文件名
0赞 RARE Kpop Manifesto 11/26/2023
@Chris:当然可以。您现在能否解决我的实际发现,即为什么会出现如此荒谬的差异?当它偏离整个数量级时,这有点令人担忧。
0赞 Chris 11/26/2023
当它作为答案而不是问题发布时,则不然。您已经在这里待了足够长的时间,知道该网站是如何运作的,但如果您需要复习一下,请随时重新访问这次旅行
0赞 RARE Kpop Manifesto 12/1/2023
@Chris : 系统不允许我发布我应该做什么的问题
0赞 Chris 12/1/2023
我是普通用户,不是版主。我对您的帐户和发布历史记录(尤其是可能已删除的内容)的了解比您少得多。不过,您可能想从这里开始