【问题标题】:How did Python implement the built-in function pow()?Python是如何实现内置函数pow()的?
【发布时间】:2011-03-09 14:01:18
【问题描述】:

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

【问题讨论】:

  • cpython 源代码库位于hg.python.org/cpython
  • ...在 Objects/longobject.c:long_pow() 下(正如 JimB 已经评论的那样)。

标签: python algorithm math


【解决方案1】:

如果abc是整数,则可以通过binary exponentiation和在每个步骤中减少模c来提高实现效率,包括第一个步骤(即减少ac 在你开始之前)。这确实是the implementation of long_pow() 所做的。该函数有两百多行代码,因为它必须处理引用计数,并且它处理负指数和一大堆特殊情况。

不过,算法的核心思想相当简单。假设我们要计算正整数aba ** b,而b 具有二进制数字b_i。那么我们可以把b写成

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

此产品中的每个因子的格式为(a**2**i)**b_i。如果b_i 为零,我们可以简单地省略该因子。如果b_i 为1,则因子等于a**2**i,并且可以通过重复对a 求平方来计算所有i 的这些幂。总的来说,我们需要将k 乘以平方和乘以,其中kb 的二进制位数。

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

【讨论】:

  • 为什么我们可以在每一步中减少模c?
  • @BenSandler:因为 aa' (mod c) 和 bb' (mod c) 隐含 aba'b' (mod c i>),或者换句话说,你是否先减少 abc 然后将它们相乘,或者先将它们相乘,然后对 c 取模。见the Wikipedia article on modular arithmetic
  • 请注意,long_pow 现在在该文件的另一行定义:github.com/python/cpython/blob/master/Objects/…
  • @JohanC 我已经更新了链接以包含提交哈希,所以它不再过时了。
【解决方案2】:

您可以考虑使用以下两种实现来快速计算(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;
}

【讨论】:

  • @Noctis,我尝试运行您的 Python 实现并得到了这个:TypeError: ufunc 'bitwise_and' not supported for the input types,并且输入无法根据强制转换安全地强制转换为任何支持的类型rule ''safe'' ---- 当我现在正在学习 Python 时,我想你可能对这个错误有所了解(搜索表明它可能是一个错误,但我认为有一个快速的解决方法)
  • @stackuser:在以下演示中它似乎工作正常:ideone.com/sYzqZN
  • 谁能解释为什么这个解决方案有效?我无法理解这个算法背后的逻辑。
  • @NoctisSkytower,考虑到原生 python pow() 内置函数也支持这一点并且看起来更快,这样做有什么好处? &gt;&gt;&gt; st_pow = 'pow(65537L, 767587L, 14971787L) &gt;&gt;&gt; st_pow_mod = 'pow_mod(65537L, 767587L, 14971787L)' &gt;&gt;&gt; timeit.timeit(st_pow) 4.510787010192871 &gt;&gt;&gt; timeit.timeit(st_pow_mod, def_pow_mod) 10.135776996612549
  • @Fabiano 我的函数不应该被使用。这只是对 Python 如何在幕后工作的解释,而没有提及它在 C 中的源代码。我试图回答 wong2 的关于 pow 是如何实现的问题。
【解决方案3】:

我不知道python,但是如果你需要快速的幂,你可以使用平方乘幂:

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

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

【讨论】:

    【解决方案4】:

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

    Python 对于密集的数字运算可能会很慢,但Psyco 可以为您提供相当快的速度提升,但它不如调用标准库的 C 代码那么好。

    【讨论】:

    • math.pow() 没有模参数,并且与内置 pow() 的功能不同。另外仅供参考,Psyco 变得非常陈旧,并且没有 64 位支持。 NumPy 非常适合严肃的数学。
    【解决方案5】:

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

    【讨论】:

      【解决方案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
      

      查看此link 以获得解释

      【讨论】:

        猜你喜欢
        • 2023-02-16
        • 2021-10-05
        • 2011-08-06
        • 1970-01-01
        • 2010-09-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多