滑动窗口和记忆的计算

时间:2012-05-20 12:54:27

标签: python primes memoization

我正在研究Project Euler Problem 50,其中指出:

  

素数41,可以写成六个连续素数的总和:

     

41 = 2 + 3 + 5 + 7 + 11 + 13   这是连续素数的最长和,它增加到低于一百的素数。

     

连续素数低于一千的最长和,加上素数,包含21个项,等于953。

     

哪个素数低于一百万,可以写成最连续素数的总和?

为了确定素数 P 中的项(如果它可以写成素数之和)我使用所有素数的滑动窗口(按递增顺序)到(但不是包括) P ,并计算所有这些窗口的总和,如果总和等于考虑的素数,我计算窗口的长度......

这对于 1000 的所有素数都适用,但对于 10 ** 6 的素数,它非常慢,所以我希望 memozation < / strong>会有所帮助;在计算滑动窗口的总和时,进行了大量的双重工作......(对吗?)

所以我在网上找到了标准的memoizaton实现,并将其粘贴在我的代码中,这是正确的吗? (我不知道它应该如何在这里工作......)

primes = tuple(n for n in range(1, 10**6) if is_prime(n)==True)

count_best = 0


##http://docs.python.org/release/2.3.5/lib/itertools-example.html:
## Slightly modified (first for loop)
from itertools import islice
    def window(seq):
    for n in range(2, len(seq) + 1):

        it = iter(seq)
        result = tuple(islice(it, n))
        if len(result) == n:
            yield result    
        for elem in it:
            result = result[1:] + (elem,)
            yield result   

def memoize(function):
    cache = {}
    def decorated_function(*args):
        if args in cache:
            return cache[args]
        else:
            val = function(*args)
            cache[args] = val
            return val
    return decorated_function


@memoize 


def find_lin_comb(prime):
    global count_best

    for windows in window(primes[0 : primes.index(prime)]):
        if sum(windows) == prime and len(windows) > count_best:
            count_best = len(windows)
            print('Prime: ', prime, 'Terms: ', count_best)


##Find them:
for x in primes[::-1]: find_lin_comb(x)

(顺便说一句,素数的元组是“正常”快速生成的)

所有的输入都很受欢迎,我只是一个爱好程序员,所以请不要对我有所帮助。

谢谢!

编辑:这是一个没有破坏缩进的工作代码粘贴: http://pastebin.com/R1NpMqgb

2 个答案:

答案 0 :(得分:3)

  

这适用于1000以下的所有素数,但对于高达10 ** 6的素数来说它非常慢,所以我希望备忘录会有所帮助;在计算滑动窗口的总和时,进行了大量的双重工作......(对吗?)

是的,没错。当然,素数达到10 6 的速度很慢。

假设您n素数最多为N,按递增顺序编号p_1 = 2, p_2 = 3, ...。在考虑是否素数没有。对于k[p_i, ..., p_j](i,j)是连续素数的总和,您考虑所有窗口i < j < k。其中有(k-1)*(k-2)/2个。通过所有kn,您可以检查总共n³/6个窗口(计算多重性,您总共w(i.j)次检查n-j次。即使忽略了创建窗口并对其进行求和的成本,您也可以看到它的扩展程度如何:

  • 对于N = 1000,有n = 168素数和约790000个窗口需要检查(计算多重性)。
  • 对于N = 10**6,有n = 78498个素数和约8.3*10**13个窗口需要检查。

现在考虑创建和汇总窗口的工作,估算j-i+1的低j-i+1总和w(i,j)中的p_k素数,k³/6的工作是关于k**4/24,总工作量大致为N = 1000。对1.6*10**18花生而言,有3300万步,而N = 1000000几乎为3.1*10**7

一年包含大约n³/6秒,具有~3GHz的CPU,大约10 17 时钟周期。所以我们谈论的是需要100个CPU年的操作(可能是10个左右的因素)。

我想,你不愿意等那么久;)

现在,通过记忆,您仍然会多次查看每个窗口,但是您只对每个窗口进行一次计算。这意味着你需要n³/6工作来计算窗口,并在任何窗口查看8.3*10**13次。

  • 问题1:你仍然需要关注8.3*10**13次的窗口,即使只花费一个周期也需要几个小时。
  • 问题2:大约有{{1}}个窗口要记忆。你没有那么多内存,除非你可以使用真正的大型高清。

您可以通过丢弃不再需要的数据来规避内存问题,并且只在需要时计算窗口的数据,但是一旦您知道可能丢弃的数据,您应该能够看到更好的方法。

  

连续素数低于一千的最长和,加上素数,包含21个项,等于953。

这告诉你关于产生这笔金额的窗口是什么?哪里可以开始,哪里可以停止?如何使用该信息创建一个有效的算法来解决问题?

答案 1 :(得分:1)

memoize装饰器为函数添加一个包装器,以缓存参数的每个值的返回值(多个参数的每个值组合)。当使用相同的参数多次调用函数时,它很有用。您只能使用纯函数,即

  1. 该功能没有副作用。更改全局变量和输出是副作用的示例。
  2. 返回值仅取决于参数的值,而不取决于可能在调用之间更改值的某些全局变量。
  3. 您的find_lin_comb函数不符合上述条件。首先,每次都使用不同的参数调用它,另一方面,函数不返回值。