Skip to content
Open
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
78 changes: 78 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# ruff.toml
target-version = "py311"
line-length = 100

# включить все основные правила
select = [
"E", # pycodestyle errors
"W", # pycodestyle warnings
"F", # Pyflakes
"I", # isort
"N", # pep8-naming
"UP", # pyupgrade
"YTT", # flake8-2020
"S", # flake8-bandit
"A", # flake8-builtins
"COM", # flake8-commas
"C4", # flake8-comprehensions
"DTZ", # flake8-datetimez
"T10", # flake8-debugger
"EM", # flake8-errmsg
"EXE", # flake8-executable
"ISC", # flake8-implicit-str-concat
"ICN", # flake8-import-conventions
"G", # flake8-logging-format
"INP", # flake8-no-pep420
"PIE", # flake8-pie
"T20", # flake8-print
"PYI", # flake8-pyi
"PT", # flake8-pytest-style
"Q", # flake8-quotes
"RSE", # flake8-raise
"RET", # flake8-return
"SLF", # flake8-self
"SIM", # flake8-simplify
"TID", # flake8-tidy-imports
"TCH", # flake8-type-checking
"INT", # flake8-gettext
"ARG", # flake8-unused-arguments
"FBT", # flake8-boolean-trap
"B", # flake8-bugbear
"AIR", # flake8-airflow
"PERF", # flake8-perflint
]

# игнорировать правила
ignore = [
"E501", # line too long - handled by formatter
"S101", # assert used - ok in tests
"T201", # print found - sometimes needed
"COM812", # trailing comma missing - not always required
]

# настройки для конкретных файлов
[per-file-ignores]
"__init__.py" = ["F401"] # Unused imports allowed in __init__.py
"tests/**" = ["S101", "SLF001"] # Allow assert and self in tests
"**/migrations/**" = ["ALL"] # Ignore all in migrations

# настройки форматтера
[format]
indent-style = "space"
quote-style = "double"
skip-magic-trailing-comma = false
line-ending = "auto"

# настройки для конкретных правил
[flake8-quotes]
docstring-quotes = "double"
inline-quotes = "double"

[flake8-tidy-imports]
ban-relative-imports = "all"

[isort]
known-first-party = ["myapp"]
lines-after-imports = 2
combine-as-imports = true
split-on-trailing-comma = true
35 changes: 35 additions & 0 deletions src/hw_4-1_queen/complexity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Оценка сложности решений задачи о расстановке ферзей

## 1. Переборное решение (bruteforce)

**Сложность:** O(N! * N²)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для написания формул можно использовать $$: $N! \cdot N^2$


**Обоснование:**
- Генерируются все перестановки из N элементов: O(N!)
- Для каждой перестановки выполняется проверка валидности за O(N²)
- Проверка включает сравнение всех пар ферзей (N*(N-1)/2 сравнений)

**Применение:** Только для очень маленьких N (N ≤ 10)

## 2. Рекурсивное решение (backtracking)

**Сложность:** O(N!)

**Обоснование:**
- В худшем случае перебираются все возможные расстановки
- Отсечение невалидных ветвей сокращает перебор
- Фактическое количество рекурсивных вызовов меньше N!
- Проверка валидности за O(N) на каждом уровне

**Применение:** Работает для N ≤ 15-20 за нормальное время
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Вы уверены, что оно будет работать для 20?...


## 3. Простое решение с множествами

**Сложность:** O(N!)

**Обоснование:**
- Теоретическая сложность остается O(N!)
- Проверка конфликтов через множества работает за O(1)
- Немного медленнее из-за работы с множествами

**Применение:** Работает для N ≤ 15-18
33 changes: 33 additions & 0 deletions src/hw_4-1_queen/hw_4-1_queen_backtracking.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
def is_valid_recursive(board, row, col):
# проверяем, можно ли поставить ферзя на позицию (row, col)
for i in range(row):
# проверка столбца и диагоналей
if board[i] == col or abs(board[i] - col) == abs(i - row):
return False
return True

def solve_recursive(n, row, board, solutions):
# рекурсивно находит все решения
if row == n:
solutions.append(board)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Тут добавляется не копия доски board, а ссылка на один и тот же список. При этом при изменении board в следующих ветках рекурсии, сохраненные решения так же изменятся.
Лучше использовать счетчик, так как копирование board не имеет смысла и требует дополнительных ресурсов.

return

for col in range(n):
if is_valid_recursive(board, row, col):
board[row] = col
# возвращаемся к 1 строке и смотрим следующий столбец
solve_recursive(n, row + 1, board, solutions)

def queens_recursive(n):
# запускаем решение
if n < 1:
return 0

solutions = []
board = [-1] * n # board[i] = столбец ферзя в строке i
solve_recursive(n, 0, board, solutions)
return len(solutions)

# тест
for n in range(1, 11):
print(f"n={n}: {queens_recursive(n)} решений")
41 changes: 41 additions & 0 deletions src/hw_4-1_queen/hw_4-1_queen_best.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
def backtrack(row, columns, diagonal1, diagonal2):
if row == n:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Переменная n не объявлена нигде в функции и не передается как её аргумент. Сейчас код берет n из глобального цикла, но стоит написать queens_simple(10) и он падает:

Traceback (most recent call last):
  File "main.py", line 41, in <module>
    queens_simple(10)
  File "main.py", line 36, in queens_simple
    return backtrack(0, set(), set(), set())
  File "main.py", line 2, in backtrack
    if row == n:
NameError: name 'n' is not defined

return 1

count = 0
for col in range(n):
# вычисляем номера диагоналей
d1 = row - col # диагональ \
d2 = row + col # диагональ /

# проверяем конфликты
if col in columns or d1 in diagonal1 or d2 in diagonal2:
continue

# добавляем ферзя
columns.add(col)
diagonal1.add(d1)
diagonal2.add(d2)

# рекурсивный вызов
count += backtrack(row + 1, columns, diagonal1, diagonal2)

# убираем ферзя (откат)
columns.remove(col)
diagonal1.remove(d1)
diagonal2.remove(d2)

return count


def queens_simple(n):
# рекурсивное решение с множествами
if n < 1:
return 0

return backtrack(0, set(), set(), set())


# тест
for n in range(1, 11):
print(f"n={n}: {queens_simple(n)} решений")
28 changes: 28 additions & 0 deletions src/hw_4-1_queen/hw_4-1_queen_bruteforce.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import itertools

def is_valid_bruteforce(board):
# проверяет, можно ли так поставить ферзей
n = len(board)
for i in range(n):
for j in range(i + 1, n):
# проверка на одну диагональ
if abs(i - j) == abs(board[i] - board[j]):
return False
return True

def queens_bruteforce(n):
# перебор всех возможных расстановок
if n < 1 or n > 10: # ограничение из-за сложности
return 0

count = 0
# генерируем все перестановки (каждый ферзь в уникальной строке и столбце)
for permutation in itertools.permutations(range(n)):
if is_valid_bruteforce(permutation):
count += 1
return count


# тест
for n in range(1, 11):
print(f"n={n}: {queens_bruteforce(n)} решений")