简体   繁体   English

N-Queens对称性打破了Google OR工具

[英]N-Queens Symmetry Breaking Google OR Tools

One of the samples for the Google or-tools is a solver for the n-queens problem. Google或工具的示例之一是n皇后问题的求解器。 At the bottom it says that the implementation can be improved by adding symmetry breaking constraints to the constraint solver. 在最下面说,可以通过将对称破坏约束添加到约束求解器来改进实现。

Looking around the internet, I found the symmetry breaking constraints for the n-queens problem , but I cannot for the life of me figure out how to convert those to constraints to python code that implements them. 环顾互联网, 我发现了n皇后问题的对称突破约束 ,但是我一生无法解决如何将约束转换为实现约束的python代码。


EDIT: this was a bad question, let's update... 编辑:这是一个不好的问题,让我们更新...

What have I tried? 我尝试了什么?

Here is the setup from the first link above: 这是上面第一个链接中的设置:

from ortools.constraint_solver import pywrapcp

N = 8
solver = pywrapcp.Solver("n-queens")
# Creates the variables.
# The array index is the column, and the value is the row.
queens = [solver.IntVar(0, N - 1, "x%i" % i) for i in range(N)]
# Creates the constraints.
# All rows must be different.
solver.Add(solver.AllDifferent(queens))
# All columns must be different because the indices of queens are all different.
# No two queens can be on the same diagonal.
solver.Add(solver.AllDifferent([queens[i] + i for i in range(N)]))
solver.Add(solver.AllDifferent([queens[i] - i for i in range(N)]))

# TODO: add symmetry breaking constraints

db = solver.Phase(queens, solver.CHOOSE_FIRST_UNBOUND, solver.ASSIGN_MIN_VALUE)
solver.NewSearch(db)
num_solutions = 0
while solver.NextSolution():
  num_solutions += 1
solver.EndSearch()
print()
print("Solutions found:", num_solutions)
print("Time:", solver.WallTime(), "ms")

I know I can implement simple constraints successfully. 我知道我可以成功实现简单的约束。 If I wanted to ensure the solution always has a queen in the first column on the first row, I could implement that like this: 如果我想确保解决方案在第一行的第一列中始终有一个皇后号,则可以这样实现:

solver.Add(queens[0] == 0)

The queens[0] variable represents the queens location in the first column and this constraint is only satisfied when the first column has a queen in the first row. queens[0]变量表示第一列中的皇后区位置,只有在第一列的第一行中有一个皇后区时,才满足此约束。 This of course is not what I want to do however because it's possible that a solution does not include any corner cells. 当然,这不是我想要做的,因为解决方案有可能不包含任何角落单元。

The symmetry breaking constraints for the n-queens problem are found below. 以下找到n皇后问题的对称破坏约束。 They are pulled directly from the link in the second paragraph. 它们直接从第二段的链接中拉出。

n-皇后对称打破约束

I understand how these constraints work. 我了解这些约束的工作原理。 The idea is that you can apply this function to each cell on the n-queens board in order to transform the state into an equivalent state. 这个想法是,您可以将此功能应用于n皇后板上的每个单元,以将状态转换为等效状态。 One of these states will be the canonical representation of that state. 这些状态之一将是该状态的规范表示。 This is used as a method to prune future processing by eliminating duplicate evaluations. 通过消除重复的评估,这被用作修剪未来处理的方法。

If I were just implementing this in an after the fact way, I would do exactly what I describe above, convert the state using each possible symmetry breaking function, calculate some sort of state hash (eg a string of the selected row in each column) and select the one that's the lowest for each proposed solution. 如果我只是以事后的方式实施此操作,那么我将按照上面的描述进行操作,使用每个可能的对称破坏函数转换状态,计算某种状态哈希(例如,每列中选定行的字符串)并为每个建议的解决方案选择最低的解决方案。 Skip future processing on ones we have seen before. 跳过我们以前见过的处理。

My problem is that I don't know how to convert these transformations into constraints for the google or-tools constraint programming solver. 我的问题是我不知道如何将这些转换转换为Google或工具约束编程求解器的约束。

Let's take a look at the simplest one, d1(r[i] = j) => r[j] = i , reflection about the main diagonal. 让我们看一下最简单的d1(r[i] = j) => r[j] = i ,它围绕主对角线反射。 What I know is that the transformation needs to be applied to all cells, then compared against the current state in order to prevent one from being expanded. 我知道的是,需要将转换应用于所有单元,然后将其与当前状态进行比较,以防止扩展一个单元。 I don't understand enough about python to understand what kind of expression works here for the transformation, and I just can't figure out how to create the constraint that compares the transformation to the current state for this particular solver. 我对python的了解不足,无法理解在此转换中可以使用哪种表达式,而且我无法弄清楚如何为该特定求解器创建将转换与当前状态进行比较的约束。

state = [queens[i].Value() for i in range(N)]
symX    = [state[N - (i + 1)] for i in range(N)]
symY    = [N - (state[i] + 1) for i in range(N)]
symD1   = [state.index(i) for i in range(N)]
symD2   = [N - (state.index(N-(i+1)) + 1) for i in range(N)]
symR90  = [N - (state.index(i) + 1) for i in range(N)]
symR180 = [N - (state[N-(i+1)] + 1) for i in range(N)]
symR270 = [state.index(N-(i+1)) for i in range(N)]

