我正在尝试使用Python中的约束编程来执行自定义幻方解算器。为此,我使用python-constraint(http://labix.org/python-constraint)。
对于这个问题,幻方的定义是:“幻方是nxn矩阵中整数(正数或负数)的排列,并且是任何行,任何列的条目总和,或者任何主要的对角线都是一样的。“
我有一个预先填充的魔方:
+----+----+----+----+
| 7 | | | 4 |
+----+----+----+----+
| | | | |
+----+----+----+----+
| 0 | -3 | -2 | 3 |
+----+----+----+----+
| -5 | 6 | | |
+----+----+----+----+
以下是我使用的代码:
from constraint import *
problem = Problem()
problem.addVariables(range(0, 16), range(-20, 20))
problem.addConstraint(lambda a: a==7, [0])
problem.addConstraint(lambda a: a==4, [3])
problem.addConstraint(lambda a: a==0, [8])
problem.addConstraint(lambda a: a==-3, [9])
problem.addConstraint(lambda a: a==-2, [10])
problem.addConstraint(lambda a: a==3, [11])
problem.addConstraint(lambda a: a==-5, [12])
problem.addConstraint(lambda a: a==6, [13])
problem.addConstraint(ExactSumConstraint(-2), [0,5,10,15])
problem.addConstraint(ExactSumConstraint(-2), [3,6,9,12])
for row in range(4):
problem.addConstraint(ExactSumConstraint(-2),
[row*4+i for i in range(4)])
for col in range(4):
problem.addConstraint(ExactSumConstraint(-2),
[col+4*i for i in range(4)])
solutions = problem.getSolution()
print solutions
我找不到任何解决方案,而我认为我的约束是纠正的。每行和每列以及两个对角线的总和必须等于-2(基于我们在魔方上的行)。
你有什么想法吗?谢谢。
答案 0 :(得分:1)
好的,让我们做一些数学(和python)来解决你的谜团。
第一行的行约束告诉你,pos的值。 4是-4。 off off对角线的约束告诉你,pos的值。 6是2。
因此我们已经使用了值[-5,-4,-3,-2,0,2,3,4,6,7]。这些值的总和是8.
因此,我们必须在没有选择值的情况下选择超出范围(-20,20)的六个值。
在4个4个条目的魔术方阵中,行/列/对角线总和必须
1/4 * sum(all_entries)
有了这个,我们就可以准备一个强力解决方案。
from itertools import combinations
choosen = [-5, -4, -3, -2, 0, 2, 3, 4, 6, 7] # len(choosen) == 10
s_choosen = sum(choosen)
free_values = [x for x in range(-20, 20) if x not in choosen]
to_test = []
# we have to choose 6 values out of free_values
for comb in combinations(free_values, 6):
if (1 / 4. * (sum(comb) + s_choosen)) == -2: # could form correct row/col/diag sum
to_test.append(comb)
此代码为我们提供了7254个可能的6元组来填补广场上的空闲位置。并且它们都没有产生魔术方阵。
如果您明确地不想应用AllDifferentConstraint(),那么您必须执行以下操作以通过强制执行来验证python-constraint的解决方案。
你仍然需要选择6个值;但是这次超出整个范围(-20,20)而需要更换。
from itertools import combinations_with_replacement
to_test2 = []
for comb in combinations_with_replacement(range(-20, 20), 6):
if (1 / 4. * (sum(comb) + s_choosen)) == -2: # could form correct row/col/diag sum
to_test2.append(comb)
len (to_test2)
97063
函数combinations_with_replacements
仅返回已排序的组合。现在我们必须添加满足行和的约束的所有排列。
已设置的值(7和4)的总和为11.因此,6元组中的前两个条目必须具有sum == -13。对于第二行,推导的条目(-4和2)具有-2。因此,此行的剩余条目必须加起来为0.在最后一行中,已设置条目的总和为1.因此,最后两个条目的总和必须为-3:
from itertools import permutations
sum_rows = []
for comb in to_test2:
for entry in permutations(comb):
if entry[0] + entry[1] == -13 and entry[2] + entry[3] == 0 and entry[4] + entry[5] == -3:
sum_rows.append(entry)
len(sum_rows)
56672
现在我们必须检查列总和。 第二列的总和为(-3和6)。因此,条目0和2的总和必须为-5。 第三列的总和为(2和-2)。因此,条目1和4的总和必须为-2。 第四列有(4和3)和7.因此,条目3和5的总和必须是-9。
col_sums = []
for entry in sum_rows:
if entry[0] + entry[2] == -5 and entry[1] + entry[4] == -2 and entry[3] + entry[5] == -9:
col_sums.append(entry)
len(col_sums)
32
最后我们要检查对角线的总和。 对角线(7和-2)之和为5.因此,条目2和5的总和必须为-7。
diag_sums = []
for entry in col_sums:
if entry[2]+ entry[5] == -7:
diag_sums.append(entry)
len(diag_sums)
1
print diag_sums
[(-6, -7, 1, -1, 5, -8)]