【问题标题】:Getting the position of 1-bits in a python Long object在 python Long 对象中获取 1 位的位置
【发布时间】:2018-09-10 13:11:52
【问题描述】:

假设我有一个非常大的 python 整数,在 python 2.7 中(但如果需要,我不介意切换到 python 3)。

比说的更大,2^100000。

找到二进制序列中所有 1 的位置的最快方法是什么? (例如:24 将是 11000 ---> = [4,5](或 [5,4].. 我不在乎顺序)

目前我正在使用:

sum = whatever_starting_number

while 1:
    val = sum.bit_length()-1
    sum -= 2**val
    mylist.append(val)
    if sum == 0:
        break

这没关系,但它几乎不比只取 log2 并反复减去它快。我真正想做的只是看位,跳过零,记录1的位置,甚至不需要修改原始值。

edit:得到多个答案,非常感谢。我将在几个 timeit 测试中实现它们,并将在明天更新结果。

【问题讨论】:

  • 你不应该使用 sum 作为变量名,因为它是 Python 中内置函数的名称

标签: python binary bit-manipulation bitwise-operators


【解决方案1】:

可能不是最快的解决方案,但相当简单并且似乎足够快(2^1M 是即时的)。

bits = []
for i, c in enumerate(bin(2**1000000)[:1:-1], 1):
    if c == '1':
        bits.append(i)

以防[:1:-1] 不清楚,它被称为“扩展切片”,更多信息在这里:https://docs.python.org/2/whatsnew/2.3.html#extended-slices

编辑:由于我不太同意@Voo 对此案的评论和否决票,因此我决定对答案中发布的 3 个解决方案进行计时,结果如下:

import timeit


def fcn1():
    sum = 3**100000
    one_bit_indexes = []
    index = 0
    while sum: # returns true if sum is non-zero
        if sum & 1: # returns true if right-most bit is 1
            one_bit_indexes.append(index)
        sum >>= 1 # discard the right-most bit
        index += 1
    return one_bit_indexes


def fcn2():
    number = 3**100000
    bits = []
    for i, c in enumerate(bin(number)[:1:-1], 1):
        if c == '1':
            bits.append(i)
    return bits


def fcn3():
    sum = 3**100000
    return [i for i in range(sum.bit_length()) if sum & (1<<i)]


print(timeit.timeit(fcn1, number=1))
print(timeit.timeit(fcn2, number=1))
print(timeit.timeit(fcn3, number=1))

对于 3^100k:
fcn1: 0.7462488659657538
fcn2: 0.02108444197801873
fcn3: 0.40482770901871845

对于 3^1M:
fcn1: 70.9139410170028
fcn2: 0.20711017202120274
fcn3: 43.36111917096423

【讨论】:

  • 如果您致电enumerate(),您可以取消您的柜​​台:for i,c in enumerate(str(bin(24))[:1:-1], 1):
  • bin 已经生成字符串时,调用str 毫无意义。
  • 一般来说,如果您使用字符串操作来处理数字,您可以将“可能不是最快的解决方案”替换为“保证内存和性能方面效率最低的解决方案”。
  • 很难说,但我的猜测是其他两种解决方案都需要在不同的内存之间重复移动/创建大量数字(我认为 sum >>= 1 将创建一个新数字),而这个解决方案保持大数字不变,不创建任何新的大数字,只适用于小数字(索引)。但实际上这只是猜测。 |不管怎样,现在找到了:stackoverflow.com/questions/9829578/… |而且似乎在纯 Python 中你不能做得更好。在 C 语言中,情况就不同了。
  • 你是对的。字符串解决方案的性能确实是python中最好的,即使在玩to_bytes和co时也是如此。似乎 cpython 没有利用 inplace 运算符,因此一遍又一遍地分配相同的数字,这对性能有很大影响。赞成。
【解决方案2】:

也许这已经够快了:

mylist = [i for i in range(sum.bit_length()) if sum & (1<<i)]

【讨论】:

    【解决方案3】:

    您可以使用位运算符。

    one_bit_indexes = []
    index = 0
    while sum: # returns true if sum is non-zero
        if sum & 1: # returns true if right-most bit is 1
            one_bit_indexes.append(index)
        sum >>= 1 # discard the right-most bit
        index += 1
    

    尚未对此进行测试,但很确定它会起作用。按位运算速度很快,因此这也应该比计算和减去 2 的幂更有效。(除非您的 Python 解释器已经在做一些聪明的事情,例如转换代码以用按位运算替换 2 的幂)。

    编辑:要使其适用于负数,您必须首先取“sum”的绝对值。

    【讨论】:

    • 哦,非常好。这基本上是我自己想出的,但使用 & 和 1 是我缺少的聪明部分。
    猜你喜欢
    • 2012-03-20
    • 1970-01-01
    • 1970-01-01
    • 2022-12-01
    • 2015-06-17
    • 1970-01-01
    • 1970-01-01
    • 2016-11-09
    • 2016-02-26
    相关资源
    最近更新 更多