http://projecteuler.net/index.php?section=problems&id=106
まず、可能なBとCの組合せの数を数えておきましょう。まずBを一つ以上選びます。そして、残った中から一つ以上選びます。
nC1(n-1C1 + … n-1Cn-1) + … + nCn-11C1
= nC1(2n-1 - 1) + … + nCn-1(21 - 1)
= nC12n-1 + … + nCn-121 - (nC1 + … + nCn-1)
= 3n - (2n + 1) - (2n - 2)
= 3n - 2n+1 + 1
実際にはBとCが入れ替わっただけのペアを重複して数えているので、この半分になります。
(3n + 1) / 2 - 2n
さて、例えばA = { a1, a2, a3, a4 } として、
a1 < a2 < a3 < a4
とすると、
B = { a1, a4 } C = { a2, a3 }
なら、S(B) = S(C)となるかどうかわからないのですが、
B = { a1, a3 } C = { a2, a4 }
なら、S(B) < S(C)となるので、調べる必要がありません。
BとCを並べて、(Bが必ずa1を含むとすると)先頭からインデックスを見て、常にBよりCのほうが小さければ調べる必要がないと判定されます。一つでもそれに反すれば調べる必要があることになります。
また、n = 6のでBとCをあわせて要素数4の場合、6つの中からどの4つを選んでも調べる必要があるBとCの組合せの数は同じです。だから、一つの組合せについて調べて6C4倍すればよいです。
from itertools import * import time def C(n, m): return 1 if m == 0 else C(n, m - 1) * (n - m + 1) / m def gen_pair_of_sets(n): def gen(a, p, q): if p == 0: yield (), a elif q == 0: yield a, () else: for a1, a2 in gen(a[1:], p - 1, q): yield (a[0],) + a1, a2 for a1, a2 in gen(a[1:], p, q - 1): yield a1, (a[0],) + a2 for a1, a2 in gen(tuple(range(1, n)), n / 2 - 1, n / 2): yield (0,) + a1, a2 def needs_rule1(a1, a2): return not all(a1[k] < a2[k] for k in xrange(len(a1))) def count_pair_of_sets(n): counter = 0 for m in range(4, n + 1, 2): counter += C(n, m) * sum(starmap(needs_rule1, gen_pair_of_sets(m))) return counter t = time.clock() N = 20 print count_pair_of_sets(N) print time.clock() - t