Project Euler Problem 024
http://projecteuler.net/index.php?section=problems&id=24
http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2024
A permutation is an ordered arrangement of objects. For example, 3124 is one possible permutation of the digits 1, 2, 3 and 4. If all of the permutations are listed numerically or alphabetically, we call it lexicographic order. The lexicographic permutations of 0, 1 and 2 are: 012 021 102 120 201 210 What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9?
0から9までの数字を使って10桁の数字を作る。それを辞書順に並べた時(数字なんで普通に並べた時)の100万番目の数は調べる。
Pythonでは順列を扱うには itertools の permutations が使える。順番に調べて100万番目の値を調べればよい。
import itertools import datetime def euler024a(): N = 1000000 result = [] i = 0 for x in itertools.permutations(range(10), 10): i += 1 if i == N: result = x break print reduce(lambda a, b: a * 10 + b, result) begin = datetime.datetime.now() euler024a() end = datetime.datetime.now() print end - begin
答え: 2783915460
実行時間: 0.290858秒くらい
ひと工夫
最初のやりかたは、順列を全部調べるので効率が悪い。なので工夫する。説明は超てきとう。
例えば最初の数字が 0 の場合の組み合わせの数は、9! で 362880 となり、1000000 以下なので、最初の数字は 0 では無い事がわかる。
最初の数字が 1 のとき(9! * 2 = 725760)でも 1000000 に満たないので 1 でもない。同じように調べると、最初の数字が 2 である事がわかる。
最初の数を 2 とした場合、残りの組み合わせは 8! なので同じように調べていけば、n番目の順列をピンポイントで見つける事ができる。
import datetime def permutation_of_index(lst, index): def make_fact_table(n): result = [ 0 ] a = 1 for i in xrange(1, n): a *= i result.append(a) return result fact_tbl = make_fact_table(len(lst)) fact_tbl.reverse() l = lst[:] n = index result = [] for i in xrange(len(l)): id = 0 if fact_tbl[i] and n >= fact_tbl[i]: id = n / (fact_tbl[i]) n %= fact_tbl[i] result.append(l[id]) l.pop(id) return result def euler024b(): N = 1000000 result = permutation_of_index(range(10), N-1) print reduce(lambda a, b: a * 10 + b, result) begin = datetime.datetime.now() euler024b() end = datetime.datetime.now() print end - begin
答え: 2783915460
実行時間: 0.000051秒くらい
だいぶん、早くなった!