Python:如何有效地计算;1“;在1到n个数的二进制表示中是什么?

Python:如何有效地计算;1“;在1到n个数的二进制表示中是什么?,python,algorithm,Python,Algorithm,例如,对于输入5,输出应为7。 (仓位(1)=1,仓位(2)=10…仓位(5)=101)-->1+1+2+1+2=7 这是我尝试过的,但考虑到我对每个整数迭代一次循环,这不是一个非常有效的算法。我的代码(Python 3): 谢谢大家! 以下是一个基于OEIS的递归关系的解决方案: def onecount(n): if n == 0: return 0 if n % 2 == 0: m = n/2 return onecount(

例如,对于输入5,输出应为7。 (仓位(1)=1,仓位(2)=10…仓位(5)=101)-->1+1+2+1+2=7

这是我尝试过的,但考虑到我对每个整数迭代一次循环,这不是一个非常有效的算法。我的代码(Python 3):


谢谢大家!

以下是一个基于OEIS的递归关系的解决方案:

def onecount(n):
    if n == 0:
        return 0
    if n % 2 == 0:
        m = n/2
        return onecount(m) + onecount(m-1) + m
    m = (n-1)/2
    return 2*onecount(m)+m+1

>>> [onecount(i) for i in range(30)]
[0, 1, 2, 4, 5, 7, 9, 12, 13, 15, 17, 20, 22, 25, 28, 32, 33, 35, 37, 40, 42, 45, 48, 52, 54, 57, 60, 64, 67, 71]

以下是基于OEIS的递归关系的解决方案:

def onecount(n):
    if n == 0:
        return 0
    if n % 2 == 0:
        m = n/2
        return onecount(m) + onecount(m-1) + m
    m = (n-1)/2
    return 2*onecount(m)+m+1

>>> [onecount(i) for i in range(30)]
[0, 1, 2, 4, 5, 7, 9, 12, 13, 15, 17, 20, 22, 25, 28, 32, 33, 35, 37, 40, 42, 45, 48, 52, 54, 57, 60, 64, 67, 71]
由于等人的原因,它的性能似乎更好,至少在我的Win10机器上是这样

from time import time
import gmpy2

def onecount(n):
    if n == 0:
        return 0
    if n % 2 == 0:
        m = n/2
        return onecount(m) + onecount(m-1) + m
    m = (n-1)/2
    return 2*onecount(m)+m+1

N = 10000

initial = time()
for _ in range(N):
    for i in range(30):
        onecount(i)
print (time()-initial)

initial = time()
for _ in range(N):
    total = 0
    for i in range(30):
        total+=gmpy2.popcount(i)
print (time()-initial)
以下是输出:

1.7816979885101318
0.07404899597167969
如果您想要一个列表,并且正在使用>Py3.2:

>>> from itertools import accumulate
>>> result = list(accumulate([gmpy2.popcount(_) for _ in range(30)]))
>>> result
[0, 1, 2, 4, 5, 7, 9, 12, 13, 15, 17, 20, 22, 25, 28, 32, 33, 35, 37, 40, 42, 45, 48, 52, 54, 57, 60, 64, 67, 71]
由于等人的原因,它的性能似乎更好,至少在我的Win10机器上是这样

from time import time
import gmpy2

def onecount(n):
    if n == 0:
        return 0
    if n % 2 == 0:
        m = n/2
        return onecount(m) + onecount(m-1) + m
    m = (n-1)/2
    return 2*onecount(m)+m+1

N = 10000

initial = time()
for _ in range(N):
    for i in range(30):
        onecount(i)
print (time()-initial)

initial = time()
for _ in range(N):
    total = 0
    for i in range(30):
        total+=gmpy2.popcount(i)
print (time()-initial)
以下是输出:

1.7816979885101318
0.07404899597167969
如果您想要一个列表,并且正在使用>Py3.2:

>>> from itertools import accumulate
>>> result = list(accumulate([gmpy2.popcount(_) for _ in range(30)]))
>>> result
[0, 1, 2, 4, 5, 7, 9, 12, 13, 15, 17, 20, 22, 25, 28, 32, 33, 35, 37, 40, 42, 45, 48, 52, 54, 57, 60, 64, 67, 71]


请检查这个问题:因为每个数字的计数操作是独立的,所以您可以使用线程级并行来加速您的工作。对于n的值,1到n的二进制表示中的数字是序列A000788-这里有一些可能有用的公式:@Ahmad此循环的核心是CPU限制的,GIL将阻塞线程。此外,为了跟进@ChaiT.Rex,
a(2n)
表示偶数,
a(2n+1)的公式
表示奇数的公式。请检查这个问题:因为每个数字的计数操作是独立的,所以可以使用线程级并行来加速工作。对于n值,1到n的二进制表示中的数字是序列A000788-这里有些可能有用的公式:@Ahmad the core这个循环中的任何一个都是CPU受限的,GIL将阻塞线程。此外,要使用@ChaiT.Rex进行后续操作,
a(2n)
表示偶数的公式,
a(2n+1)
表示奇数的公式。删除
n==1
检查可能更有效,因为大多数输入都必须检查失败,这会减慢大多数输入(以及大多数递归调用)的速度。另外,像
if n&1==0
m=n>>1
(在
if
前面处理这两种情况)这样的按位操作可能会稍微加快速度。是的,没错。我刚才发现了n==1。首先编写的是可读性。为什么要对
m
执行计算?例如
n/2
(n-1)/2
?缓存以前计算的结果会有好处吗?@wwii备忘录在某些情况下很有用,但我不想在这一点上超越基本python。您可以随意编写一个memoize decorator,看看它的效果如何。删除
n==1
检查可能会更有效,因为大多数输入都必须失败地进行检查,从而减慢大多数输入(以及大多数递归调用)的速度。另外,像
if n&1==0
m=n>>1
(在
if
前面处理这两种情况)这样的按位操作可能会稍微加快速度。是的,没错。我刚才发现了n==1。首先编写的是可读性。为什么要对
m
执行计算?例如
n/2
(n-1)/2
?缓存以前计算的结果会有好处吗?@wwii备忘录在某些情况下很有用,但我不想在这一点上超越基本python。请随意写一个备忘录装饰器,看看它的效果如何。