web-dev-qa-db-ja.com

数値のリストを指定して、各列と行の合計が264になるようにすべての行列を見つけます

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行のすべての組み合わせを見つけるには、多くの時間とコンピューターの能力が必要です。

7
Olba12

これは、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]
2
JohanC