有效地生成斯特恩的双原子序列

时间:2016-12-19 18:48:01

标签: python algorithm performance python-2.7

斯特恩的双原子序列可以更详细地阅读over here;但是,为了我的目的,我现在就定义它。

斯特恩双原子序列的定义

n成为一个数字来生成fusc函数。表示fusc(n)

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

如果n为偶数,则返回值为fusc(n / 2) 如果n为奇数,则返回值为fusc((n - 1) / 2) + fusc((n + 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)

但是,我的代码必须能够处理大小为 1000s 数百万位的数字,并且递归运行函数千元数百万次似乎不太高效或实用。

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

2 个答案:

答案 0 :(得分:7)

对于一百万位的记忆,递归堆栈将非常大。我们可以先尝试查看一个足够大的数字,我们可以手工处理,fusc(71)在这种情况下:

  

fusc(71)= fusc(35)+ fusc(36)

     
    

fusc(35)= fusc(17)+ fusc(18)
    fusc(36)= fusc(18)

  
     

fusc(71)= 1 * fusc(17)+ 2 * fusc(18)

     
    

fusc(17)= fusc(8)+ fusc(9)
    fusc(18)= fusc(9)

  
     

fusc(71)= 1 * fusc(8)+ 3 * fusc(9)

     
    

fusc(8)= fusc(4)
    fusc(9)= fusc(4)+ fusc(5)

  
     

fusc(71)= 4 * fusc(4)+ 3 * fusc(5)

     
    

fusc(4)= fusc(2)
    fusc(3)= fusc(1)+ fusc(2)

  
     

fusc(71)= 7 * fusc(2)+ 3 * fusc(3)

     
    

fusc(2)= fusc(1)
    fusc(3)= fusc(1)+ fusc(2)

  
     

fusc(71)= 11 * fusc(1)+ 3 * fusc(2)

     
    

fusc(2)= fusc(1)

  
     

fusc(71)= 14 * fusc(1)= 14

我们意识到在这种情况下我们可以完全避免递归,因为我们总是可以在fusc(n)形式中表达a * fusc(m) + b * fusc(m+1),同时将m的值减小为0.从上面的示例中,您可以找到以下模式:

  

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

因此,您可以使用简单的循环函数来解决O(lg(n))时间内的问题

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

答案 1 :(得分:3)

在您的情况下,

lru_cache可以创造奇迹。确保maxsize是2的幂。可能需要为您的应用程序调整一下这个大小。 cache_info()会对此有所帮助。

也可以使用//代替/进行整数除法。

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())

是的,这只是Filip Malczak提出的meomization。

你可以在while循环中使用位操作获得额外的 tiny 加速:

while not n & 1:  # as long as the lowest bit is not 1
    n >>= 1       # shift n right by one

<强>更新

这是一种“手工”进行meomzation的简单方法:

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

<强>更新

阅读short article by dijkstra himself次要更新后。

文章指出,如果f(n) = f(m)的第一位和最后一位与m的第一位和第一位相同,并且其间的位被反转,则为n。我的想法是让n尽可能小。

这是位掩码(1<<n.bit_length()-1)-2的用途(第一位和最后一位是0;中间位于1; xor n的位是m如上所述给出def fusc_ed(n, _mem={}): if n <= 1: return n while not n & 1: n >>= 1 if n == 1: return 1 # https://www.cs.utexas.edu/users/EWD/transcriptions/EWD05xx/EWD578.html # bit invert the middle bits and check if this is smaller than n m = n ^ (1<<n.bit_length()-1)-2 n = m if m < n else n if n in _mem: return _mem[n] ret = fusc(n >> 1) + fusc((n >> 1) + 1) _mem[n] = ret return ret

我只能做小基准测试;我很感兴趣,如果这对你输入的大小有任何帮助......这将减少缓存的内存,并希望带来一些加速。

import sys
sys.setrecursionlimit(10000)  # default limit was 1000

我不得不增加递归限制:

_mem
基准测试给出了奇怪的结果;使用下面的代码,并确保我总是启动一个新的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

这些是我得到的三个随机结果:

data= ['Mom','Dad',1,'Dog','World']

for d in data:
    print("You are my {}".format("'{}'".format(d) if isinstance(d, str) else d))

这就是我停止的地方......