8-Queens算法示例?

时间:2010-05-24 02:28:54

标签: c# algorithm n-queens

有人知道8-queens的优秀/简洁算法示例吗? 我做了网络搜索,没有找到任何好的例子。

14 个答案:

答案 0 :(得分:14)

这是一个简单的Java实现的朴素递归算法;它应该是有益的。

public class NQueens {
    final static int N = 4;
    static int[] position = new int[N];

    public static void main(String[] args) {
        solve(0);
    }

    static boolean isSafe(int k, int p) {
//      for (int i = 1; i <= k; i++) {
//          int other = position[k - i];
//          if (other == p || other == p - i || other == p + i) {
//              return false;
//          }
//      }
        return true;
    }
    static void solve(int k) {
        if (k == N) {
            System.out.println(java.util.Arrays.toString(position));
        } else {
            for (char p = 0; p < N; p++) {
                if (isSafe(k, p)) {
                    position[k] = p;
                    solve(k+1);
                }
            }
        }
    }
}

请注意,isSafe现在包含注释行;它是故意的。通过这些行注释,该程序成为递归N - 元组生成器,其中每个值介于0(包括)和N(不包括)之间。也就是说,程序生成以下输出:

[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 0, 3]
[0, 0, 1, 0]
[0, 0, 1, 1]
[0, 0, 1, 2]
[0, 0, 1, 3]
:
:
[3, 3, 3, 0]
[3, 3, 3, 1]
[3, 3, 3, 2]
[3, 3, 3, 3]

这个N - 元组生成是NQueens问题的具体子问题。关于如何编写N - 嵌套循环的stackoverflow上有很多问题,当你不知道N是什么时。我觉得暂时停止这个问题并首先了解它的解决方案,isSafe注释只是return true;,首先要了解递归的作用,这是教学性的。

一旦你觉得这个递归元组生成器工作正常,只需取消注释这些行,你就会得到一个简单,天真但有效的NQueens求解器。在取消注释N=5isSafe行的情况下,程序现在会生成以下输出:

[0, 2, 4, 1, 3]
[0, 3, 1, 4, 2]
[1, 3, 0, 2, 4]
[1, 4, 2, 0, 3]
[2, 0, 3, 1, 4]
[2, 4, 1, 3, 0]
[3, 0, 2, 4, 1]
[3, 1, 4, 2, 0]
[4, 1, 3, 0, 2]
[4, 2, 0, 3, 1]

每一行都是5皇后问题的解决方案。数组的i - 元素表示放置在i列上的i - 女王的行位置(所有索引都是从0开始的)。因此,第一个解决方案在板上看起来像这样:

[0,2,4,1,3]

 Q · · · ·
 · · · Q ·
 · Q · · ·
 · · · · Q
 · · Q · ·

我将把它作为练习来理解为什么isSafe有效,以及如何打印电路板布局,以及如何实现更快但更复杂的递归算法。

快乐学习。

答案 1 :(得分:3)

8皇后问题的关键通常只是为了说明搜索结合修剪的力量。您几乎可以对搜索空间进行强力搜索,但在违反解决方案的约束时(即一个女王检查另一个)会消除任何部分解决方案。

只需使用这种修剪,8级皇后很容易解决。

如果您想要更有效的解决方案,这对于例如100或1000个皇后,这是一个不同的故事,你可以看看维基百科中的东西。但对于8级女王来说,蛮力和修剪就足够了。 (即,对搜索空间进行深度优先搜索,消除任何包含女王的中间节点)。

答案 2 :(得分:3)

将女王放在第r行:

if r = 0 then you're done -- return ok
for each c [1 .. 8]:
  if (r,c) is safe:
    mark (r,c)
    if you can place queen on row r-1 then return ok
    unmark (r,c)  (if you're here, this c won't work)
