Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit bffacf0

Browse files
committed
Add nQueens bitwise solution.
1 parent 2015931 commit bffacf0

File tree

4 files changed

+156
-60
lines changed

4 files changed

+156
-60
lines changed

‎src/algorithms/uncategorized/n-queens/README.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,46 @@ and return false.
6565
backtracking.
6666
```
6767

68+
## Bitwise Solution
69+
70+
Bitwise algorithm basically approaches the problem like this:
71+
72+
- Queens can attack diagonally, vertically, or horizontally. As a result, there
73+
can only be one queen in each row, one in each column, and at most one on each
74+
diagonal.
75+
- Since we know there can only one queen per row, we will start at the first row,
76+
place a queen, then move to the second row, place a second queen, and so on until
77+
either a) we reach a valid solution or b) we reach a dead end (ie. we can't place
78+
a queen such that it is "safe" from the other queens).
79+
- Since we are only placing one queen per row, we don't need to worry about
80+
horizontal attacks, since no queen will ever be on the same row as another queen.
81+
- That means we only need to check three things before placing a queen on a
82+
certain square: 1) The square's column doesn't have any other queens on it, 2)
83+
the square's left diagonal doesn't have any other queens on it, and 3) the
84+
square's right diagonal doesn't have any other queens on it.
85+
- If we ever reach a point where there is nowhere safe to place a queen, we can
86+
give up on our current attempt and immediately test out the next possibility.
87+
88+
First let's talk about the recursive function. You'll notice that it accepts
89+
3 parameters: `leftDiagonal`, `column`, and `rightDiagonal`. Each of these is
90+
technically an integer, but the algorithm takes advantage of the fact that an
91+
integer is represented by a sequence of bits. So, think of each of these
92+
parameters as a sequence of `N` bits.
93+
94+
Each bit in each of the parameters represents whether the corresponding location
95+
on the current row is "available".
96+
97+
For example:
98+
- For `N=4`, column having a value of `0010` would mean that the 3rd column is
99+
already occupied by a queen.
100+
- For `N=8`, ld having a value of `00011000` at row 5 would mean that the
101+
top-left-to-bottom-right diagonals that pass through columns 4 and 5 of that
102+
row are already occupied by queens.
103+
104+
Below is a visual aid for `leftDiagonal`, `column`, and `rightDiagonal`.
105+
106+
![](http://gregtrowbridge.com/content/images/2014/Jul/Screenshot-from-2014年06月17日-19-46-20.png)
107+
68108
## References
69109

70110
- [Wikipedia](https://en.wikipedia.org/wiki/Eight_queens_puzzle)
@@ -73,5 +113,5 @@ and return false.
73113
- [On YouTube by Tushar Roy](https://www.youtube.com/watch?v=xouin83ebxE&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8)
74114
- Bitwise Solution
75115
- [Wikipedia](https://en.wikipedia.org/wiki/Eight_queens_puzzle)
76-
- [GREG TROWBRIDGE](http://gregtrowbridge.com/a-bitwise-solution-to-the-n-queens-problem-in-javascript/)
77-
-[Backtracking Algorithms in MCPL using Bit Patterns and Recursion](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.51.7113&rep=rep1&type=pdf)
116+
- [Solution by Greg Trowbridge](http://gregtrowbridge.com/a-bitwise-solution-to-the-n-queens-problem-in-javascript/)
117+

‎src/algorithms/uncategorized/n-queens/__test__/nQeensBitwise.test.js

Lines changed: 0 additions & 26 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import nQueensBitwise from '../nQueensBitwise';
2+
3+
describe('nQueensBitwise', () => {
4+
it('should have solutions for 4 to N queens', () => {
5+
expect(nQueensBitwise(4)).toBe(2);
6+
expect(nQueensBitwise(5)).toBe(10);
7+
expect(nQueensBitwise(6)).toBe(4);
8+
expect(nQueensBitwise(7)).toBe(40);
9+
expect(nQueensBitwise(8)).toBe(92);
10+
expect(nQueensBitwise(9)).toBe(352);
11+
expect(nQueensBitwise(10)).toBe(724);
12+
expect(nQueensBitwise(11)).toBe(2680);
13+
});
14+
});
Lines changed: 100 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,101 @@
1-
export default function (n) {
2-
// Keeps track of the # of valid solutions
3-
let count = 0;
4-
5-
// Helps identify valid solutions
6-
const done = (2 ** n) - 1;
7-
8-
// Checks all possible board configurations
9-
const innerRecurse = (ld, col, rd) => {
10-
// All columns are occupied,
11-
// so the solution must be complete
12-
if (col === done) {
13-
count += 1;
14-
return;
15-
}
16-
17-
// Gets a bit sequence with "1"s
18-
// whereever there is an open "slot"
19-
let poss = ~(ld | rd | col);
20-
21-
// Loops as long as there is a valid
22-
// place to put another queen.
23-
while (poss & done) {
24-
const bit = poss & -poss;
25-
poss -= bit;
26-
innerRecurse((ld | bit) >> 1, col | bit, (rd | bit) << 1);
27-
}
28-
};
29-
30-
innerRecurse(0, 0, 0);
31-
32-
return count;
1+
/**
2+
* Checks all possible board configurations.
3+
*
4+
* @param {number} boardSize - Size of the squared chess board.
5+
* @param {number} leftDiagonal - Sequence of N bits that show whether the corresponding location
6+
* on the current row is "available" (no other queens are threatening from left diagonal).
7+
* @param {number} column - Sequence of N bits that show whether the corresponding location
8+
* on the current row is "available" (no other queens are threatening from columns).
9+
* @param {number} rightDiagonal - Sequence of N bits that show whether the corresponding location
10+
* on the current row is "available" (no other queens are threatening from right diagonal).
11+
* @param {number} solutionsCount - Keeps track of the number of valid solutions.
12+
* @return {number} - Number of possible solutions.
13+
*/
14+
function nQueensBitwiseRecursive(
15+
boardSize,
16+
leftDiagonal = 0,
17+
column = 0,
18+
rightDiagonal = 0,
19+
solutionsCount = 0,
20+
) {
21+
// Keeps track of the number of valid solutions.
22+
let currentSolutionsCount = solutionsCount;
23+
24+
// Helps to identify valid solutions.
25+
// isDone simply has a bit sequence with 1 for every entry up to the Nth. For example,
26+
// when N=5, done will equal 11111. The "isDone" variable simply allows us to not worry about any
27+
// bits beyond the Nth.
28+
const isDone = (2 ** boardSize) - 1;
29+
30+
// All columns are occupied (i.e. 0b1111 for boardSize = 4), so the solution must be complete.
31+
// Since the algorithm never places a queen illegally (ie. when it can attack or be attacked),
32+
// we know that if all the columns have been filled, we must have a valid solution.
33+
if (column === isDone) {
34+
return currentSolutionsCount + 1;
35+
}
36+
37+
// Gets a bit sequence with "1"s wherever there is an open "slot".
38+
// All that's happening here is we're taking col, ld, and rd, and if any of the columns are
39+
// "under attack", we mark that column as 0 in poss, basically meaning "we can't put a queen in
40+
// this column". Thus all bits position in poss that are '1's are available for placing
41+
// queen there.
42+
let availablePositions = ~(leftDiagonal | rightDiagonal | column);
43+
44+
// Loops as long as there is a valid place to put another queen.
45+
// For N=4 the isDone=0b1111. Then if availablePositions=0b0000 (which would mean that all places
46+
// are under threatening) we must stop trying to place a queen.
47+
while (availablePositions & isDone) {
48+
// firstAvailablePosition just stores the first non-zero bit (ie. the first available location).
49+
// So if firstAvailablePosition was 0010, it would mean the 3rd column of the current row.
50+
// And that would be the position will be placing our next queen.
51+
//
52+
// For example:
53+
// availablePositions = 0b01100
54+
// firstAvailablePosition = 100
55+
const firstAvailablePosition = availablePositions & -availablePositions;
56+
57+
// This line just marks that position in the current row as being "taken" by flipping that
58+
// column in availablePositions to zero. This way, when the while loop continues, we'll know
59+
// not to try that location again.
60+
//
61+
// For example:
62+
// availablePositions = 0b0100
63+
// firstAvailablePosition = 0b10
64+
// 0b0110 - 0b10 = 0b0100
65+
availablePositions -= firstAvailablePosition;
66+
67+
/*
68+
* The operators >> 1 and 1 << simply move all the bits in a bit sequence one digit to the
69+
* right or left, respectively. So calling (rd|bit)<<1 simply says: combine rd and bit with
70+
* an OR operation, then move everything in the result to the left by one digit.
71+
*
72+
* More specifically, if rd is 0001 (meaning that the top-right-to-bottom-left diagonal through
73+
* column 4 of the current row is occupied), and bit is 0100 (meaning that we are planning to
74+
* place a queen in column 2 of the current row), (rd|bit) results in 0101 (meaning that after
75+
* we place a queen in column 2 of the current row, the second and the fourth
76+
* top-right-to-bottom-left diagonals will be occupied).
77+
*
78+
* Now, if add in the << operator, we get (rd|bit)<<1, which takes the 0101 we worked out in
79+
* our previous bullet point, and moves everything to the left by one. The result, therefore,
80+
* is 1010.
81+
*/
82+
currentSolutionsCount += nQueensBitwiseRecursive(
83+
boardSize,
84+
(leftDiagonal | firstAvailablePosition) >> 1,
85+
column | firstAvailablePosition,
86+
(rightDiagonal | firstAvailablePosition) << 1,
87+
solutionsCount,
88+
);
89+
}
90+
91+
return currentSolutionsCount;
92+
}
93+
94+
/**
95+
* @param {number} boardSize - Size of the squared chess board.
96+
* @return {number} - Number of possible solutions.
97+
* @see http://gregtrowbridge.com/a-bitwise-solution-to-the-n-queens-problem-in-javascript/
98+
*/
99+
export default function nQueensBitwise(boardSize) {
100+
return nQueensBitwiseRecursive(boardSize);
33101
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /