【问题标题】:Is there a way to have a fair implementation of Brian Kernighan's algorithm for counting ones in Python?有没有办法公平地实现 Brian Kernighan 的算法来计算 Python 中的数?
【发布时间】:2018-09-23 13:19:49
【问题描述】:

与在字符串中计数的内置函数相比,Brian Kernighan 的算法在 python 中计数的性能存在巨大差异,这让我感到惊讶。

在我看来,转换为字符串然后计数是个坏主意。

现在看起来一个坏主意是在寻找性能时循环而不使用内置函数。

import random

x = random.randint(0,1<<1000000)

def count_ones(number):
    c = 0
    while(number !=0 ):
        number = number&(number-1)
        c = c + 1
    return c


%timeit bin(x).count("1")
%timeit count_ones(x)


5.09 ms ± 20 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
25 s ± 544 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

【问题讨论】:

  • 我可能很想这样做:sum(x &amp; (1 &lt;&lt; n) &gt; 0 for n in range(x.bit_length()))....不确定性能如何...
  • 在 CPython 中,内置函数在机器代码中,而想要运行 Python 需要运行整个解释器,因此使用内置函数通常比实现自定义算法更快。即使如果编译成机器代码它们会更快。在 PyPy 上运行时它们可能会更快。
  • 如果你有 Python 3.6+,def bincount_f(n): return f'{n:b}'.count("1")bincount_fmt 稍快,但仍然比 bin(n).count("1") 慢。
  • 或者sum(1 for n in range(x.bit_length()) if x &amp; (1 &lt;&lt; n)) 更明智一些...
  • 但这还不是全部。您的算法实际上具有关于数字表示大小的二次时间,因此它对于 bigints 仍然无效。

标签: python algorithm performance loops built-in


【解决方案1】:

Kernighan 的算法最适用于适合 ALU 的数据,在现代硬件上通常是 64 位。对于更长的数字,算法在数字的长度上变成二次方,因为每次迭代都会对数字的整个长度进行计算。算法可以手动优化,因为很明显,一旦借位停止传播,按位与不会改变任何内容。

即使进行了优化,我们仍然处于 Shlemiel the Painter 领域;计算是二次的,因为每次迭代时有效地扫描总是从同一个地方开始,每次都扫描得越来越远。

无论如何,即使是复杂的优化器发现优化和 Python 的 bignum 实现都没有复杂的优化器,这将是非常令人惊讶的。例如,它不会将递减与按位与融合。

Bitwise 和 on bignums 显然可以就地完成,所以写起来很诱人:

number &= number - 1

希望执行就地操作。但这对 CPython 没有任何影响; CPython 的 bignums 是不可变的,因此不可能进行就地突变。

简而言之,Python 将为每次迭代创建两个新的百万位数。需要一段时间并不奇怪。相反,令人惊讶的是它只需要 25 秒。

根据版本和平台,CPython bignum 操作以 15 位或 30 位单元执行。 (对适合 30 位的整数的计算略微优化。但是 64 位远远超出了该范围,因此不要期望将数字限制为 64 位以避免 bignum 开销。)假设您的平台使用 30 位单元,运行预期 50 万次迭代的算法意味着执行超过 660 亿个单字计算(对 (1000000/30 = 33334) 个字大数进行减法和按位计算),以及一百万个 130KB 内存分配。在 25 秒内做到这一点一点也不寒碜。它证明了现代 CPU 的速度有多快,并警告人们在数字变得非常大之前很容易注意到您正在使用二次算法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 2015-06-06
    • 2012-05-22
    • 2018-02-24
    • 1970-01-01
    相关资源
    最近更新 更多