【问题标题】:Calculations on sliding windows and memoization滑动窗口和记忆的计算
【发布时间】:2012-05-27 06:20:51
【问题描述】:

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

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

41 = 2 + 3 + 5 + 7 + 11 + 13 这是与小于 100 的素数相加的最长的连续素数之和。

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

100 万以下的哪个素数可以写成最连续素数之和?

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

这对 1000 以内的所有素数都有效,但对于 10**6 以内的素数来说它非常慢,所以我希望 memozation 会有所帮助;在计算滑动窗口的总和时,做了很多双重工作......(对吗?)

所以我在网上找到了标准的 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

【问题讨论】:

  • 对不起,它有效,由于 Stackoverflows“代码”输入模式,存在缩进错误。我现在就修复它
  • 对不起,代码输入过程完全把我弄糊涂了,所以这里缩进正确的代码是:pastebin.com/R1NpMqgb

标签: python primes memoization


【解决方案1】:

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

  1. 该函数没有副作用。更改全局变量和执行输出是副作用的示例。
  2. 返回值仅取决于参数的值,而不取决于某些可能在调用之间改变值的全局变量。

您的 find_lin_comb 函数不满足上述条件。一方面,它每次调用时都使用不同的参数,另一方面,函数不返回值。

【讨论】:

    【解决方案2】:

    这适用于最高 1000 的所有素数,但对于最高 10**6 的素数,它非常慢,所以我希望记忆会有所帮助;在计算滑动窗口的总和时,做了很多双重工作......(对吗?)

    是的,没错。当然,对于高达 106 的素数来说,它的速度很慢。

    假设您有nN 的素数,按递增顺序编号,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 处对w(i,j) 中的j-i+1 素数求和,p_k 的工作大约是k³/6,总工作大致变成k**4/24N = 1000 的步数约为 3300 万步,花生,但 N = 1000000 的步数接近 1.6*10**18

    一年大约包含 3.1*10**7 秒,使用 ~3GHz CPU,大约是 1017 个时钟周期。所以我们说的操作需要大约 100 个 CPU 年(可能是 10 倍左右)。

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

    现在,通过记忆,您仍然可以多次查看每个窗口,但您只对每个窗口进行一次计算。这意味着您需要大约 n³/6 工作来计算窗口,并在任何窗口查看大约 n³/6 次。

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

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

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

    这告诉您关于生成该总和的窗口的什么信息?它可以从哪里开始,又可以在哪里停止?您如何使用这些信息来创建有效的算法来解决问题?

    【讨论】:

    • 谢谢你们。我看到我的方法简直太可怕了:) 我通过在窗口(以及所有剩余的窗口(“排序”)产生超过 100 万的总和)时停止求和来找到解决方案。再次感谢:)
    • 嗯,“我只是一个爱好程序员”,你说,所以如果你看不到你的方法需要多少时间也没关系。在被告知之后,您找到了快速完成它的正确条件,这是一个非常好的迹象。
    • 谢谢你,我想说通过 Project Euler 学习编程让我大开眼界。对于像我这样的外行来说,几乎无法想象当今时代的计算机可能需要十年时间才能解决如此简单的问题,所以对我来说,这真的很酷,而且令人大开眼界:)
    • 十年算不了什么 ;) 有一些欧拉计划问题,其中一个天真的蛮力搜索将运行数倍于宇宙年龄的问题。但是破解这些是非常令人满意的,所以继续享受吧。有了更多经验,您将能够在运行算法之前估算出算法所需的时间,因此您知道有些甚至不值得尝试。
    • 对不起,我不知道。我通常首先估计高级操作的数量(这里要查看的窗口数量),然后估计每个高级操作需要多少低级操作,一直持续到达到足够低的级别。如果我需要比可能相差 100 倍的估计更精确的东西,首先使用更精确的估计甚至计算,其次,一些较小的基准是定时的 - 推断更高的内存使用如何影响运行时间是棘手的,虽然(让事情变慢,但多少?)。
    猜你喜欢
    • 2018-08-27
    • 2015-12-16
    • 2018-06-05
    • 2011-01-10
    • 2017-03-12
    • 1970-01-01
    • 2023-01-29
    • 2011-12-01
    • 2021-07-01
    相关资源
    最近更新 更多