return not ok     (if you're here, no c generated a solution)

(r,c)对于每一行[r + 1 .. 8]:

是安全的
  • (行,c)未标记
  • c - (row-r)&lt; 1或(行,c - (行 - r))未标记
  • c +(row-r)&gt; 8或(行,c +(行-r))未标记

答案 3 :(得分:2)

// Demonstration of the 8-Queens problem

// This handout shows interactively how the 8-Queens problem can be solved
// using recursion and backtracking (with exhaustive search with pruning).

// As far as this code goes, some improvements can definitely be made,
// especially with regard to the interface and the flexibility for the
// user.  However, it does an adequate job of showing the steps involved in
// solving the 8-Queens problem.

import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;

public class JRQueens extends JFrame implements Runnable
{
    public ChessSquare [][] squares;
    public boolean [] saferow;   // is the row empty?  true or false
    public boolean [] safeleftdiag; // is the left (from upper right to lower left)
    // diagonal unthreatened?  true or false
    public boolean [] saferightdiag; // is the right (from upper left to lower right)
    // diagonal unthreatened?  true or false

    private ShapePanel drawPanel; // panel for the board to be drawn -- see details
    // in class definition below
    private JLabel info;  // informative label
    private JButton runDemo; // button to allow interaction
    private Thread runThread; // thread to allow "motion"
    private int delay;   // delay between moves
    private PauseClass pauser; // class to allow execution to pause in between
    // solutions -- see details in definition below
    private boolean paused;  // is execution paused?  true or false
    private int sol, calls;

    public JRQueens(int delta)
    {
        super("Interactive 8 Queens Problem");
        delay = delta;
        drawPanel = new ShapePanel(450, 450);

        runDemo = new JButton("See Solutions");  // Set up button
        ButtonHandler bhandler = new ButtonHandler();
        runDemo.addActionListener(bhandler);

        info = new JLabel("The 8 Queens Problem", (int) CENTER_ALIGNMENT);

        Container c = getContentPane();   // Set up layout
        c.add(drawPanel, BorderLayout.CENTER);
        c.add(info, BorderLayout.NORTH);
        c.add(runDemo, BorderLayout.SOUTH);

        squares = new ChessSquare[8][8]; // initialize variables
        saferow = new boolean[8];
        safeleftdiag = new boolean[15];
        saferightdiag = new boolean[15];
        int size = 50;
        int offset = 25;
        for (int row = 0; row < 8; row++)
        {
            saferow[row] = true;  // all rows are safe
            for (int col = 0; col < 8; col++)
            {
                squares[row][col] = new ChessSquare(offset + col*size, 
                offset + row*size, 
                size, size);
            }
        }

        for (int i = 0; i < 15; i++)
        {                               // initialize all diagonals to safe
            safeleftdiag[i] = true;
            saferightdiag[i] = true;
        }
        sol = 0;
        calls = 0;

        runThread = null;

        setSize(475, 525);
        setVisible(true);
    }

    // Is the current location safe?  We check the row and both diagonals.
    // The column does not have to be checked since our algorithm proceeds in
    // a column by column manner.
    public boolean safe(int row, int col)
    {
        return (saferow[row] && safeleftdiag[row+col] &&
        saferightdiag[row-col+7]);
    }

    // This recursive method does most of the work to solve the problem.  Note
    // that it is called for each column tried in the board, but, due to
    // backtracking, will overall be called many many times.  Each call is from
    // the point of view of the current column, col.
    public void trycol(int col)
    {
        calls++; // increment number of calls made
        for (int row = 0; row < 8; row++)    // try all rows if necessary
        {
            // This test is what does the "pruning" of the execution tree --
            // if the location is not safe we do not bother to make a recursive
            // call from that position, saving overall many thousands of calls.
            // See notes from class for details.
            if (safe(row, col))
            {
                // if the current position is free from a threat, put a queen
                // there and mark the row and diags as unsafe
                saferow[row] = false;
                safeleftdiag[row+col] = false;
                saferightdiag[row-col+7] = false;
                (squares[row][col]).occupy();
                repaint();

                if (col == 7)      // queen safely on every column, announce
                {                  // solution and pause execution
                    sol++;
                    info.setText("Solution " + sol + " Found After " + calls + " Calls");
                    runDemo.setText("Click to Continue");
                    runDemo.setEnabled(true);
                    pauser.pause();
                }
                else
                {
                    // Still more columns left to fill, so delay a bit and then
                    // try the next column recursively
                    try
                    {
                        Thread.sleep(delay);
                    }
                    catch (InterruptedException e)
                    {
                        System.out.println("Thread error B");
                    }

                    trycol(col+1);  // try to put a queen onto the next column
                }

                saferow[row] = true;               // remove the queen from
                safeleftdiag[row+col] = true;      // the current row and
                saferightdiag[row-col+7] = true;   // unset the threats. The
                (squares[row][col]).remove();      // loop will then try the
                // next row down
            } 
        }
    // Once all rows have been tried, the method finishes, and execution
    // backtracks to the previous call.
    }

    // This method executes implicitly when the Thread is started.  Note that
    // its main activity is the call trycol(0), which starts the recursive
    // algorithm on its way.
    public void run()
    {
        paused = false;
        pauser = new PauseClass();
        trycol(0);
        repaint();
        info.setText("Program Completed: " + sol + " Solutions, "
        + calls + " Calls, "
        + (8*calls) + " iterations ");
    }

    public static void main(String [] args)
    {
        // Use the delay value entered by the user, or use 100 if no
        // value is entered.
        int delay;
        if (args != null && args.length >= 1)
        delay = Integer.parseInt(args[0]);
        else
        delay = 100;
        JRQueens win = new JRQueens(delay);

        win.addWindowListener(
            new WindowAdapter()
            {
                public void windowClosing(WindowEvent e)
                { System.exit(0); }
            }
        );
    }

    // This class is used to enable the execution to pause between
    // solutions.  The Java details of this class have to do with monitors
    // and synchronized Threads and are beyond the scope of the CS 1501
    // course.  However, if you are interested in learning more about these
    // Java features, feel free to look them up in the Java API.
    private class PauseClass
    {
        public synchronized void pause()
        {
            paused = true;
            try
            {
                wait();
            }
            catch (InterruptedException e)
            {
                System.out.println("Pause Problem");
            }
        }

        public synchronized void unpause()
        {
            paused = false;
            notify();
        }
    }

    // Class to handle button.  For more on the Java details, see
    // the API online.
    private class ButtonHandler implements ActionListener
    {
        public void actionPerformed(ActionEvent e)
        {
            if (e.getSource() == runDemo)
            {
                if (!paused)
                {
                    runDemo.setEnabled(false);
                    info.setText("Searching for Solutions");
                    runThread = new Thread(JRQueens.this);
                    runThread.start();
                }
                else
                {
                    runDemo.setEnabled(false);
                    info.setText("Searching for Solutions");
                    pauser.unpause();
                }
                repaint();
            }
        }
    }

    // Inner class to represent a square on the board.  This class extends
    // Rectangle2D.Double, which can be drawn graphically by the draw() method
    // of the Graphics2D class.  The main additions I have made in the subclass
    // are the occupied variable and the drawing of the Q if the space is
    // occupied.
    private class ChessSquare extends Rectangle2D.Double
    {
        private boolean occupied;

        public ChessSquare(double x1, double y1, double wid, double hei)
        {
            super(x1, y1, wid, hei);
            occupied = false;
        }

        public void draw(Graphics2D g2d)
        {
            g2d.draw(this);
            int x = (int) this.getX();
            int y = (int) this.getY();
            int sz = (int) this.getWidth();

            if (occupied)
            {
                g2d.setFont(new Font("Serif", Font.BOLD, 36));
                g2d.drawString("Q", x+10, y+sz-10);
            }
        }

        public void occupy()
        {
            occupied = true;
        }

        public void remove()
        {
            occupied = false;
        }

        public boolean isOccupied()
        {
            return occupied;
        }
    }

    // Class that allows the board to be rendered in a nice way.
    // See that Java API or a good book on Java graphics for more
    // details about this class.
    private class ShapePanel extends JPanel
    {
        private int prefwid, prefht;

        public ShapePanel (int pwid, int pht)
        {
            prefwid = pwid;
            prefht = pht;
        }

        public Dimension getPreferredSize()
        {
            return new Dimension(prefwid, prefht);
        }

        public void paintComponent (Graphics g)
        {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            for (int i = 0; i < 8; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    (squares[i][j]).draw(g2d);
                }
            }    
        }
    }
}

