In-depth explanation of the two most efficient algorithms on Queen N's question

Source: Internet
Author: User

The N queen problem is a classic problem. Place N queens on a N * N board, each row is one and cannot attack each other (the queen of the same row, column, and slash will automatically attack each other ).
I. Solving the N queen problem is a classic case of backtracking in algorithms
The backtracking algorithm is also called the testing method. It is a systematic method for searching for problems. The basic idea of the backtracking algorithm is to move forward from one path. If you can enter, enter, and return if you cannot enter. Try again in another way.
In reality, there are many problems that we often need to put all the possibilities out, and then find out the possible or optimal conditions that meet certain requirements, so as to get the solution of the entire problem. The backtracking algorithm is a "general algorithm" to solve this problem. It is called a "Universal algorithm. The question of Queen N is such a space-consuming solution when N increases, so it is suitable for solving this problem. This is also a classic solution to Queen N's problem.
The following is an advanced pseudo-code description of the algorithm. Here an N * N matrix is used to store the Board:
1) The algorithm starts and clears the board. The current row is set to the first row, and the current column is set to the first column.
2) Determine whether the conditions are met on the current row and position of the current column (that is, ensure that there are no two queens on the columns and slashes passing through this row). If not, skip to step 2.
3) Situations where conditions are met at the current position:
Put a queen in the current position. If the current row is the last row, record a solution;
If the current row is not the last row, the current row is set as the next row, and the current column is set as the first location to be tested for the current row;
If the current row is the last row, the current column is not the last column, and the current column is set as the next column;
If the current row is the last row and the current column is the last column, backtrack: clears the chessboard of the current row and the following rows, and then sets the current row as the previous row, the current column is set to the next location to be tested for the current row;
The preceding steps are returned to step 1.
4) The current position does not meet the conditions:
If the current column is not the last column and the current column is set as the next column, return to step 2;
If the current column is the last column, backtrack: if the current row is already the first row, the algorithm exits. Otherwise, the chessboard of the current row and the following rows is cleared. Then, the current row is set to the previous row, and the current column is set to the next location of the current row to be tested. Step 2 is returned;
The basic principle of the algorithm is as above, but the difference is that the data structure is different, and the methods for checking whether a location meets the conditions are also different. To improve efficiency, there are various optimization strategies, such as multithreading and multi-allocation of memory to represent the board.

