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
70 changes: 70 additions & 0 deletions src/homework4-task_queens/queens_optimized.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
def n_queens_bitmask(n):
"""Самое быстрое решение с использованием битовых масок"""
if n < 1:
return 0

def backtrack(row=0, columns=0, diagonals1=0, diagonals2=0):
if row == n:
return 1

count = 0
# Доступные позиции в текущей строке
available_positions = ((1 << n) - 1) & ~(columns | diagonals1 | diagonals2)

while available_positions:
# Берем самую правую доступную позицию
position = available_positions & -available_positions
# Убираем ее из доступных
available_positions -= position

count += backtrack(
row + 1,
columns | position,
(diagonals1 | position) << 1,
(diagonals2 | position) >> 1,
)
Comment on lines +10 to +25
Copy link

Choose a reason for hiding this comment

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

Всю эту логику стоит пояснить подробнее.
Например, что происходит в этих строчках?

columns | position,
(diagonals1 | position) << 1,
(diagonals2 | position) >> 1

Вернувшись к ним через время вы уже и не сможете точно вспомнить.


return count

return backtrack()


def test_bitmask_basic():
"""Тест оптимизированного решения (bitmask)"""
print("ТЕСТИРОВАНИЕ BITMASK РЕШЕНИЯ")
print("=" * 50)

# Известные значения для N ферзей
test_cases = [
(1, 1), # [0]
(2, 0), # нет решений
(3, 0), # нет решений
(4, 2), # 2 решения
(5, 10), # 10 решений
(6, 4), # 4 решения
(7, 40), # 40 решений
(8, 92), # 92 решения
(9, 352), # 352 решения
(10, 724), # 724 решения
]

all_passed = True

for n, expected in test_cases:
result = n_queens_bitmask(n)

if result == expected:
print(f"✓ N={n}: {result} (ожидалось {expected})")
else:
print(f"✗ N={n}: {result} (ожидалось {expected})")
all_passed = False

print("=" * 50)
print(f"РЕЗУЛЬТАТ: {'Все тесты пройдены' if all_passed else 'Есть ошибки'}")

return all_passed
Comment on lines +32 to +65
Copy link

Choose a reason for hiding this comment

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

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



# Запуск теста
if __name__ == "__main__":
test_bitmask_basic()
52 changes: 52 additions & 0 deletions src/homework4-task_queens/queens_perebor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import itertools


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


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

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


def test_placement_small():
"""Тестирование для маленьких N где перебор работает быстро"""
test_cases = {
1: 1, # [0]
2: 0, # нет решений
3: 0, # нет решений
4: 2, # [1,3,0,2] и [2,0,3,1]
5: 10, # известное значение
6: 4, # известное значение
}

print("Тестирование переборного решения:")
print("N\tОжидаемо\tПолучено\tСтатус")
print("-" * 40)

for n, expected in test_cases.items():
if n <= 6: # Ограничиваем для скорости
result = n_queens_placement(n)
status = "✓" if result == expected else "✗"
print(f"{n}\t{expected}\t\t{result}\t\t{status}")


if __name__ == "__main__":
test_placement_small()
75 changes: 75 additions & 0 deletions src/homework4-task_queens/queens_recursive.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
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_n_queens_recursive(n, row=0, board=None, count=None):
"""Рекурсивное решение с backtracking"""
if board is None:
board = [-1] * n
if count is None:
count = [0]
Comment on lines +15 to +16
Copy link

Choose a reason for hiding this comment

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

Зачем тут передавать список count? Можно было бы сделать так, чтобы функция возвращала количество решений из своей ветки, а в основной ветке они суммировались.


if row == n:
# Найдена корректная расстановка
count[0] += 1
return

for col in range(n):
if is_valid_recursive(board, row, col):
board[row] = col
solve_n_queens_recursive(n, row + 1, board, count)
# Backtrack - не нужно явно сбрасывать, так как перезаписываем

return count[0]


def n_queens_recursive(n):
"""Обертка для рекурсивного решения"""
if n < 1:
return 0
return solve_n_queens_recursive(n)


def test_n_queens_recursive_basic():
"""Тест рекурсивного решения на известных значениях"""
print("ТЕСТИРОВАНИЕ РЕКУРСИВНОГО РЕШЕНИЯ")
print("=" * 40)

# Известные значения для N ферзей
test_cases = [
(1, 1), # [0]
(2, 0), # нет решений
(3, 0), # нет решений
(4, 2), # 2 решения
(5, 10), # 10 решений
(6, 4), # 4 решения
(7, 40), # 40 решений
(8, 92), # 92 решения
]

all_passed = True

for n, expected in test_cases:
result = n_queens_recursive(n)

if result == expected:
print(f"✓ N={n}: {result} (ожидалось {expected})")
else:
print(f"✗ N={n}: {result} (ожидалось {expected})")
all_passed = False

print("=" * 40)
print(f"РЕЗУЛЬТАТ: {'Все тесты пройдены' if all_passed else 'Есть ошибки'}")

return all_passed


# Запуск теста
if __name__ == "__main__":
test_n_queens_recursive_basic()