mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-27 15:01:08 +00:00
N queens math (#2175)
* add new file for another solution to the n queens problem * Add the code for the algorithm, add comments and add at the top a general explanation * Update backtracking/n_queens_math.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update backtracking/n_queens_math.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update backtracking/n_queens_math.py Co-authored-by: Christian Clauss <cclauss@me.com> * Update backtracking/n_queens_math.py Co-authored-by: Christian Clauss <cclauss@me.com> * No newline at the end of the file * Type hints * whitespaces fixed * Fixed whitespaces * Add type hints * CodeSpell fixed * update * All changes made except changing the board variable to local * Add doctest * Update * Update * Update * Update n_queens_math.py Co-authored-by: Christian Clauss <cclauss@me.com>
This commit is contained in:
parent
b6ca263983
commit
05c14c6be8
165
backtracking/n_queens_math.py
Normal file
165
backtracking/n_queens_math.py
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
r"""
|
||||||
|
Problem:
|
||||||
|
|
||||||
|
The n queens problem is of placing N queens on a N * N chess board such that no queen
|
||||||
|
can attack any other queens placed on that chess board. This means that one queen
|
||||||
|
cannot have any other queen on its horizontal, vertical and diagonal lines.
|
||||||
|
|
||||||
|
Solution:
|
||||||
|
|
||||||
|
To solve this problem we will use simple math. First we know the queen can move in all
|
||||||
|
the possible ways, we can simplify it in this: vertical, horizontal, diagonal left and
|
||||||
|
diagonal right.
|
||||||
|
|
||||||
|
We can visualize it like this:
|
||||||
|
|
||||||
|
left diagonal = \
|
||||||
|
right diagonal = /
|
||||||
|
|
||||||
|
On a chessboard vertical movement could be the rows and horizontal movement could be
|
||||||
|
the columns.
|
||||||
|
|
||||||
|
In programming we can use an array, and in this array each index could be the rows and
|
||||||
|
each value in the array could be the column. For example:
|
||||||
|
|
||||||
|
. Q . . We have this chessboard with one queen in each column and each queen
|
||||||
|
. . . Q can't attack to each other.
|
||||||
|
Q . . . The array for this example would look like this: [1, 3, 0, 2]
|
||||||
|
. . Q .
|
||||||
|
|
||||||
|
So if we use an array and we verify that each value in the array is different to each
|
||||||
|
other we know that at least the queens can't attack each other in horizontal and
|
||||||
|
vertical.
|
||||||
|
|
||||||
|
At this point we have that halfway completed and we will treat the chessboard as a
|
||||||
|
Cartesian plane. Hereinafter we are going to remember basic math, so in the school we
|
||||||
|
learned this formula:
|
||||||
|
|
||||||
|
Slope of a line:
|
||||||
|
|
||||||
|
y2 - y1
|
||||||
|
m = ----------
|
||||||
|
x2 - x1
|
||||||
|
|
||||||
|
This formula allow us to get the slope. For the angles 45º (right diagonal) and 135º
|
||||||
|
(left diagonal) this formula gives us m = 1, and m = -1 respectively.
|
||||||
|
|
||||||
|
See::
|
||||||
|
https://www.enotes.com/homework-help/write-equation-line-that-hits-origin-45-degree-1474860
|
||||||
|
|
||||||
|
Then we have this another formula:
|
||||||
|
|
||||||
|
Slope intercept:
|
||||||
|
|
||||||
|
y = mx + b
|
||||||
|
|
||||||
|
b is where the line crosses the Y axis (to get more information see:
|
||||||
|
https://www.mathsisfun.com/y_intercept.html), if we change the formula to solve for b
|
||||||
|
we would have:
|
||||||
|
|
||||||
|
y - mx = b
|
||||||
|
|
||||||
|
And like we already have the m values for the angles 45º and 135º, this formula would
|
||||||
|
look like this:
|
||||||
|
|
||||||
|
45º: y - (1)x = b
|
||||||
|
45º: y - x = b
|
||||||
|
|
||||||
|
135º: y - (-1)x = b
|
||||||
|
135º: y + x = b
|
||||||
|
|
||||||
|
y = row
|
||||||
|
x = column
|
||||||
|
|
||||||
|
Applying this two formulas we can check if a queen in some position is being attacked
|
||||||
|
for another one or vice versa.
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
def depth_first_search(
|
||||||
|
possible_board: List[int],
|
||||||
|
diagonal_right_collisions: List[int],
|
||||||
|
diagonal_left_collisions: List[int],
|
||||||
|
boards: List[List[str]],
|
||||||
|
n: int,
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
>>> boards = []
|
||||||
|
>>> depth_first_search([], [], [], boards, 4)
|
||||||
|
>>> for board in boards:
|
||||||
|
... print(board)
|
||||||
|
['. Q . . ', '. . . Q ', 'Q . . . ', '. . Q . ']
|
||||||
|
['. . Q . ', 'Q . . . ', '. . . Q ', '. Q . . ']
|
||||||
|
"""
|
||||||
|
|
||||||
|
""" Get next row in the current board (possible_board) to fill it with a queen """
|
||||||
|
row = len(possible_board)
|
||||||
|
|
||||||
|
"""
|
||||||
|
If row is equal to the size of the board it means there are a queen in each row in
|
||||||
|
the current board (possible_board)
|
||||||
|
"""
|
||||||
|
if row == n:
|
||||||
|
"""
|
||||||
|
We convert the variable possible_board that looks like this: [1, 3, 0, 2] to
|
||||||
|
this: ['. Q . . ', '. . . Q ', 'Q . . . ', '. . Q . ']
|
||||||
|
"""
|
||||||
|
possible_board = [". " * i + "Q " + ". " * (n - 1 - i) for i in possible_board]
|
||||||
|
boards.append(possible_board)
|
||||||
|
return
|
||||||
|
|
||||||
|
""" We iterate each column in the row to find all possible results in each row """
|
||||||
|
for col in range(n):
|
||||||
|
|
||||||
|
"""
|
||||||
|
We apply that we learned previously. First we check that in the current board
|
||||||
|
(possible_board) there are not other same value because if there is it means
|
||||||
|
that there are a collision in vertical. Then we apply the two formulas we
|
||||||
|
learned before:
|
||||||
|
|
||||||
|
45º: y - x = b or 45: row - col = b
|
||||||
|
135º: y + x = b or row + col = b.
|
||||||
|
|
||||||
|
And we verify if the results of this two formulas not exist in their variables
|
||||||
|
respectively. (diagonal_right_collisions, diagonal_left_collisions)
|
||||||
|
|
||||||
|
If any or these are True it means there is a collision so we continue to the
|
||||||
|
next value in the for loop.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
col in possible_board
|
||||||
|
or row - col in diagonal_right_collisions
|
||||||
|
or row + col in diagonal_left_collisions
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
|
||||||
|
""" If it is False we call dfs function again and we update the inputs """
|
||||||
|
depth_first_search(
|
||||||
|
possible_board + [col],
|
||||||
|
diagonal_right_collisions + [row - col],
|
||||||
|
diagonal_left_collisions + [row + col],
|
||||||
|
boards,
|
||||||
|
n,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def n_queens_solution(n: int) -> None:
|
||||||
|
boards = []
|
||||||
|
depth_first_search([], [], [], boards, n)
|
||||||
|
|
||||||
|
""" Print all the boards """
|
||||||
|
for board in boards:
|
||||||
|
for column in board:
|
||||||
|
print(column)
|
||||||
|
print("")
|
||||||
|
|
||||||
|
print(len(boards), "solutions were found.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import doctest
|
||||||
|
|
||||||
|
doctest.testmod()
|
||||||
|
n_queens_solution(4)
|
Loading…
Reference in New Issue
Block a user