When solving this problem, you can split it into several small problems. The first is how to determine whether two queens can attack each other on the board. When we first came into contact with this issue, we first thought of storing the board as a two-dimensional array, then, when the Queen needs to be placed in column j of row I, based on the Problem description, first determine whether there is a queen in row I, because each row has only one queen, this judgment can also be omitted, and then judge whether there is a queen in column j. This is also very simple. Finally, you need to judge whether there is a queen on the same diagonal line. This method requires two judgments, in general, it is not difficult to set the direction of the positive and negative diagonal lines. But after writing it, I always feel very stupid, because this function is used too many times in the question of Queen N, and this is very inefficient, I personally feel very uncomfortable. I was surprised when I checked others' implementations online. The Titans used a one-dimensional array to store the board. It was easy to determine whether there were any queens at a certain position that could attack each other. The details are as follows:
Store the Board as an N-dimensional array a [N]. The value of the I element in the array represents the Queen position of the I line, in this way, the space size of the problem can be reduced to one-dimensional O (N). It is also very easy to determine whether a conflict exists. First, each line has only one queen, and only occupies the position of one element in the array. Row conflicts do not exist, followed by column conflicts, determine whether a [I] is equal to the column j where the queen is to be placed. As for diagonal line conflicts, we can observe that the positions of all the queens conflicting on the diagonal lines are regular, that is, the absolute values of the columns where they are located are equal, that is, | row-I | = | col-a [I] |. The problem of whether the queen can be placed in a certain position has been solved.
Which of the following methods can be used to find all the solutions of Queen N. As mentioned above, this problem is a classic application of the backtracking method, so we can use the backtracking method to solve this problem. There are two ways to implement it: recursion and non-recursion. The recursive method is relatively simple. The general idea is as follows:Copy codeThe Code is as follows: void queen (int row)
{
If (n = row) // if the result is found, the result is printed.
Print_result ();
Else {
For (k = 0 to N) {// test each column in the row
If (can_place (row, k ){
Place (row, k); // place the queen
Queen (row + 1); // continue to test the next row
}
}
}
}

This method, because after detecting row I, if a position j can be placed in the Queen's position is found, the method recursively detects the next row, after the detection is complete, the j + 1 column in row I will continue to be detected, so we can find all the solutions of Queen N.
However, recursion is generally less efficient. Next we will focus on the non-Recursive Implementation of this issue.
A key issue of non-recursive methods is how to trace back and how to trace back. The program first detects each row in N rows to find the location where the queen can be placed in the row. The specific method is to detect each column of the row to see if the queen can be placed, if possible, place a queen in the column and then continue to detect the Queen position in the next row. If no columns have been detected and the Queen columns can be placed, you should trace back and move the location of the queen from the last row to the next column, if no location is found after the last row of the queen moves, continue to trace back until a row finds the Queen's location or goes back to the first line, if the queen of the first line cannot find the location where the queen can be placed, it means that all solutions have been found and terminated. If this row is already the last row, after the row is detected, if the location of the Queen is found, a result is found and printed. But at this time, we cannot end the program here, because we are looking for all the solutions to the N queen's problem. At this time, we should clear the queen of this row, continue the test from the next column where the queen columns are currently placed.
The complete code is as follows:Copy codeThe Code is as follows :/**
* The backtracking method solves the N queen problem
* Uses a one-dimensional array to represent the location of the Queen.
* The subscript of the array indicates the row of the Queen.
* The value of the array element indicates the column where the queen is located.
* In this way, all the queens are not in the same line, so the line conflict does not exist.
* Date: 2011-08-03
* Author: liuzhiwei
**/
# Include <stdio. h>
# Include <stdlib. h>
# Include <math. h>
# Define QUEEN 8 // Number of queens
# Define INITIAL-10000 // the INITIAL value of the Board
Int a [QUEEN]; // a one-dimensional array indicates the chessboard
Void init () // initialize the chessboard
{
Int * p;
For (p = a; p <a + QUEEN; ++ p)
{
* P = INITIAL;
}
}
Int valid (int row, int col) // determines whether the col column of the row can be placed in the queen
{
Int I;
For (I = 0; I <QUEEN; ++ I) // scan the Board
{
If (a [I] = col | abs (I-row) = abs (a [I]-col) // you can determine the conflict between a column and a diagonal line.
Return 0;
}
Return 1;
}
Void print () // print a group of solutions for output N queens
{
Int I, j;
For (I = 0; I <QUEEN; ++ I)
{
For (j = 0; j <QUEEN; ++ j)
{
If (a [I]! = J) // a [I] is the initial value.
Printf ("% c ",'.');
Else // a [I] indicates that the queen can be placed in column a [I] of row I.
Printf ("% c ",'#');
}
Printf ("\ n ");
}
For (I = 0; I <QUEEN; ++ I)
Printf ("% d", a [I]);
Printf ("\ n ");
Printf ("-------------------------------- \ n ");
}
Void queen () // N queen Program
{
Int n = 0;
Int I = 0, j = 0;
While (I <QUEEN)
{
While (j <QUEEN) // detects each column of row I to see if the QUEEN can be placed
{
If (valid (I, j) // This location can be placed as a queen
{
A [I] = j; // place the Queen in line I
J = 0; // after the queen is placed in row I, You need to continue to detect the Queen position in the next row. Therefore, we need to clear j and start column-by-column detection from column 0th in the next row.
Break;
}
Else
{
+ + J; // continue to test the next column
}
}
If (a [I] = INITIAL) // row I does not find the location where the queen can be placed
{
If (I = 0) // go back to the first line and still cannot find the place where the queen can be placed, it means that all solutions have been found and the program is terminated.
Break;
Else // The columns that can be placed in the queen are not found.
{
-- I;
J = a [I] + 1; // move back the position of the Queen of the last row
A [I] = INITIAL; // clear the location of the Queen of the last row and re-test
Continue;
}
}
If (I = QUEEN-1) // find a QUEEN location in the last row, which indicates that a result is found and printed.
{
Printf ("answer % d: \ n", ++ n );
Print ();
// You cannot end the program here because we are looking for all solutions to Queen N's problem. In this case, we should clear the queen of the row and continue the probe from the next column where the queen columns are currently placed.
/_ Sleep (600 );
J = a [I] + 1; // continue the test from the next column in the last row where the queen columns are placed
A [I] = INITIAL; // clear the Queen location of the last row
Continue;
}
++ I; // continue to detect the location of the Queen in the next row
}
}
Int main (void)
{
Init ();
Queen ();
System ("pause ");
Return 0;
}

The following code is similar to the above Code, but has made some changes .. When the above function judges the validity of a certain position on the board, the QUEEN in the valid function can be changed to row. You only need to compare the previous row with the row, you do not need to compare all rows with the row... The check function in the following code shows that the number of loops is k rather than the number of Queens...Copy codeThe Code is as follows: # include "iostream"
# Include "cmath"
Using namespace std;
# Define Max 20 // defines the maximum value of the Board
Int a [Max];
Int show (int S) // defines the output function
{
Int I, p, q;
Int B [Max] [Max] = {0}; // define and initialize B [] [] Output Array
For (I = 1; I <= S; I ++) // output the array coordinates of a [I] in the order of column I
{
B [I] [a [I] = 1;
Printf ("(% d, % d) \ t", I, a [I]);
}
Printf ("\ n ");
For (p = 1; p <= S; p ++) // indicate the location of the Queen in the order of p in the horizontal column of the Board.
{
For (q = 1; q <= S; q ++)
{
If (B [p] [q] = 1) // place a queen's pawn in column q of row p
Printf ("● ");
Else
Printf ("○ ");
}
Printf ("\ n ");
}
Return 0;
}
Int check (int k) // defines the check function
{
Int I;
For (I = 1; I <k; I ++) // compare the k row with the first 1st ~~ K-1 line judgment
{
If (a [I] = a [k]) | (a [I]-a [k] = k-I) | (a [I]-a [k] = I-k) // check whether multiple queens are in the same line.
{
Return 0;
}
}
Return 1;
}
Void check_m (int num) // defines the Function
{
Int k = 1, count = 0;
Printf ("The possible configuration of N queens are: \ n ");
A [k] = 1;
While (k> 0)
{
If (k <= num & a [k] <= num) // starting from the position in the first column of row k, select a proper position for the subsequent pawns
{
If (check (k) = 0) // The a [k] column of row k cannot be placed as queen
{
A [k] ++; // continue to test the next column of the current row: a [k] + 1
}
Else
{
K ++; // The location of row K has been determined. Continue to find the location of the queen of row k + 1.
A [k] = 1; // search from the first column
}
}
Else
{
If (k> num) // if the output array is satisfied, the output Array
{
Count ++;
Printf ("[% d]:", count );
Show (num); // call the output function show ()
}
// If k> num, the following two lines of code will be executed, because although we have found a solution to Queen N's problem, we need to trace back all the solutions, continue the probe from the next column where the current Queen is placed
// If a [k]> num, it will execute the following two lines of code, that is, the current row does not find the location where the queen can be placed, so it will be traced back, continue the detection in the next column of the Queen's position in the previous row
K --; // if the position of the chess piece does not meet the requirements, return to the previous step.
A [k] ++; // continue testing the position of the next column
}
}
Printf ("The count is: % d \ n", count );
}
Int main (void)
{
Int N, d;
// System ("color 2a ");
Do
{
Printf ("******************** N queen's problem system ************* * ******* \ n ");
Printf ("1. Four queens question \ n ");
Printf ("2. Eight queens question \ n ");
Printf ("3. N queen question (N <20) \ n ");
Printf ("4. Exit \ n ");
Printf ("************************************* * **************** \ n ");
Printf ("\ n select the required operation from Number 1 to number 4 \ n");/* indicates the input option */
Printf ("Enter the function options you want to select :__ \ n ");
Scanf ("% d", & d );
Switch (d)
{
Case 1:
Check_m (4); // 4 Queen's question
Break;
Case 2:
Check_m (8); // 8 Queen's question
Break;
Case 3:
Printf ("Enter N value :_");
Fflush (stdin); // clear the buffer
Scanf ("% d", & N );
Printf ("\ n ");
If (N> 0 & N <20)
{
Check_m (N); // N queen question
Break;
}
Else
{
Printf ("input error, please input again :");
Printf ("\ n ");
Break;
}
Case 4:
Exit (0); // end of the program
}
} While (1 );
System ("pause ");
Return 0;
}

Recursive solution:Copy codeThe Code is as follows: # include <stdio. h>
# Include <stdlib. h>
Const int N = 20; // maximum number of Queens allowed
Int q [N]; // The row number of each queen
Int cont = 0; // count the number of obtained results
// Output a solution
Void print (int n)
{
Int I, j;
Cont ++;
Printf ("% d solution:", cont );
For (I = 1; I <= n; I ++)
Printf ("(% d, % d)", I, q [I]);
Printf ("\ n ");
For (I = 1; I <= n; I ++) // line
{
For (j = 1; j <= n; j ++) // Column
{
If (q [I]! = J)
Printf ("x ");
Else
Printf ("Q ");
}
Printf ("\ n ");
}
}
// Check whether the queen can be placed in column k of row I
Int find (int I, int k)
{
Int j = 1;
While (j <I) // j = 1 ~ The I-1 is the row that has been placed for the queen
{
// Whether the queen of Row j is in the k column or (j, q [j]) or (I, k) on the diagonal line
If (q [j] = k | abs (j-I) = abs (q [j]-k ))
Return 0;
J ++;
}
Return 1;
}
// Place the Queen on the board
Void place (int k, int n)
{
Int j;
If (k> n)
Print (n );
Else
{
For (j = 1; j <= n; j ++) // test each column in row k.
{
If (find (k, j ))
{
Q [k] = j;
Place (k + 1, n); // recursion always completes the next task when the previous task is successfully completed.
}
}
}
}
Int main (void)
{
Int n;
Printf ("Enter the number of Queens (n <= 20), n = :");
Scanf ("% d", & n );
If (n> 20)
Printf ("n value is too large to be solved! \ N ");
Else
{
Printf ("% d Queen Problem Solving (number of rows of the Queen in each column): \ n", n );
Place (1, n); // the problem starts from the initial state.
Printf ("\ n ");
}
System ("pause ");
Return 0;
}

Ii. Use bitwise operations to solve Queen N's efficient algorithms
The core code is as follows:Copy codeCode: void test (int row, int ld, int rd)
{
Int pos, p;
If (row! = Upperlim)
{
Pos = upperlim &(~ (Row | ld | rd ));
While (pos)
{
P = pos &(~ Pos + 1 );
Pos = pos-p;
Test (row | p, (ld | p) <1, (rd | p)> 1 );
}
}
Else
++ Ans;
}

Initialization: upperlim = (1 <n)-1; Ans = 0;
Call parameter: test (0, 0, 0 );
Like a common algorithm, this is a recursive function. The program searches for places where the queen can be placed in a row. The function has three parameters: row, ld, and rd, respectively, indicating which parts of the row cannot be placed under the conditions of the vertical column and two diagonal lines. The conflict location on the row is represented by 1 in row, ld, and rd. Combine the three of them to obtain all the prohibited bits of the row. After the reverse operation, all the positions that can be placed are obtained (expressed by pos ).
P = pos &(~ Pos + 1) the result is the rightmost one. In this way, p indicates a certain position of the row that can be put into the sub-account, removes it from the pos, and recursively calls the test process.
Note the changes of the three parameters during recursive calling. Each parameter is added with a forbidden bit, but the effect of the two diagonal lines on the next line needs to be translated by one. Finally, if row = upperlim is found at a time after recursion, it means that all n queens are put in, and the number of solutions found is increased by one.

Note:
Upperlime: = (1 <n)-1 generates a binary number consisting of n numbers of 1.
This program is searched from top to bottom.
Pos &-pos indicates that the rightmost 1 is used to form a binary number, which is equivalent to pos &(~ Pos + 1), because after the inverse is obtained, all numbers are the opposite (how to listen like nonsense), plus 1, that is, to change the second bit. If the number of low positions is 1, this 1 will be added, and it will continue to enter 0. When doing this operation, it will overlap with the 1 corresponding to the original number. For example:
Original number 0 0 0 1 0 0 0 original number 0 1 0 0 1 1 1
Take the inverse 1 1 1 1 0 1 1 1 1 take the inverse 1 0 1 0 1 0 0
Add 1 1 1 1 1 0 0 0 + 1 1 0 1 0 1 0 1 1
And 0 0 0 0 1 0 0 0 and 0 0 0 0 0 0 1
Here, the bitwise and addition of 1 is the complement code, and operation and negative number, namely the bitwise and complement and operation.
(Ld | p) <1 is because the placeholder caused by ld needs to be shifted right in the next row;
(Rd | p)> 1 is because the placeholder caused by rd needs to be shifted left in the next row.
Ld rd row must be compared with upperlime. The result is that n numbers are valid from the minimum number of digits, because ld shifted right in the previous operation, if not, the positions other than n are treated as valid bits.
After the pos has completed the task, we need to subtract p because?
While loop is because?
When a certain layer of search is performed, all the positions that can be placed are stored in the pos. To find all solutions, all the positions that can be placed must be traversed, and each point that passes through must be deleted, otherwise it will become an endless loop!
This is the most efficient algorithm recognized as Queen N.
The complete code is as follows:Copy codeThe Code is as follows :/*
** The fastest recursive solution for n queen
** N Queens Problem
** Test-backtracking algorithm and Recursive Implementation
*/
# Include "iostream"
Using namespace std;
# Include "time. h"

// Sum is used to record the number of different la s that have been successfully placed by the Queen; upperlim is used to mark that all columns have been placed by the Queen.
Long sum = 0, upperlim = 1;

// Test the algorithm from the rightmost column.
Void test (long row, long ld, long rd)
{
If (row! = Upperlim)
{
// Perform the "or" operation on row, ld, and rd to obtain all columns that can be placed in the Queen's position. The corresponding bits are 0,
// Then retrieve the full number of "and" on the backend to obtain all the positions where the current Queen can be placed. The corresponding column is changed to 1.
// Determine which columns can be placed as the Queen
Long pos = upperlim &~ (Row | ld | rd );
While (pos) // 0 -- there is no place for the Queen to go back
{
// Copy the bit with 1 at the rightmost of the pos, and set the remaining bit to 0.
// Obtain the rightmost column of the Queen.
Long p = pos &-pos;

// Clears the bits whose rightmost position is 1.
// This is used to obtain the rightmost available columns for the next time,
// The program will go back to this position to continue testing in the future
Pos-= p;

// Row + p: set the current column to 1, indicating to record the column placed by the Queen.
// (Ld + p) <1, marking the adjacent columns on the left of the current queen cannot be placed by the next queen.
// (Ld + p)> 1. The column adjacent to the right of the current queen cannot be placed by the next queen.
// The shift operation here is actually the limit on the diagonal line of the record, just because the problems are all
// Solve the problem in a row grid, so the column restriction is acceptable. Obviously, with the shift
// Before each selection column, an existing queen in the N × N mesh is directed at its diagonal line.
// All restrictions are recorded.
Test (row + p, (ld + p) <1, (rd + p)> 1 );
}
}
Else
{
// All the bits of row are 1, that is, a successful layout is found.
Sum ++;
}
}

Int main (int argc, char * argv [])
{
Time_t tm;
Int n = 16;

If (argc! = 1)
N = atoi (argv [1]);
Tm = time (0 );

// The maximum integer value is 32 bits,
// If you want to solve the Queen Problem with N greater than 32, you need
// Store data with a bitset Data Structure
If (n <1) | (n> 32 ))
{
Printf ("can only calculate between 1-32 \ n ");
Exit (-1 );
}
Printf ("% d \ n", n );

// N queens only need to be stored in N bits. If a column in N has a queen, the corresponding bit is set to 1.
Upperlim = (upperlim <n)-1;

Test (0, 0, 0 );
Printf ("% ld in total, calculation time % d seconds \ n", sum, (int) (time (0)-tm ));
System ("pause ");
Return 0;
}

The above code is easy to understand, but I think the core is the design of the data structure used by the test-backtracking algorithm.
The program uses recursion, that is, the automatic tracing function provided by the compilation system.
The core of the algorithm: The bit Array is used instead of the int or bool array to store the occupied or available information of the current grid. From this, we can see that the N queens need N-bit representation.
Clever: In the past, we had to move the Queen in an N * N square mesh to perform TEST backtracking, observe and record the information of the grids on the left and right sides of a grid at each step. If bit is used for information storage, you can just perform a test backtrack in a row of grids (one row × N columns). The limitations on the diagonal lines are classified as column restrictions.
The program mainly needs the following three bit arrays, each corresponding to a column of the grid, in C is to take a certain part of the integer continuous bit. Row is used to record which columns are currently unavailable, that is, which columns are occupied by the queen, corresponding to 1. Ld and rd also record which columns are currently unavailable, but do not indicate that they are occupied by the queen. Instead, they indicate that they will be eaten by the existing queen on the diagonal line. After the three-digit group performs the "or" operation, it indicates which other positions can be placed as the new queen, and the corresponding 0 positions can be placed as the new queen. As shown in figure 8. The first step is to solve the Queen's problem:
Row: [] [] [] [] [] [] [] [] [*]
Ld: [] [] [] [] [] [] [*] []
Rd: [] [] [] [] [] [] [] [] []
--------------------------------------
Row | ld | rd: [] [] [] [] [] [] [*] [*]
The testing process for all the next locations is implemented through bitwise operations, which borrow the advantages of the C language. For details, see the code comments.
Considering the symmetry of the N × N board, this algorithm can greatly improve the efficiency of N!
Bit operations-a new understanding of the Optimization Algorithm
This is the fastest Algorithm for the N queen problem found in csdn. After reading it for a while, I understand that this algorithm is clever and I think there are two:
1. Previously, we used arrays to describe the state, and this algorithm uses the correct bit to describe the state. The computing speed can be greatly improved. In the future, we can refer to this example for writing programs to describe the state variables, will make your program run faster
2. To describe the positions that can be placed in each row, only the three variables "row, ld, and rd" are used to describe the fields. This makes the program look concise.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.