答案 4 :(得分:1)

来自Wikipedia

  

这种启发式方法可以解决任何nn≥4或n = 1的n个皇后:

     
      
  1. 将n除以12.记住   余数(8是8的n   女王拼图)。
  2.   
  3. 写一份清单   偶数从2到n的数字。
  4.   
  5. 如果剩余部分是3或9,请移动2   到列表的末尾。
  6.   
  7. 追加   奇数从1到n按顺序排列,   但是,如果余数为8,则切换   对(即3,1,7,5,11,9,......)。
  8.   
  9. 如果余数为2,请切换   1和3的位置,然后移动5到   列表的末尾。
  10.   
  11. 如果   余数是3或9,移动1和3   列表的末尾。
  12.   
  13. 放置   第一列女王排成一排   列表中的第一个数字,地点   排在第二列的女王   列表中的第二个数字,   等
  14.   

答案 5 :(得分:1)

这是一个简单的C#解决方案 我认为它有效

public static class EightQueens
    {
        static   int[] board = new int[8];
        static int MaxRows = 8, MaxCols = 8;
        public static int[] GetPosition()
        {
            if (GetPosition(0)) return board;
            else return null;
        }
        public static bool IsCollision(int row, int col)
        {
            for (int i = 0; i < col; i++)
            {
                if (board[i] == row) return true; // Same Row
                if ((board[i] + col - i == row) || (board[i] - col + i == row))
                    return true;
            }
            return false;

        }
        public static bool GetPosition(int col)
        {
            if (col == MaxCols) return true;
            for (int row = 0; row < MaxRows; row++)
                if (!IsCollision(row, col))
                {
                    board[col] = row;
                    if (GetPosition(col + 1)) return true;
                }
            return false;

        }
    }

