元のコードはこうでしたが、
from itertools import izip, count def next(s, k): return s[:k] + (s[k] - 1,) + tuple(n + 1 for n in s[k+1:]) def E(s): num_papers = sum(s) if num_papers == 0: return 0.0 return (1 if num_papers == 1 else 0) \ + sum(n * E(next(s, k)) / num_papers for k, n in izip(count(), s) if n > 0) N = 5 print "%.6f" % (E((1,) + (0,) * (N - 1)) - 2)
E(s)は何回通るでしょうか。実行時間はこれによると思われます。
上のコードで通る回数を数えればよいですが、これでは上のコードと時間がかかります。なんとかならないでしょうか。
例えば、状態(0, 0, 1)は、(0, 1, 0)と(0, 0, 2)から移行します。だから、(0, 1, 0)と(0, 0, 2)が通った回数を足せば、(0, 0, 1)が通った回数になります。その際に次のコードのgen_sのようにすると、封筒を取り出した回数順に状態を出すことができるので、容易に通った回数を計算することができます。
from itertools import chain def add(dic, key, value): if key in dic: dic[key] += value else: dic[key] = value def next(s, k): return s[:k] + (s[k] - 1,) + tuple(n + 1 for n in s[k+1:]) def gen_s(set_s): if len(set_s) > 0: return chain(set_s, gen_s(set(next(s, k) for s in set_s for k in xrange(len(s)) if s[k] > 0))) else: return set_s def num_pass(N): s0 = (1,) + (0,) * (N - 1) dic_num = { s0 : 1 } for s in gen_s(set((s0,))): n = dic_num[s] for k in (k for k in xrange(len(s)) if s[k] > 0): add(dic_num, next(s, k), n) return sum(dic_num.itervalues()) for N in xrange(2, 9): print N, num_pass(N)
これを実行すると、
2 3 3 8 4 125 5 191170 6 7087287305719 7 404634508398297036890780530296 8 151348294904659598414501102210976841112270718960049871705594229385
ものすごい勢いで増加しますね。どうりで最初のコードではN=6のときに結果が返ってこないわけです。