【问题标题】:How did Python implement the built-in function pow()?Python 是如何实现内置函数 pow() 的?
【发布时间】:2023-02-16 22:12:57
【问题描述】:

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

【问题讨论】:

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

标签: python algorithm math


【解决方案1】:

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

不过,该算法的核心思想相当简单。假设我们要为正整数ab计算a ** b,而b有二进制数字b_i。然后我们可以写b

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

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'(模式C) 和bb'(模式C) 暗示aba'b'(模式C), 或者换句话说, 你是否先减少并不重要Ab取模C然后乘以它们,或者先乘以然后减模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
", pow_mod(63437, 3935969939, 20628));
    return 0;
}

【讨论】:

  • @Noctis,我尝试运行你的 Python 实现并得到这个:TypeError: ufunc 'bitwise_and' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting 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以获取解释

      【讨论】:

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