我正在尝试为n皇后问题实现Sosic和Gu算法-它提供了一个称为initial_search()的初始化阶段。
该算法首先在有限的尝试次数内将皇后分配到棋盘上的随机位置(由int(3.08 * n)迭代的循环控制)。对于每次皇后放置尝试,它使用partial_collision函数检查是否存在与先前放置的皇后的任何立即冲突。如果检测到冲突,算法会在不同的随机位置重新尝试放置,直到找到一个有效的放置。这一步的目的是在最小化初始冲突的同时将皇后分布在整个板上。我发现partial_collision()的实现存在问题:在文章中,他们说它应该花费O(1),但是我找不到一种方法来使用对角数组只检查左边的列(实际上函数total_collision()是O(1))。
import random
def queen_search(queens):
while True:
nd, pd = initialize_diagonal_arrays(len(queens))
k = initial_search(queens, nd, pd)
if len(queens) > 200:
final_search(queens, k, nd, pd)
break
else:
final_search_reduced(queens, k, nd, pd)
if board_collision(queens) == 0:
break
def initial_search(queens, nd, pd):
n = len(queens)
queens[:] = list(range(n))
update_diagonal_arrays(queens, nd, pd)
j = 0
# place queens without collisions
for i in range(int(3.08 * n)):
if j == n:
break
m = random.randint(j, n - 1)
swap(queens, j, m, nd, pd)
if partial_collision(queens, j) == 0:
j += 1
else:
swap(queens, j, m, nd, pd)
# place queens with possible collisions
for i in range(j, n):
m = random.randint(i, n - 1)
swap(queens, i, m, nd, pd)
# return the number of queens with possible collisions
return n - j
def final_search(queens, k, nd, pd):
n = len(queens)
it = 0
for i in range(n - k, n):
if total_collisions(queens, i, nd, pd) > 0:
while it < 7000:
j = random.randint(0, n - 1)
swap(queens, i, j, nd, pd)
b = (total_collisions(queens, i, nd, pd) > 0) or (total_collisions(queens, j, nd, pd) > 0)
if b:
swap(queens, i, j, nd, pd)
it += 1
else:
break
def final_search_reduced(queens, k, nd, pd):
n = len(queens)
for i in range(n - k, n):
if total_collisions(queens, i, nd, pd) > 0:
for j in range(n):
swap(queens, i, j, nd, pd)
b = (total_collisions(queens, i, nd, pd) > 0) or (total_collisions(queens, j, nd, pd) > 0)
if b:
swap(queens, i, j, nd, pd)
else:
break
# auxiliary functions
def initialize_diagonal_arrays(n):
neg_diagonal = [0] * (2 * n - 1)
pos_diagonal = [0] * (2 * n - 1)
return neg_diagonal, pos_diagonal
def update_diagonal_arrays(queens, neg_diagonal, pos_diagonal):
n = len(queens)
for i in range(len(queens)):
neg_diagonal[i + queens[i]] += 1
pos_diagonal[i - queens[i] + n - 1] += 1
def swap(queens, a, b, neg_diagonal, pos_diagonal):
original_a, original_b = queens[a], queens[b]
queens[a], queens[b] = queens[b], queens[a]
neg_diagonal[a + original_a] -= 1
neg_diagonal[b + original_b] -= 1
pos_diagonal[a - original_a + len(queens) - 1] -= 1
pos_diagonal[b - original_b + len(queens) - 1] -= 1
neg_diagonal[a + queens[a]] += 1
neg_diagonal[b + queens[b]] += 1
pos_diagonal[a - queens[a] + len(queens) - 1] += 1
pos_diagonal[b - queens[b] + len(queens) - 1] += 1
def partial_collision(queens, i):
return sum(1 for j in range(i) if (i - j) == abs(queens[i] - queens[j]))
def total_collisions(queens, i, neg_diagonal, pos_diagonal):
n = len(queens)
return neg_diagonal[i + queens[i]] + pos_diagonal[i - queens[i] + n - 1] - 2 # Exclude self-collision
def board_collision(queens):
return sum(partial_collision(queens, i) for i in range(len(queens)))
字符串
1条答案
按热度按时间ff29svar1#
你不需要一个单独的
partial_collision
实现,因为你可以依赖total_collisions
。后者将查看对角线上的占领,当你还没有放置所有皇后时也有效。在
board_collisions
中,我甚至会直接查看对角计数器,看看是否有任何计数器大于1。