Project Euler 111

http://projecteuler.net/index.php?section=problems&id=111


単純に、対象となる整数を生成して素数判定します。生成は、例えば4桁で1を3つ使うとすると、残りの1つの桁をまず決めて、そこに1以外の数字をあてはめます。素数判定は、単純に素数で割っていきます。
これで2秒でした。もうちょっとなんとかならないでしょうか。
素数性を調べる対象は、大半は最後の桁が繰り返しの数字ではない場合です。そのため10個がまとまっているので、エラトステネスのふるいが使えそうです。ただ、やってみると桁数が大きくなるにつれて遅くなりました。
単純にミラー・ラビン素数判定法を使ったら0.3秒でした。
以下は、それを使っていないコードです。

from itertools import *
import time

def sieve(max_n):
    a = [ True ] * (max_n + 1)
    for p in (n for n in xrange(2, max_n + 1) if a[n]):
        for k in xrange(p * 2, max_n + 1, p):
            a[k] = False
    return [ n for n in xrange(2, max_n + 1) if a[n] ]

def is_prime(n):
    return all(n % p != 0 for p in
                takewhile(lambda p: p * p <= n, primes))

def to_number(a):
    return reduce(lambda x, y: x * 10 + y, a)

def S(d):
    def gen_0(m):
        for a in combinations(range(1, N), m - 1):
            yield (0,) + a
    
    def gen_number(m):
        if d == 0:
            g = gen_0(m)
        else:
            g = combinations(range(N), m)
        for a in g:
            for b in product(range(d) + range(d + 1, 10),
                                            repeat = len(a)):
                if b[0] == 0 and a[0] == 0:
                    continue
                c = [ d ] * N
                for x, y in izip(a, b):
                    c[x] = y
                yield to_number(c)
    
    def S_(m):
        return sum(filter(is_prime, gen_number(m)))
    
    return ifilter(lambda s: s > 0, imap(S_, count(1))).next()

t = time.clock()
N = 10
primes = sieve(int(10 ** (N / 2.) + 1))
print sum(S(d) for d in range(10))
print time.clock() - t