Project Euler Problem 027

http://projecteuler.net/index.php?section=problems&id=27
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2027

Euler published the remarkable quadratic formula:

  n^2 + n + 41
  
It turns out that the formula will produce 40 primes for the consecutive values n = 0 to 39.
However, when n = 40, 402 + 40 + 41 = 40(40 + 1) + 41 is divisible by 41,
and certainly when n = 41, 41^2 + 41 + 41 is clearly divisible by 41.
Using computers, the incredible formula  n^2  79n + 1601 was discovered, which produces 80 primes for the consecutive values n = 0 to 79. The product of the coefficients, 79 and 1601, is 126479.
Considering quadratics of the form:

  n^2 + an + b, where |a| < 1000 and |b| < 1000

where |n| is the modulus/absolute value of n
e.g. |11| = 11 and |4| = 4
Find the product of the coefficients, a and b,
for the quadratic expression that produces the maximum number of primes for consecutive values of n,
starting with n = 0.

aとbの条件に当てはまる組み合わせを計算すると400万回弱計算しないといけないけど、あんまり計算したくないので、少し考える。


まず n = 0 のとき、

  f(0) = b

となるので、bは素数でないといけない。1000以下の素数は169個ある。


さらに n が偶数の場合、

  f(n) = 偶数 + 偶数 + b

となるので、b が 2 のときはf(偶数)が偶数になり、ダメっぽい。
したがって、 b は 3 以上 1000 以下の素数 168 個のいずれかになる。


つぎに a について考える。
n = 1 のとき、

  f(1) = 1 + a + b

となり、負数は素数でないので a は -b より大きい数である事が分かる。


さらに n が奇数のとき、

  f(n) = 奇数 + a * 奇数 + 奇数

となるので、a が奇数である事がわかる。(aが偶数だと 奇数 + 偶数 + 奇数 で f(n) = 偶数となる)


そこまで調査する数字を削減すれば十分かな。あとはグルグルと調べる。

import math
import itertools
import datetime


def primelist(limit=1000):
    checked = [ True ] * (limit/2)
    result = [2]
    for i in xrange(len(checked)):
        val = i * 2 + 3
        if checked[i]:
            result.append(val)
            j = val + val + val
            while j < limit:
                checked[(j - 3) / 2] = False
                j += val + val
    return result


cache = {}
def is_prime(n):
    if n < 0: n *= -1
    if n <  2: return False
    if n == 2: return True
    if not n & 0x01: return False
    if n in cache: return cache[n]
    
    x = 3
    limit = math.sqrt(n)
    while x <= limit:
        if n % x == 0:
            cache[n] = False
            return False
        x += 2
    cache[n] = True
    return True



def make_func(a, b):
    def f(n):
        return n * n + a * n + b
    return f



def count_consecutive_length(func, cond):
    for i in itertools.count():
        if not cond(func(i)): break
    return i


def euler027():
    maxcnt = 0
    result = 0
    
    it = iter(primelist(1000))
    it.next()
    
    for b in it:
        for a in xrange(-b, 1000, 2):
            func = make_func(a, b)
            cnt  = count_consecutive_length(func, is_prime)
            if cnt > maxcnt:
                maxcnt = cnt
                result = (a, b)
            
    print result, result[0] * result[1]


begin = datetime.datetime.now()
euler027()    
end   = datetime.datetime.now()
print end - begin

答え: -59231 (a=-61, b=971)
実行時間: 0.569957秒くらい