Project Euler Problem 037

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

3797は面白い性質を持っている. 
まずそれ自身が素数であり, 左から右に桁を除いたときに
全て素数になっている (3797, 797, 97, 7). 
同様に右から左に桁を除いたときも全て素数である (3797, 379, 37, 3).

右から切り詰めても左から切り詰めても素数になるような素数は11個しかない. 総和を求めよ.

注: 2, 3, 5, 7を切り詰め可能な素数とは考えない.

右から左に切り詰めたとき、最終的に最上位桁の数が残る。この数が素数でないといけないので、最上位桁は一桁の素数 2,3,5,7 のいずれかである事がわかる。
同じように、左から右に切り詰めてたとき、最終的に最下位桁の数が残る。右から左に切り詰める時と同じように最下位桁も素数でないといけないが、2 は切り詰める過程で偶数を作り出すので 2 をのぞいた 3,5,7 のいずれかである事がわかる。
間の数は左から右に切りつめる過程で最下位桁になるので奇数でないといけない。


上記の条件に当てはまる数だけを調査し、11個発見した時点で終了する。

import math
import itertools
import datetime


def is_prime(n):
    if n < 2: return False
    if n < 4: return True
    if n % 2 == 0: return False
    if n < 9: return True
    if n % 3 == 0: return False

    r = math.floor(math.sqrt(n))
    f = 5
    while f <= r:
        if n % f == 0: return False
        if n % (f+2) == 0: return False
        f += 6
    return True


def nlen(n):
    return int(math.ceil(math.log(n)/math.log(10)))


def is_interesting_property(n):
    m = n / 10
    while m:
        if not is_prime(m):
            return False
        m /= 10
    
    l = int(math.pow(10, nlen(n)-1))
    while l > 1:
        if not is_prime(n % l):
            return False
        l /= 10
    
    return True


def candidates(length):
    midlen = length - 2
    
    expa = int(math.pow(10, length-1))
    
    for a in [2,3,5,7]:
        vala = a * expa
        
        if midlen == 0:
            for valc in[3,5,7]:
                yield vala + valc
        else:
            for b in itertools.product([1,3,5,7,9], repeat=midlen):
                valb = reduce(lambda a, b: a * 10 + b, b) * 10
                for valc in [3,5,7]:
                    yield vala + valb + valc


def euler037():
    v = 0
    c = 0
    for i in itertools.count(2):
        for j in candidates(i):
            if not is_prime(j): continue
            
            if is_interesting_property(j):
                v += j
                c += 1
                print '', j
                if c == 11: break
        else: continue
        break
        
    print v
    

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

答え: 748317
実行時間: 0.090463秒くらい