Python 有效生成Stern双原子序列

Python 有效生成Stern双原子序列,python,algorithm,performance,python-2.7,Python,Algorithm,Performance,Python 2.7,斯特恩的双原子序列可以更详细地阅读;然而,出于我的目的,我现在将定义它 斯特恩双原子序列的定义 设n为生成fusc函数的数字。表示为fuscn 如果n为0,则返回值为0。 如果n为1,则返回值为1 如果n为偶数,则返回值为fuscn/2。 如果n为奇数,则返回值为fuscn-1/2+fuscn+1/2 目前,我的Python代码在生成过程中大部分时间都是强制执行的,除了除以两部分之外,因为它始终不会产生任何更改 def fusc (n): if n <= 1: re

斯特恩的双原子序列可以更详细地阅读;然而,出于我的目的,我现在将定义它

斯特恩双原子序列的定义 设n为生成fusc函数的数字。表示为fuscn

如果n为0,则返回值为0。 如果n为1,则返回值为1

如果n为偶数,则返回值为fuscn/2。 如果n为奇数,则返回值为fuscn-1/2+fuscn+1/2

目前,我的Python代码在生成过程中大部分时间都是强制执行的,除了除以两部分之外,因为它始终不会产生任何更改

def fusc (n):
    if n <= 1:
        return n

    while n > 2 and n % 2 == 0:
        n /= 2

    return fusc((n - 1) / 2) + fusc((n + 1) / 2)
然而,我的代码必须能够处理1000百万位数的数字,并且递归地在函数中运行数千次似乎不是非常有效或实用

有没有什么方法可以在算法上改进我的代码,使大量的数字可以通过,而不必递归调用函数这么多次?

在您的例子中效果非常好。确保maxsize是2的幂。您可能需要在应用程序中稍微调整该大小。缓存信息将对此有所帮助

对于整数除法也使用//而不是/

from functools import lru_cache


