平方数を含むボックスと数独パズル

balways:

二日前、私は解決策が存在しないことを通知してきたのPython 3で解決しようとした数独の問題を与えられたが、複数のソリューションが存在するかどうかは確かではありませんよ。

問題は、次の通りです:数独の9x9のグリッドが完全に空になっています。しかしそれは含まれていない色付きのボックスを、そしてこれらのボックスの内側に、数字の合計がなければならない平方数それ以外は、通常の数独のルールが適用されます。

ここでの問題はされていない数独パズルを解くのではなく、生きたパズルを生成し、満たすのルール色の箱

私の戦略

numpyのアレイを使用し、私は9x9のグリッドに再配置することができる81個のインデックスへのグリッドに分割しています。

import numpy as np
print(np.array([i for i in range(81)]).reshape((9, 9)))

->
[[ 0  1  2  3  4  5  6  7  8]
 [ 9 10 11 12 13 14 15 16 17]
 [18 19 20 21 22 23 24 25 26]
 [27 28 29 30 31 32 33 34 35]
 [36 37 38 39 40 41 42 43 44]
 [45 46 47 48 49 50 51 52 53]
 [54 55 56 57 58 59 60 61 62]
 [63 64 65 66 67 68 69 70 71]
 [72 73 74 75 76 77 78 79 80]]

ここでインデックスのすべてのブロックを含むリストです。

boxes = [[44, 43, 42, 53],[46, 47, 38],[61, 60],[69, 70],[71, 62],
         [0, 9, 18],[1, 10, 11, 20],[2, 3, 12],[4, 13, 14],[5, 6],
         [7, 8],[17, 26, 35],[21, 22, 23],[15, 16, 24, 25, 34],
         [27, 36, 37],[19, 28, 29],[45, 54],[55, 56],[63, 64, 65],
         [72, 73, 74],[57, 66, 75 ],[58, 59, 67, 68],[76, 77],[78, 79, 80]]

あなたから見ることができるように画像、又は上記配列から、ボックスは2、3、4、または5(8つの補数、12のスリー、3四つんばい、1ドル紙幣)のブロックに配置されています。私はまた、箱が数独のルールを壊すことなく、複数の番号を含めることができることに気付きましたが、唯一の2 1の数が可能です。情報は、最大の可能広場は= 39 + 7 9 + 9 + 8 + 6のように、36になり、リストの合計が正方形の数が含まれている場合ので、ブロックのない合計が今まで見つけるには49に達することができなかったことを考えると、私は次の関数を作りました。

def isSquare(array):
    if np.sum(array) in [i**2 for i in range(1,7)]:
        return True
    else:
        return False

リストは重複の正確な量は、一つだけの数の複数の複製が含まれているかどうかを調べるために、私は次の関数を作りました。

def twice(array):
    counter = [0]*9
    for i in range(len(array)):
        counter[array[i]-1]+=1
        if 3 in counter:
            return False
    if counter.count(2)>1:
        return False
    return True

リストには、正方形の数に合計しなければならない場合さて、数字1-9を与え、リストに限られた方法ソリューションは、あります。使用itertoolsを、私は解決策を見つけることができる、インデックス0が補数のブロックを含む配列、に分けて、インデックス1はスリーのブロックが含まれ、上のようにします。

from itertools combinations_with_replacement
solutions = []
for k in range(2, 6):
    solutions.append([list(i) for i in combinations_with_replacement(np.arange(1, 10), k) if 
    isSquare(i) and twice(i)])

しかし、これらのリストの任意の順列は、「四角い問題」への実行可能なソリューションです。使用itertoolsを再び、(数独のルールなし)可能箱の総量は8782に合計します。

from itertools import permutations

def find_squares():
    solutions = []
    for k in range(2, 6):
        solutions.append([list(i) for i in combinations_with_replacement(np.arange(1, 10), k) if 
            isSquare(i) and twice(i)])
    s = []
    for item in solutions:
        d=[]
        for arr in item:
            for k in permutations(arr):
                d.append(list(k))
        s.append(d)
    return s # 4-dimensional array, max 2 of each

solutions = find_squares()

total = sum([len(i) for i in solutions])
print(total)
-> 8782

これは、ボードは一桁のそれぞれを1-9含まれていることである、行、列、ボックス、合法である場合を決定する機能を実装するために十分でなければなりません。私の実装:

def legal_row(arr):
    for k in range(len(arr)):
        values = []
        for i in range(len(arr[k])):
            if (arr[k][i] != 0):
                if (arr[k][i] in values):
                    return False
                else:
                    values.append(arr[k][i])
    return True

def legal_column(arr):
    return legal_row(np.array(arr, dtype=int).T)


def legal_box(arr):
    return legal_row(arr.reshape(3,3,3,3).swapaxes(1,2).reshape(9,9))


def legal(arr):
    return (legal_row(arr) and legal_column(arr) and legal_box(arr))

ランタイムでの難しさ