答案 6 :(得分:1)

EWD316中,Dijkstra非常正式地解决了八皇后问题。尝试一下,你可能会喜欢它!

答案 7 :(得分:1)

public class NQueensSolver
{
    private readonly List<int[]> _solutions = new List<int[]>();
    private readonly int[] _current;
    public int N { get; private set; }

    public NQueensSolver(int n)
    {
        N = n;
        _current = new int[N];
    }

    public IList<int[]> FindAllFormations()
    {
        PopulateFormations(0);
        return _solutions;
    }

    private void PopulateFormations(int row)
    {
        if (row == N)
        {
            _solutions.Add(_current.ToArray());
            return;
        }

        for (int col = 0; col <= N-1; col++)
        {
            if (Threatened(row, col))
                continue;

            _current[row] = col;
            PopulateFormations(row + 1);
        }
    }

    private bool Threatened(int row, int col)
    {
        for (int i = 0; i <= row - 1; i++)
            if (_current[i] == col || row - i == Math.Abs(col - _current[i]))
                return true;

        return false;
    }
}

一些测试:

[TestMethod]
public void TestNQueens()
{
  Assert.AreEqual(1, new NQueensSolver(1).FindAllFormations().Count);
  Assert.AreEqual(0, new NQueensSolver(2).FindAllFormations().Count);
  Assert.AreEqual(0, new NQueensSolver(3).FindAllFormations().Count);
  Assert.AreEqual(2, new NQueensSolver(4).FindAllFormations().Count);
  Assert.AreEqual(10, new NQueensSolver(5).FindAllFormations().Count);
  Assert.AreEqual(4, new NQueensSolver(6).FindAllFormations().Count);
  Assert.AreEqual(40, new NQueensSolver(7).FindAllFormations().Count);
  Assert.AreEqual(92, new NQueensSolver(8).FindAllFormations().Count);
  Assert.AreEqual(352, new NQueensSolver(9).FindAllFormations().Count);
  Assert.AreEqual(724, new NQueensSolver(10).FindAllFormations().Count);
}

答案 8 :(得分:1)

这是C#中的一个简单解决方案。

//----N Queens ----
public static bool NQueens(bool[,] board, int x)
{
      for (int y = 0; y < board.GetLength(1); y++)
      {
          if (IsAllowed(board, x, y))
          {
               board[x, y] = true;
               if (x == board.GetLength(0) - 1 || NQueens(board, x + 1))
               {
                   return true;
               }
               board[x, y] = false;
          }
     }
     return false;
}
//verify diagonals, column and row from i=0 to x because from x to down it dont put anything
private static bool IsAllowed(bool[,] board, int x, int y)
{
    for (int i = 0; i <= x; i++)
    {
        if (board[i, y] || (i <= y && board[x - i, y - i]) || (y + i < board.GetLength(0) && board[x - i, y + i]))
        {
            return false;
        }
    }
    return true;
}

来自http://yadiragarnicabonome.com/n-queens-problem/

答案 9 :(得分:0)

TheWalnut.io中,有一个N-Queens的可视化:它被认为是约束满足问题,并使用局部搜索算法(使用最小冲突启发式)来解决它。 The code (in Javascript) is also there

可视化是针对N == 8的特定情况,但可以将其更改为任何N.

