什么是n皇后问题详见:八皇后问题 - 维基百科,自由的百科全书 (wikipedia.org)
简言之:
在n*n的网格上,共有n个皇后,要求任两个不能在同一行或者同一列或对角的位置上,求共有多少种摆放方式或最多能放下多少个皇后
这篇笔记主要记录一下对角约束的实现:
(1)diagonal的用法示例
import numpy as np
x = np.arange(64).reshape(8,8)
print(x)
[[ 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]]
print(x.diagonal(0))#输出主对角元素
[ 0 9 18 27 36 45 54 63]
print(x.diagonal(2))#输出偏移量为2的对角元素(以一个列表的形式)
[ 2 11 20 29 38 47]
print(x.diagonal(-2))#输出偏移量为-2的元素
[16 25 34 43 52 61]
x1=np.flipud(x)#按垂直方向翻转x
print(x1)
[[56 57 58 59 60 61 62 63]
[48 49 50 51 52 53 54 55]
[40 41 42 43 44 45 46 47]
[32 33 34 35 36 37 38 39]
[24 25 26 27 28 29 30 31]
[16 17 18 19 20 21 22 23]
[ 8 9 10 11 12 13 14 15]
[ 0 1 2 3 4 5 6 7]]
x2=np.fliplr(x)#按水平方向翻转x
print(x2)
[[ 7 6 5 4 3 2 1 0]
[15 14 13 12 11 10 9 8]
[23 22 21 20 19 18 17 16]
[31 30 29 28 27 26 25 24]
[39 38 37 36 35 34 33 32]
[47 46 45 44 43 42 41 40]
[55 54 53 52 51 50 49 48]
[63 62 61 60 59 58 57 56]]
(2)8皇后问题的实现
import numpy as np
import gurobipy as gp
from gurobipy import GRB
n = 8
# 创建模型
m = gp.Model("queen")
# 设置变量
x = m.addMVar((8,8), vtype = GRB.BINARY, name="x")
#设置目标函数
m.setObjective(x.sum(), GRB.MAXIMIZE)
#添加约束条件
#每行最多一个
m.addConstr(x.sum(axis=1) <=1, name="row")
#每列最多一个
m.addConstr(x.sum(axis=0) <= 1, name="col")
# 正对角线最多一个
for i in range(-n+1, n):
m.addConstr(sum(x.diagonal(i)) <= 1, name="diagonal")
# 反对角线最多一个
#x1= np.fliplr(x) # 水平翻转
for i in range(-n+1, n):
#m.addConstr(sum(x1.diagonal(i)) <= 1, name="anti-diagonal")
m.addConstr(sum(x[:, ::-1].diagonal(i)) <= 1, name="anti-diagonal")
m.optimize()
print(x.X)
print(f"Queens placed :{m.ObjVal:.0f}")
注:因为fliplr操作的对象是数组numpy,所以不能直接对Gurobi变量进行fliplr操作,如会出现以下错误:
目前还没有找到GUROBI对矩阵变量进行翻转的python API(后续遇到了回来补充), 所以采用以下方式来进行翻转:
m.addConstr(sum(x[:, ::-1].diagonal(i)) <= 1, name="anti-diagonal")