16個の番号のリストがあるとしましょう。これらの16の数値を使用して、さまざまな4x4行列を作成できます。リスト内の各要素が1回使用され、各行と各列の合計が264に等しいすべての4x4行列を見つけたいと思います。
まず、合計264個のリスト内の要素のすべての組み合わせを見つけます
numbers = [11, 16, 18, 19, 61, 66, 68, 69, 81, 86, 88, 89, 91, 96, 98, 99]
candidates = []
result = [x for x in itertools.combinations(numbers, 4) if sum(x) == 264]
result
は、各要素が4つの要素を持つリストであり、4つの要素の合計= 264であるリストになります。これらを私の行と考えています。次に、加算は可換であるため、行のすべての順列を取得したいと思います。
for i in range(0, len(result)):
candidates.append(list(itertools.permutations(result[i])))
ここで、合計が264である可能性のあるすべての行を指定します。すべての列の合計が264になるように、4行のすべての組み合わせを選択したいと思います。
test = []
for i in range(0, len(candidates)):
test = test + candidates[i]
result2 = [x for x in itertools.combinations(test, 4) if list(map(add, x[0], list(map(add, x[1], list( map(add, x[2], x[3])))))) == [264, 264, 264, 264]]
より速く/より良い方法はありますか?最後の部分である4行のすべての組み合わせを見つけるには、多くの時間とコンピューターの能力が必要です。
これは、Pythonバージョンの Z3 SAT/SMTソルバー である z3py を使用したアプローチです。行や列のすべての順列、およびミラーリングによって、追加のソリューションが提供されることに注意してください。一緒に、各プリミティブソリューションは24 * 24 * 2の同等のソリューションにつながります。
順序を強制するための制約を追加すると、すべてのプリミティブソリューションを見つけることができるはずです。間違いがなければ、次のプログラムは6つすべてを見つけます。したがって、すべて一緒に6 * 24 * 24 * 2 = 6912のソリューションがあるはずです。
from z3 import Solver, BitVec, Or, Distinct, sat
numbers = [11, 16, 18, 19, 61, 66, 68, 69, 81, 86, 88, 89, 91, 96, 98, 99]
# X is a table to store the 16 variables for the solution
X = [BitVec(f'x{i}{j}', 16) for i in range(4) for j in range(4)]
s = Solver()
for x in X:
s.add(Or([x == n for n in numbers])) # all X[i] should be one of the given numbers
# constraints to avoid reordered solutions
s.add(X[0] == 11)
s.add(X[0] < X[1])
s.add(X[1] < X[2])
s.add(X[2] < X[3])
s.add(X[1] < X[4])
s.add(X[4] < X[8])
s.add(X[8] < X[12])
# all X[i] have to be distinct
s.add(Distinct(X))
for i in range(4):
# all rows and all columns need to sum to 264
s.add(sum([X[4*i+j] for j in range(4)]) == 264)
s.add(sum([X[4*j+i] for j in range(4)]) == 264)
# start solving
res = s.check()
while res == sat:
m = s.model()
# show the solution
for i in range(4):
print([m[X[i*4+j]] for j in range(4)])
print()
# add the just found solution as a constraint so it doesn't get outputted again
s.add(Or([X[i] != m[X[i]].as_long() for i in range(16)]))
# solve again to find different solutions
res = s.check()
出力:
[11, 68, 89, 96]
[69, 16, 91, 88]
[86, 99, 18, 61]
[98, 81, 66, 19]
[11, 68, 86, 99]
[69, 16, 98, 81]
[88, 91, 19, 66]
[96, 89, 61, 18]
[11, 66, 89, 98]
[69, 18, 91, 86]
[88, 99, 16, 61]
[96, 81, 68, 19]
[11, 66, 88, 99]
[68, 19, 91, 86]
[89, 98, 16, 61]
[96, 81, 69, 18]
[11, 66, 88, 99]
[69, 18, 96, 81]
[86, 91, 19, 68]
[98, 89, 61, 16]
[11, 66, 89, 98]
[68, 19, 96, 81]
[86, 91, 18, 69]
[99, 88, 61, 16]