Project Euler Problem 051

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

 *1290601139*57の第1桁を置き換えることで, 157, 257, 457, 557, 757, 857という6つの素数が得られる.

56**3の第3桁と第4桁を同じ数で置き換ることを考えよう.
この5桁の数は7つの素数をもつ最初の例である: 56003, 56113, 56333, 56443, 56663, 56773, 56993.
よって, この族の最初の数である56003は, このような性質を持つ最小の素数である.

桁を同じ数で置き換えることで8つの素数が得られる最小の素数を求めよ. (注:連続した桁でなくても良い)

???あとで整理します。

import math
import itertools            
import datetime


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


def num2list(n):
    result = []
    while n:
        result.append(n % 10)
        n /= 10
    return result[::-1]
    

def count_digits(n):
    result = [ 0 ] * 10
    while n:
        result[n % 10] += 1
        n /= 10
    return result


def make_numbers(*terms):
    for num in range(10):
        val = 0
        for i in terms:
            val = val * 10 + (num if i == '?' else i)
        yield val


def make_glob(n, digit):
    original  = num2list(n)
    
    a = count_digits(n)
    a[n%10] = 0
    
    replaced = []
    for i, j in enumerate(a):
        if j >= digit:
            replaced.append(i)
    
    candidate = []
    for i in replaced:
        b = []
        for j in xrange(len(original)):
            if i == original[j]:
                b.append(j)
        candidate.append(b)
        
    for p in candidate:
        for k in xrange(digit, len(p)+1):
            for q in itertools.combinations(p, k):
                x = original[:]
                for y in q:
                    x[y] = '?'
                yield x


def euler051():
    primes = primelist(1000000)
    primes = [ i for i in primes if i > 100000 and max(count_digits(i)) >= 2 ]
    setprimes = set(primes)
    
    for i in primes:
        for j in make_glob(i, 2):
            c = sum([ 1 for k in make_numbers(*j) if k in setprimes ])
            if c == 8: return i
            

begin = datetime.datetime.now()
print euler051()                
end = datetime.datetime.now()

print end - begin

答え: 121313
実行時間: 0.756335秒くらい