答案 10 :(得分:0)

    private const int Size = 8;
    private static readonly bool[,] chessboard = new bool[Size, Size];

    //The Rows are 8, numbered from 0 to 7.
    //The Columns are 8, numbered from 0 to 7.
    //The left diagonals are 15, numbered from -7 to 7. following formula : leftDiag = col - row.
    //The right diagonals are 15, numbered from 0 to 14 by the formula: rightDiag = col + row.
    private static readonly HashSet<int> AttackedRows = new HashSet<int>();

    private static readonly HashSet<int> AttackedCols = new HashSet<int>();

    private static readonly HashSet<int> AttackedLeftDiagonals = new HashSet<int>();

    private static readonly HashSet<int> AttackedRightDiagonals = new HashSet<int>();

    private static int solutionsFound;

    private static void Main(string[] args)
    {
        PutQueens(0);
        Console.WriteLine(solutionsFound);
    }

    private static void PutQueens(int row)
    {
        if (row == Size)
        {
            PrintBoard();
            solutionsFound++;
        }

        else
        {
            for (var col = 0; col < Size; col++)
            {
                if (CanPlaceQueen(row, col))
                {
                    MarkAllAttackedPositons(row, col);
                    PutQueens(row + 1);
                    UnMarkAllAttackedPositons(row, col);
                }
            }
        }
    }

    private static void UnMarkAllAttackedPositons(int row, int col)
    {
        AttackedRows.Remove(row);
        AttackedCols.Remove(col);
        AttackedLeftDiagonals.Remove(col - row);
        AttackedRightDiagonals.Remove(col + row);
        chessboard[row, col] = false;
    }

    private static void MarkAllAttackedPositons(int row, int col)
    {
        AttackedRows.Add(row);
        AttackedCols.Add(col);
        AttackedLeftDiagonals.Add(col - row);
        AttackedRightDiagonals.Add(col + row);
        chessboard[row, col] = true;
    }

    private static bool CanPlaceQueen(int row, int col)
    {
        var positionOccuppied = AttackedCols.Contains(col) || AttackedRows.Contains(row)
                                || AttackedLeftDiagonals.Contains(col - row)
                                || AttackedRightDiagonals.Contains(col + row);
        return !positionOccuppied;
    }

    private static void PrintBoard()
    {
        for (var row = 0; row < Size; row++)
        {
            for (var col = 0; col < Size; col++)
            {
                Console.Write(chessboard[row, col] ? "Q " : "- ");
            }
            Console.WriteLine();
        }
        Console.WriteLine();
    }

答案 11 :(得分:0)

我写了一个detailed, commented example of the N Queens problem in Python,然后把它放在GitHub上。看看吧!

答案 12 :(得分:0)

我解决了这种部署C ++的问题,请参见终端上的正确操作。

enter image description here

您可能会在my queen.cpp

上看到更多详细信息

下棋功能

void amo::Queen::place(int probing, int n) {
    if (n != row || n != col ) {
        if (chess != nullptr) {
            for (int i=0;i<row;i++) delete *(chess+i);
            delete chess;
        }   
        row = n;
        col = n;
        chess = new int*[n];
        for (int i=0;i<n;i++) {
            *(chess+i) = new int[col];
            for (int j=0; j<col; j++) 
                *(*(chess+i)+j) = 100*(i+1)+j;
        }
    }
    if (probing >= n) {
        ans += 1;
        std::cout << GREEN <<"[Queen::place()]: returns when met the pass-the-end of probing which means deduced one of answer:" << ans << WHITE << std::endl;
        for (std::vector<int>::iterator it=queens.begin(); it!=std::next(queens.begin(), n); it++)
            collect(moves, 1, std::distance(queens.begin(), it), *it);
        traverse(moves);
        moves.clear();
        return;
    }
    for (int pruning=0; pruning<col; pruning++) {
        track++;
        //traverse(probing, pruning);
        //if (probing==n-1 && pruning==col-1) std::cout << RED <<"[Queen::place()]: track:" << track << WHITE << std::endl; //final track=4^4+4^3+4^2+4^1=340 if n=4 or track=3^3+3^2+3^1=39 if n=3
        if (!check(probing, pruning)) {
        //if (false) { //watching all moves
            //std::cout << "[Queen::place()]: going to prune" << WHITE << std::endl;
            /**
             * 'prunes' when met one of dead ends of this probing at this pruning
             */
            continue;
        }
        else { //found one of rights of this probing at this pruning and digs into the one more deeper 
            //std::cout << "[Queen::place()]: going to probe" << WHITE << std::endl;
            if (queens.size() < n) queens.resize(n);
            queens.at(probing) = pruning;
            /**
             * 'probes' one more deeper by making one more call stack returning back here as backtracking and proceeding to another pruning
             */ 
            place(probing+1, n);
        }
    }
    /**
     * 'backtracks'
     */ 
    return;
}

和用于检查移动是否为解决方案的功能

