两天前,我得到了一个我试图用Python 3解决的数独问题。我被告知确实存在一个解决方案,但我不确定是否存在多个解决方案。
问题如下:一个9x9的数独网格完全是空的。然而,它确实包含彩色框,在这些框中,数字的总和必须是一个平方数。除此之外,通常的数独规则也适用。
这里的问题不是解决一个数独谜题,而是生成一个可行的谜题,满足彩色框的规则。
我的策略
使用numpy数组,我将网格划分为81个索引,这些索引可以重新排列为9x9网格。
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个2、12个3、3个4、1个5)。我还注意到一个盒子可以包含多个数字而不违反数独的任何规则,但一个数字中只有2个是可能的。给定这些信息,最大的可能正方形是36,9 9 8 7 6 = 39,因此一个块的总和永远不会达到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包含2块,索引1包含3块,依此类推。
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))
运行时困难
一种简单的方法是检查每个块的每个组合。我已经完成了这项工作,并提出了几个可行的问题,但是我的算法的复杂性使得这项工作花费的时间太长。
相反,我尝试了随机化一些属性:块的顺序和解决方案的顺序。使用此方法,我限制了尝试次数,并检查解决方案是否可行:
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)
在上面的代码中,可变分数指的是算法在一次尝试中可以找到多少块。变量correct表示生成的数独板可以完成的数量。如果你对它在700次尝试中的表现感兴趣,这里有一些统计数据(这是一个直方图,x轴代表分数,y轴代表在这700次尝试中每个分数出现的次数)。
我需要什么帮助
我正在努力寻找一种可行的方法来找到这个问题的解决方案,它实际上可以在有限的时间内运行。我非常感谢有关使我的一些代码更快或更好的任何提示,解决问题的不同方法的任何想法,问题的任何解决方案,或与此问题相关的Python/Numpy的一些有用提示。
一个名为Box的列表由9个元素创建,每个元素都是另一个列表。这9个列表对应于9个框中的每个框,每个列表都包含元组作为元素,每个框中的每个方块都有行和列索引。以与以下类似的方式显式输入值会产生相同的效果(但会浪费时间):
# The boxes list is created, with the row and column index of each square in each box
Boxes = [
[(3*i+k+1, 3*j+l+1) for k in range(3) for l in range(3)]
for i in range(3) for j in range(3) ]
这是我将使用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]))
数独游戏,游戏分三个难度来生成数独矩阵,有标记功能。关于挑战模式还没有实现。 [Code4App.com]
本章我们要制作一个俄罗斯方块游戏。 Tetris 译注:称呼:方块是由四个小方格组成的 俄罗斯方块游戏是世界上最流行的游戏之一。是由一名叫Alexey Pajitnov的俄罗斯程序员在1985年制作的,从那时起,这个游戏就风靡了各个游戏平台。 俄罗斯方块归类为下落块迷宫游戏。游戏有7个基本形状:S、Z、T、L、反向L、直线、方块,每个形状都由4个方块组成,方块最终都会落到屏幕底部。所以玩家通过控制
本章我们要制作一个俄罗斯方块游戏。 Tetris 译注:称呼:方块是由四个小方格组成的 俄罗斯方块游戏是世界上最流行的游戏之一。是由一名叫 Alexey Pajitnov 的俄罗斯程序员在 1985 年制作的,从那时起,这个游戏就风靡了各个游戏平台。 俄罗斯方块归类为下落块迷宫游戏。游戏有 7 个基本形状:S、Z、T、L、反向 L、直线、方块,每个形状都由 4 个方块组成,方块最终都会落到屏幕底部
我在做一个有趣的项目:使用OpenCV(如谷歌护目镜等)从输入图像中求解数独。我已经完成了任务,但最后我发现了一个小问题,我来到这里。 我使用OpenCV 2.3.1的Python API进行编程。 以下是我所做的: > 读取图像 找到轮廓 选择面积最大的一个,(也有点等同于正方形)。 找到角点。 e、 g.如下所示: (请注意,这里的绿线与数独的真实边界正确重合,因此可以正确扭曲数独。请查看下一
问题内容: 我当时在做一个有趣的项目:使用OpenCV(如Google护目镜等)从输入图像中解决数独。我已经完成了任务,但是最后我发现了一个我来到这里的小问题。 我使用OpenCV 2.3.1的Python API进行了编程。 以下是我所做的: 读取图像 找到轮廓 选择一个具有最大面积的((也有些等同于正方形))。 找到拐角点。 例如下面给出: (请注意,绿线正确地与数独的真实边界重合,因此数独可
本文向大家介绍java版数独游戏核心算法(一),包括了java版数独游戏核心算法(一)的使用技巧和注意事项,需要的朋友参考一下 之前学习javascript时用javascript写过一个数独游戏,最近看了一点java的内容,于是就心血来潮想搞一个java版的数独游戏。 现在将全部代码分享出来和大家学习交流,当然代码中有着各种各样的问题和不足之处,望各位朋友批评指点。 以下是生成数独地图的核心算法