@lru_cache(maxsize=512, typed=False)
def fusc(n):

    if n <= 1:
        return n

    while n > 2 and n % 2 == 0:
        n //= 2

    return fusc((n - 1) // 2) + fusc((n + 1) // 2)

print(fusc(1000000000078093254329870980000043298))
print(fusc.cache_info())
更新:

这里有一个简单的“手工”实现Meomization的方法:

def fusc(n, _mem={}):  # _mem will be the cache of the values 
                       # that have been calculated before
    if n in _mem:      # if we know that one: just return the value
        return _mem[n]

    if n <= 1:
        return n

    while not n & 1:
        n >>= 1
    if n == 1:
        return 1    

    ret = fusc((n - 1) // 2) + fusc((n + 1) // 2)
    _mem[n] = ret  # store the value for next time
    return ret
我必须增加递归限制:

import sys
sys.setrecursionlimit(10000)  # default limit was 1000
基准测试产生了奇怪的结果;使用下面的代码,并确保我总是启动一个新的interperter,并且有一个空的内存,我有时会得到更好的运行时;在其他情况下,新代码速度较慢

基准代码:

print(n.bit_length())

ti = timeit('fusc(n)', setup='from __main__ import fusc, n', number=1)
print(ti)

ti = timeit('fusc_ed(n)', setup='from __main__ import fusc_ed, n', number=1)
print(ti)
这是我得到的三个随机结果:

6959
24.117448464001427
0.013900151001507766

6989
23.92404893300045
0.013844672999766772

7038
24.33894686200074
24.685758719999285
这就是我停下来的地方…

对你的情况很有帮助。确保maxsize是2的幂。您可能需要在应用程序中稍微调整该大小。缓存信息将对此有所帮助

对于整数除法也使用//而不是/

from functools import lru_cache


@lru_cache(maxsize=512, typed=False)
def fusc(n):

    if n <= 1:
        return n

    while n > 2 and n % 2 == 0:
        n //= 2

    return fusc((n - 1) // 2) + fusc((n + 1) // 2)

print(fusc(1000000000078093254329870980000043298))
print(fusc.cache_info())
更新:

这里有一个简单的“手工”实现Meomization的方法:

def fusc(n, _mem={}):  # _mem will be the cache of the values 
                       # that have been calculated before
    if n in _mem:      # if we know that one: just return the value
        return _mem[n]

    if n <= 1:
        return n

    while not n & 1:
        n >>= 1
    if n == 1:
        return 1    

    ret = fusc((n - 1) // 2) + fusc((n + 1) // 2)
    _mem[n] = ret  # store the value for next time
    return ret
我必须增加递归限制:

import sys
sys.setrecursionlimit(10000)  # default limit was 1000
基准测试产生了奇怪的结果;使用下面的代码,并确保我总是启动一个新的interperter,并且有一个空的内存,我有时会得到更好的运行时;在其他情况下,新代码速度较慢

基准代码:

print(n.bit_length())

ti = timeit('fusc(n)', setup='from __main__ import fusc, n', number=1)
print(ti)

ti = timeit('fusc_ed(n)', setup='from __main__ import fusc_ed, n', number=1)
print(ti)
这是我得到的三个随机结果:

6959
24.117448464001427
0.013900151001507766

6989
23.92404893300045
0.013844672999766772

7038
24.33894686200074
24.685758719999285

这就是我停止的地方…

对于一百万位的记忆,递归堆栈将非常大。我们可以首先尝试查看一个足够大的数字,我们可以手动操作,在本例中为fusc71:

fusc71=fusc35+fusc36

fusc35=fusc17+fusc18 fusc36=fusc18

fusc71=1*fusc17+2*fusc18

fusc17=fusc8+fusc9 fusc18=fusc9

fusc71=1*fusc8+3*fusc9

fusc8=fusc4 fusc9=fusc4+fusc5

fusc71=4*fusc4+3*fusc5

fusc4=fusc2 fusc3=fusc1+fusc2

fusc71=7*fusc2+3*fusc3

fusc2=fusc1 fusc3=fusc1+fusc2

fusc71=11*fusc1+3*fusc2

fusc2=fusc1

fusc71=14*fusc1=14

我们意识到,在这种情况下,我们可以完全避免递归,因为我们可以始终以a*fuscm+b*fuscm+1的形式表示fuscn,同时将m的值减少到0。从上面的示例中,您可能会发现以下模式:

如果m是奇数: a*fuscm+b*fuscm+1=a*fuscm-1/2+b+a*fuscm+1/2 如果m是偶数: a*fuscm+b*fuscm+1=a+b*fuscm/2+b*fuscm/2+1

因此,您可以使用一个简单的循环函数在Olgn时间内解决该问题

def fusc(n):
  if n == 0: return 0
  a = 1
  b = 0
  while n > 0:
    if n%2:
      b = b + a
      n = (n-1)/2
    else:
      a = a + b
      n = n/2
  return b

对于一百万位的记忆,递归堆栈将非常大。我们可以首先尝试查看一个足够大的数字,我们可以手动操作,在本例中为fusc71:

fusc71=fusc35+fusc36

fusc35=fusc17+fusc18 fusc36=fusc18

fusc71=1*fusc17+2*fusc18

fusc17=fusc8+fusc9 fusc18=fusc9

fusc71=1*fusc8+3*fusc9

fusc8=fusc4 fusc9=fusc4+fusc5

fusc71=4*fusc4+3*fusc5

fusc4=fusc2 fusc3=fusc1+fusc2

fusc71=7*fusc2+3*fusc3

fusc2=fusc1 fusc3=fusc1+fusc2

fusc71=11*fusc1+3*fusc2

fusc2=fusc1

fusc71=14*fusc1=14

我们意识到,在这种情况下,我们可以完全避免递归,因为我们可以始终以a*fuscm+b*fuscm+1的形式表示fuscn,同时将m的值减少到0。从上面的示例中,您可能会发现以下模式:

如果m是奇数: a*fuscm+b*fuscm+1=a*fuscm-1/2+b+a*fuscm+1/2 如果m是偶数: a*fuscm+b*fuscm+1=a+b*fuscm/2+b*fuscm/2+1

因此,您可以使用一个简单的循环函数在Olgn时间内解决该问题

def fusc(n):
  if n == 0: return 0
  a = 1
  b = 0
  while n > 0:
    if n%2:
      b = b + a
      n = (n-1)/2
    else:
      a = a + b
      n = n/2
  return b

阅读关于记忆:它可以用python出色地完成,但现在我不确定它是否是一个好的解决方案,因为你不会计算太多次相同的事情…记忆可能会帮助稍微下降到更小的数字,但我需要能够c
快速计算大量数据,虽然实现这样的功能肯定会提高长期性能,但这并不是我真正想要的。这就是为什么它变成了一个评论而不是答案。祝你好运。阅读关于记忆化的文章:用python可以很好地完成这项工作,但现在我不确定这是否是一个好的解决方案,因为你不会多次计算同一件事……记忆化可能会稍微有帮助,但我需要能够快速计算出大量的数据,虽然实现这样的东西肯定会提高长期性能,但这并不是我真正想要的。这就是为什么它成为一个评论而不是答案。祝你好运。不幸的是,我正在使用Python2.7,它的lru_缓存不可用。我应该在标签中添加Python2.7。可能会有帮助。。。或者你也可以使用自己的。也不能使用任何外部库。我认为解决方案需要几乎完全的算法。添加了一个没有使用lru缓存的meomized版本。请在放弃缓存值的想法之前尝试一下。加速是巨大的!我认为这应该是公认的答案。另一个答案中的代码不包含递归,显然是Olog n,并且非常简单。我应该在标签中添加Python2.7。可能会有帮助。。。或者你也可以使用自己的。也不能使用任何外部库。我认为解决方案需要几乎完全的算法。添加了一个没有使用lru缓存的meomized版本。请在放弃缓存值的想法之前尝试一下。加速是巨大的!我认为这应该是公认的答案。另一个答案中的代码不包含递归,显然是Olog n,并且非常简单。