bool amo::Queen::check(int row, int column) {
    if (row == 0) {
        //std::cout << CYAN << "okay chess[" << row <<"][" << column << "]" << WHITE << std::endl;
        return true;
    }
    for (std::vector<int>::iterator it=queens.begin(); it!=std::next(queens.begin(), row); it++) {
        int placed_row = std::distance(queens.begin(), it);
        int placed_column = queens.at(placed_row);
        if (placed_column == column) {
            //std::cout << MAGENTA << "not across,   queen[" << placed_row << "][" << placed_column << "] and chess[" << row <<"][" << column << "]" << WHITE << std::endl;
            return false;
        }
        if (std::abs(placed_column-column) == std::abs(placed_row-row)) {
            //std::cout << MAGENTA << "not diagonal, queen[" << placed_row << "][" << placed_column << "] and chess[" << row <<"][" << column << "]" << WHITE << std::endl;
            return false;
        }
    }
    //std::cout << CYAN << "okay chess[" << row <<"][" << column << "]" << WHITE << std::endl;
    return true;
}

答案 13 :(得分:0)

SQL:

with tx1 as (select 1 as k union all select t2.k + 1 from tx1 as t2 where t2.k < 8)

select *
from tx1 as x1
join tx1 as x2 on 
    x2.k <> x1.k and 
    x2.k <> x1.k + 1 and x2.k <> x1.k - 1
join tx1 as x3 on 
    x3.k <> x1.k and x3.k <> x2.k and 
    x3.k <> x2.k + 1 and x3.k <> x2.k - 1 and
    x3.k <> x1.k + 2 and x3.k <> x1.k - 2
join tx1 as x4 on 
    x4.k <> x1.k and x4.k <> x2.k and x4.k <> x3.k and
    x4.k <> x3.k + 1 and x4.k <> x3.k - 1 and
    x4.k <> x2.k + 2 and x4.k <> x2.k - 2 and
    x4.k <> x1.k + 3 and x4.k <> x1.k - 3
join tx1 as x5 on 
    x5.k <> x1.k and x5.k <> x2.k and x5.k <> x3.k and x5.k <> x4.k and
    x5.k <> x4.k + 1 and x5.k <> x4.k - 1 and
    x5.k <> x3.k + 2 and x5.k <> x3.k - 2 and
    x5.k <> x2.k + 3 and x5.k <> x2.k - 3 and 
    x5.k <> x1.k + 4 and x5.k <> x1.k - 4
join tx1 as x6 on 
    x6.k <> x1.k and x6.k <> x2.k and x6.k <> x3.k and x6.k <> x4.k and x6.k <> x5.k     and
    x6.k <> x5.k + 1 and x6.k <> x5.k - 1 and
    x6.k <> x4.k + 2 and x6.k <> x4.k - 2 and
    x6.k <> x3.k + 3 and x6.k <> x3.k - 3 and 
    x6.k <> x2.k + 4 and x6.k <> x2.k - 4 and 
    x6.k <> x1.k + 5 and x6.k <> x1.k - 5
join tx1 as x7 on 
    x7.k <> x1.k and x7.k <> x2.k and x7.k <> x3.k and x7.k <> x4.k and x7.k <> x5.k and x7.k <> x6.k and
    x7.k <> x6.k + 1 and x7.k <> x6.k - 1 and
    x7.k <> x5.k + 2 and x7.k <> x5.k - 2 and
    x7.k <> x4.k + 3 and x7.k <> x4.k - 3 and 
    x7.k <> x3.k + 4 and x7.k <> x3.k - 4 and 
    x7.k <> x2.k + 5 and x7.k <> x2.k - 5 and
    x7.k <> x1.k + 6 and x7.k <> x1.k - 6
join tx1 as x8 on 
    x8.k <> x1.k and x8.k <> x2.k and x8.k <> x3.k and x8.k <> x4.k and x8.k <> x5.k and x8.k <> x6.k and x8.k <> x7.k and
    x8.k <> x7.k + 1 and x8.k <> x7.k - 1 and
    x8.k <> x6.k + 2 and x8.k <> x6.k - 2 and
    x8.k <> x5.k + 3 and x8.k <> x5.k - 3 and 
    x8.k <> x4.k + 4 and x8.k <> x4.k - 4 and 
    x8.k <> x3.k + 5 and x8.k <> x3.k - 5 and
    x8.k <> x2.k + 6 and x8.k <> x2.k - 6 and
    x8.k <> x1.k + 7 and x8.k <> x1.k - 7
order by 1,2,3,4,5,6,7,8