C# 8皇后算法示例?

C# 8皇后算法示例?,c#,algorithm,n-queens,C#,Algorithm,N Queens,有人知道好的/简洁的算法示例吗? 我做了一个网络搜索,没有找到任何好的例子 来自: 该启发式算法求解任意n个n的n个皇后≥ 4或n=1: 把n除以12。记住 余数(n是8表示8 皇后之谜) 写一张清单 按顺序从2到n的偶数 如果余数为3或9,则移动2 到列表的末尾 附加 按顺序从1到n的奇数, 但是,如果余数为8,则切换 成对(即3、1、7、5、11、9等) 如果余数为2,则切换 1和3的位置,然后移动5到 列表的末尾 如果 余数为3或9,移动1和3到 列表的末尾 放置 行中的第一列皇后,带有

有人知道好的/简洁的算法示例吗?
我做了一个网络搜索,没有找到任何好的例子

来自:

该启发式算法求解任意n个n的n个皇后≥ 4或n=1:

  • 把n除以12。记住 余数(n是8表示8 皇后之谜)
  • 写一张清单 按顺序从2到n的偶数
  • 如果余数为3或9,则移动2 到列表的末尾
  • 附加 按顺序从1到n的奇数, 但是,如果余数为8,则切换 成对(即3、1、7、5、11、9等)
  • 如果余数为2,则切换 1和3的位置,然后移动5到 列表的末尾
  • 如果 余数为3或9,移动1和3到 列表的末尾
  • 放置 行中的第一列皇后,带有 列表中的第一个数字,位置 行中的第二列 列表中的第二个数字, 等等

  • 8皇后问题的重点通常只是为了说明搜索与修剪相结合的威力。您几乎可以对搜索空间进行蛮力搜索,但如果部分解决方案违反了解决方案的约束(即一个皇后检查另一个皇后),则可以消除该部分解决方案

    仅仅使用这种修剪,8皇后是很容易解决的


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

    要将皇后放置在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-(r行)<1或(r行,c-(r行))
    • c+(r行)>8或(r行,c+(r行))未标记

    这里是一个简单的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);
                    }
                }
            }
        }
    }
    
    这个元组生成是NQueens问题的一个具体子问题。当您不知道什么是
    N
    时,关于如何编写
    N
    嵌套循环,stackoverflow上已经有很多问题。我觉得暂时停止这个问题并首先理解它的解决方案是很有指导意义的,用
    isSafe
    注释掉,只需
    返回true,首先了解递归的作用

    一旦您对这个递归元组生成器的工作感到满意,只需取消对这些行的注释,您就会得到一个简单、幼稚但有效的NQueens解算器。在未注释
    N=5
    isSafe
    行的情况下,程序现在生成以下输出:

    [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]
    
    [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
    -th元素表示放置在
    i
    -th列上的
    i
    -th皇后的行位置(所有索引均以0为基础)。因此,第一个解决方案在电路板上看起来是这样的:

    [0,2,4,1,3]
    
     Q · · · ·
     · · · Q ·
     · Q · · ·
     · · · · Q
     · · Q · ·
    
    我将把它作为一个练习来理解为什么
    isSafe
    工作,如何打印电路板布局,以及如何实现更快但更复杂的递归算法

    快乐学习。

    这里有一个简单的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;
    
            }
        }
    
    公共静态类
    {
    静态int[]板=新int[8];
    静态int MaxRows=8,MaxCols=8;
    公共静态int[]GetPosition()
    {
    if(GetPosition(0))返回板;
    否则返回null;
    }
    公共静态bool IsCollision(int行,int列)
    {
    for(int i=0;i
    年,迪克斯特拉相当正式地推导出了八皇后问题的解决方案。试试看,你可能会喜欢的

    //8皇后问题的演示
    
    // 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);
                    }
                }    
            }
        }
    }
    
    //本讲义以互动方式展示了如何解决8皇后问题 //使用递归和回溯(带修剪的穷举搜索)。 //就这段代码而言,肯定可以做一些改进, //特别是在界面和灵活性方面 //用户。但是,它在显示过程中涉及的步骤方面做得很好 //解决8皇后问题。 导入javax.swing.*; 导入java.awt.*; 导入java.awt.geom.*; 导入java.awt.event.*; 公共类JRQueens扩展JFrame实现Runnable { 公共棋盘广场[][]个广场; public boolean[]saferow;//行为空吗?true或false 公共布尔值[]safeleftdiag;//是左边(从右上角到左下角) //对角线无威胁?对还是错 公共布尔值[]saferightdiag;//为右侧(从左上角到右下角) //对角线无威胁?对还是错 private ShapePanel drawPanel;//要绘制的板的面板--请参见详细信息 //下面是课堂定义 私有JLabel info;//信息标签 私有JButton runDemo;//允许交互的按钮 私有线程runThread;//允许“移动”的线程 private int delay;//移动之间的延迟 private PauseClass pauser;//类,允许执行在其间暂停 //解决方案--请参见下面定义中的详细信息 private boolean paused;//执行是否暂停?true或false 私人电话; 公共区(内河三角洲) { 超级(“交互式8皇后问题”); 延迟=增量; drawPanel=新形状
    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);
    }
    
        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();
        }
    
    //----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;
    }
    
    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;
    }
    
    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