簡単な方法は、一つ一つのブロックのすべての単一の組み合わせをチェックすることです。しかし私は、私のアルゴリズムの複雑さは、これはあまりにも長い時間がかかります、donesこの、および生産いくつかの実行可能な問題を抱えています。

ブロックの順序とソリューションの順序:代わりに、私はいくつかのプロパティをランダム化することを試みました。これを使用して、私は試行回数を制限し、その溶液が生きた場合にチェック:

attempts = 1000
correct = 0
possibleBoards = []
for i in range(1, attempts+1):
    board = np.zeros((9, 9), dtype=int)
    score = 0
    shapes = boxes
    np.random.shuffle(shapes)
    for block in shapes:
        new_board = board
        new_1d = board.reshape(81)
        all_sols = solutions[len(block)-2]
        np.random.shuffle(all_sols)
        for sols in all_sols:
            #print(len(sols))
            new_1d[block] = sols
            new_board = new_1d.reshape((9, 9))
            if legal(new_board):
                board = new_board
                score+=1
                break
    confirm = board.reshape(81)
    #solve(board) # Using my solve function, not important here
    # Note that without it, correct would always be 0 as the middle of the puzzle has no boxes
    confirm = board.reshape(81)
    if (i%1000==0 or i==1):
        print("Attempt",i)
    if 0 not in confirm:
        correct+=1
        print(correct)
        possibleBoards.append(board)

上記のコードでは、変数スコアはアルゴリズムが試行中に見つけることができるどのように多くのブロックを指します。正しい変数が生成された数独ボードの多くを完了することができる方法を指します。あなたはそれが700回の試みでやったどれだけに興味がある場合は、ここでいくつかされている統計(これはhistorgramあり、x軸はスコアを表し、y軸は、これらの700回の試みの間に存在していたどのように多くの各スコアの表します)。

私はとの助けが必要なもの

私は実際には有限の時間で実行することができ、この問題への解決策を見つけるために実現可能な方法を見つけるのに苦労しています。私は非常に速くなったり、より良い私のコードの一部を行うことに関する任意のヒント、問題への異なるアプローチの任意のアイデア、問題への解決策、またはこの問題に関連のPython / numpyのに関するいくつかの便利なヒントをいただければ幸いです。

orlp:

私はSMTソルバーを使用する場所です。彼らは人々が信用を与えるよりも多くの強力です。あなたが考えることができる最善のアルゴリズムは、本質的にブルートフォースであれば、代わりにソルバーを試してみてください。単にあなたの制約を一覧表示し、それを実行すると、数秒であなたのユニークな答えを与えます:

278195436
695743128
134628975
549812763
386457291
721369854
913286547
862574319
457931682

使用コード(及び参照画像座標の):

import z3

letters = "ABCDEFGHI"
numbers = "123456789"
boxes = """
A1 A2 A3
B1 B2 C2 C3
C1 D1 D2
E1 E2 F2
F1 G1
H1 I1
G2 H2 G3 H3 H4
I2 I3 I4
B3 B4 C4
D3 E3 F3
A4 A5 B5
C5 B6 C6
G5 H5 I5 I6
A6 A7
B7 C7
D7 D8 D9
E7 E8 F7 F8
G7 H7
I7 I8
A8 B8 C8
G8 H8
A9 B9 C9
E9 F9
G9 H9 I9
"""
positions = [letter + number
             for letter in letters
             for number in numbers]
S = {pos: z3.Int(pos) for pos in positions}

solver = z3.Solver()

# Every symbol must be a number from 1-9.
for symbol in S.values():
    solver.add(z3.Or([symbol == i for i in range(1, 10)]))

# Every row value must be unique.
for row in numbers:
    solver.add(z3.Distinct([S[col + row] for col in letters]))

# Every column value must be unique.
for col in letters:
    solver.add(z3.Distinct([S[col + row] for row in numbers]))

# Every block must contain every value.
for i in range(3):
    for j in range(3):
        solver.add(z3.Distinct([S[letters[m + i * 3] + numbers[n + j * 3]]
                                for m in range(3)
                                for n in range(3)]))

# Colored boxes.
for box in boxes.split("\n"):
    box = box.strip()
    if not box: continue
    boxsum = z3.Sum([S[pos] for pos in box.split()])
    solver.add(z3.Or([boxsum == 1, boxsum == 4, boxsum == 9,
                      boxsum == 16, boxsum == 25, boxsum == 36]))

# Print solutions.
while solver.check() == z3.sat:
    model = solver.model()
    for row in numbers:
        print("".join(model.evaluate(S[col+row]).as_string()
                    for col in letters))
    print()

    # Prevent next solution from being equivalent.
    solver.add(z3.Or([S[col+row] != model.evaluate(S[col+row])
                      for col in letters
                      for row in numbers]))

おすすめ

転載: http://43.154.161.224:23101/article/api/json?id=370114&siteId=1
おすすめ