I tried to use a custom DecisionBuilder to prune the search tree using the symmetries as new constraints, but I couldn't make it work. 我尝试使用自定义的DecisionBuilder将对称性作为新约束来修剪搜索树,但无法使其正常工作。

Instead I had to use a SearchMonitor that captures the event of every solution and check if that solution is a symmetry of a previous one. 相反,我不得不使用SearchMonitor来捕获每个解决方案的事件,并检查该解决方案是否与前一个解决方案对称。

Here i add the code of the SearchMonitor, the capture of the solution overriding the "AcceptSolution" function, and the gen_symetries function to calculate and check all possible symmetries. 在这里,我添加了SearchMonitor的代码,覆盖了“ AcceptSolution”函数的解决方案捕获以及gen_symetries函数,以计算和检查所有可能的对称性。

    class SearchMonitor(pywrapcp.SearchMonitor):
    def __init__(self, solver, q):
        pywrapcp.SearchMonitor.__init__(self, solver)
        self.q = q
        self.all_solutions = []
        self.unique_solutions = []
        self.n = len(self.q)

    def AcceptSolution(self):
        qval = [self.q[i].Value() for i in range(self.n)]
        self.all_solutions.append(qval)

        symmetries = [vv in self.unique_solutions for vv in gen_symmetries(self.n, qval)]

        if sum(symmetries) == 0:
            self.unique_solutions.append(qval)

        return False

def gen_symmetries(n, solution):
    symmetries = []

    #x(r[i]=j) → r[n−i+1]=j
    x = list(range(n))
    for index in range(n):
        x[n - 1 - index] = solution[index]

    symmetries.append(x)

    #y(r[i]=j) → r[i]=n−j+1
    y = list(range(n))
    for index in range(n):
        y[index] = (n - 1 - solution[index])

    symmetries.append(y)

    #d1(r[i]=j) → r[j]=i
    d1 = list(range(n))
    for index in range(n):
        d1[solution[index]] = index

    symmetries.append(d1)

    # d2(r[i]=j) → r[n−j+1]=n−i+1
    d2 = list(range(n))
    for index in range(n):
        d2[n - 1 - solution[index]] = (n - 1 - index)

    symmetries.append(d2)

    # r90(r[i]=j) → r[j] = n−i+1
    r90 = list(range(n))
    for index in range(n):
        r90[solution[index]] = (n - 1 - index)

    symmetries.append(r90)

    # r180(r[i]=j) → r[n−i+1]=n−j+1
    r180 = list(range(n))
    for index in range(n):
        r180[n - 1 - index] = (n - 1 - solution[index])

    symmetries.append(r180)

    # r270(r[i]=j) → r[n−j+1]=i
    r270 = list(range(n))
    for index in range(n):
        r270[n - 1 - solution[index]] = index

    symmetries.append(r270)

    return symmetries

Later you just have to add the monitor to your solver like this. 以后,您只需要像这样将监视器添加到您的求解器即可。

monitor = SearchMonitor(solver, queens)
solver.Solve(db, monitor)
solver.NewSearch(db)

And finally just printing all the unique solutions 最后只需打印所有独特的解决方案

print("Unique Solutions:", len(monitor.unique_solutions), monitor.unique_solutions)

You can see the full working example in the gist. 您可以在要点中看到完整的工作示例。

https://gist.github.com/carlgira/7a4e6cf0f7b7412762171015917bccb4 https://gist.github.com/carlgira/7a4e6cf0f7b7412762171015917bccb4

You must use the known symmetry relations between the sought solutions to identify constraints that will eliminate equivalent solutions. 您必须使用寻求的解决方案之间的已知对称关系来确定将消除等效解决方案的约束。

  1. For every solution with queens[0] >= N/2 , then there is another, vertically mirrored, solution with queens[0] <= N/2 . 对于queens[0] >= N/2每个解决方案,都有一个垂直镜像的queens[0] <= N/2解决方案。 Therefore, we can seek for the solution with the smaller value of queens[0] and add the following constraint: 因此,我们可以使用queens[0]的较小值来寻找解决方案,并添加以下约束:

     solver.Add(queens[0] < (N+1)//2) # Handle both even and odd values of N 
  2. Then, a solution satisfying the condition queens[0] < queens[N-1] has an equivalent, horizontally-mirrored, solution satisfying queens[0] > queens[N-1] . 然后,满足条件queens[0] < queens[N-1]的解决方案具有等效的,水平镜像的满足queens[0] > queens[N-1]解决方案。 You can tell the solver to look for only those solutions, where the queen in the leftmost column is below the queen in the rightmost column: 您可以告诉求解器仅查找那些解决方案,这些解决方案的最左边一列中的皇后位于最右边一列中的皇后以下:

     solver.Add(queens[0] < queens[N-1]) 

I couldn't easily formulate a constraint reflecting the rotational symmetry, but I believe that it is possible. 我无法轻易制定出反映旋转对称性的约束条件,但我相信这是可能的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM