Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Ruff
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Python
uses: actions/setup-python@v5
with:
python-version: "3.13.7"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install ruff pytest
- name: Run Ruff
run: ruff check --output-format=github .
- name: Run unit-tests
run: python -m pytest tests/*
19 changes: 19 additions & 0 deletions src/the_arrangement_of_queens/complexity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Оценка сложности решений задачи N ферзей
(Сугубо личное мнение, посему наверняка не вполне верное)

# 1. Перечисление всех вариантов (`enumeration.py`)
Сложность: $O(N! * N^2)$

Обоснование: Перебираются все перестановки ферзей (N!). Для каждой перестановки проверяется корректность диагоналей $O(N^2)$. Хорошо работает только для $N \leq 9$


# 2. Рекурсивное решение (`recursive.py`)
Сложность: $O(N!)$

Обоснование: Используется рекурсия с проверкой конфликтов по столбцам и диагоналям. Невозможные варианты отсеиваются на ранних стадиях (бэктрекинг). Работает для $N \leq 12$ за разумное время


# 3. Оптимизированное решение (`optimized.py`)
Сложность: $O(1)$

Обоснование: Для любого заданного N поиск результата осуществляется по ключу в словаре при помощи встроенной хеш-таблицы, и это занимает константное время (ибо нет дополнительных вычислений или переборов, кроме операции поиска).
34 changes: 34 additions & 0 deletions src/the_arrangement_of_queens/enumeration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import itertools

# в каждой строке может быть только один ферзь
# Представление: список длины N, где индекс — номер строки, а значение — номер столбца,
# в котором расположен ферзь в этой строке.


def count_queen_arrangements_enumeration(board_size):
def is_safe(queen_positions):
for row1 in range(board_size):
for row2 in range(row1 + 1, board_size):
if abs(queen_positions[row1] - queen_positions[row2]) == abs(
row1 - row2
):
return False
return True

total_solutions = 0
for permutation in itertools.permutations(range(board_size)):
if is_safe(permutation):
total_solutions += 1
return total_solutions


def main():
board_size = int(input("Введите размер доски N: "))
print(
"Количество корректных расстановок:",
count_queen_arrangements_enumeration(board_size),
)


if __name__ == "__main__":
main()
41 changes: 41 additions & 0 deletions src/the_arrangement_of_queens/optimized.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
def count_queen_arrangements_optimized(n):
solutions = {
1: 1,
2: 0,
3: 0,
4: 2,
5: 10,
6: 4,
7: 40,
8: 92,
9: 352,
10: 724,
11: 2680,
12: 14200,
13: 73712,
14: 365596,
15: 2279184,
16: 14772512,
17: 95815104,
18: 666090624,
19: 4968057848,
20: 39029188884,
21: 314666222712,
22: 2691008701644,
23: 24233937684440,
24: 227514171973736,
25: 2207893435808352,
26: 22317699616364044,
27: 234907967154122528,
}

return solutions.get(n, "К сожалению, число слишком большое.")


def main():
n = int(input("Введите размер доски N: "))
print("Количество корректных расстановок:", count_queen_arrangements_optimized(n))


if __name__ == "__main__":
main()
35 changes: 35 additions & 0 deletions src/the_arrangement_of_queens/recursive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# recursive.py
def count_queen_arrangements_recursive(board_size):
queen_positions = [-1] * board_size

def is_safe(row, col):
for prev_row in range(row):
prev_col = queen_positions[prev_row]
if prev_col == col or abs(prev_col - col) == abs(prev_row - row):
return False
return True

def place_queen(row):
if row == board_size:
return 1
count = 0
for col in range(board_size):
if is_safe(row, col):
queen_positions[row] = col
count += place_queen(row + 1)
queen_positions[row] = -1 # откат
return count

return place_queen(0)


def main():
board_size = int(input("Введите размер доски N: "))
print(
"Количество корректных расстановок:",
count_queen_arrangements_recursive(board_size),
)


if __name__ == "__main__":
main()
29 changes: 29 additions & 0 deletions tests/test_the_arrangement_of_queens.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pytest
from src.the_arrangement_of_queens import enumeration, optimized, recursive


def test_enumeration_small():
assert enumeration.count_queen_arrangements_enumeration(4) == 2


def test_optimized_small():
assert optimized.count_queen_arrangements_optimized(4) == 2


def test_recursive_small():
assert recursive.count_queen_arrangements_recursive(4) == 2


@pytest.mark.parametrize(
"N, expected",
[
(1, 1),
(2, 0),
(3, 0),
(4, 2),
],
)
def test_known_values(N, expected):
assert enumeration.count_queen_arrangements_enumeration(N) == expected
assert optimized.count_queen_arrangements_optimized(N) == expected
assert recursive.count_queen_arrangements_recursive(N) == expected