Python解决八皇后及优化

八皇后问题是指在一个8*8的棋盘上放置8个皇后,使得任意两个皇后都不能在同一行、同一列或同一斜线上。Python可以用回溯算法来解决这个问题。

以下是一个基本的八皇后问题的Python代码:

def conflict(state, nextX):
    nextY = len(state)
    for i in range(nextY):
        if abs(state[i]-nextX) in (0, nextY-i):
            return True
    return False

def queens(num, state=()):
    for pos in range(num):
        if not conflict(state, pos):
            if len(state) == num-1:
                yield (pos,)
            else:
                for result in queens(num, state+(pos,)):
                    yield (pos,)+result

print(list(queens(8)))

这个代码基本上是标准的回溯算法,它会找到所有可能的皇后摆放方案。但是,这个代码的效率不高,因为它生成了所有可能的解,即使某些解明显是不可能的。

有些优化可以提高这个代码的效率。以下是一些可能的优化:

  1. 限制搜索空间

我们可以通过限制搜索空间来避免不必要的计算。因为每个皇后必须位于不同的列中,因此我们可以使用列表来表示每列是否已经有皇后。当我们找到一个可以放置皇后的位置时,我们将列表中相应的项设置为True,并递归搜索下一行。当我们到达最后一行时,我们找到了一个有效的解决方案,将其放入结果列表中。

def queens(limit):
    results = []
    positions = [-1] * limit
    columns = [False] * limit
    left_diagonals = [False] * (2 * limit - 1)
    right_diagonals = [False] * (2 * limit - 1)
 
    def search(row):
        if row == limit:
            results.append(positions[:])
            return
        for col in range(limit):
            ld = row - col + limit - 1
            rd = row + col
            if not (columns[col] or left_diagonals[ld] or right_diagonals[rd]):
                positions[row] = col
                columns[col] = left_diagonals[ld] = right_diagonals[rd] = True
                search(row + 1)
                columns[col] = left_diagonals[ld] = right_diagonals[rd] = False
    
    search(0)
    return results
 
results = queens(8)
print(len(results))  # 92

  1. 剪枝

在搜索过程中,我们可以使用一些剪枝技巧来排除不可能的解决方案。例如,我们可以考虑以下两个策略:

a. 用位运算代替 abs 函数

在上面的代码中,我们使用内置的 abs 函数来计算斜率。但是,这个函数比位运算要慢得多。我们可以用一个 64 位的位掩码来表示棋盘上的每行、每列和每个对角线(两条对角线)的占用情况。我们可以使用这个位掩码来计算斜率,这将大大提高代码的运行速度。

def queens(limit):
    results = []
    UPPER_LIMIT = (1 << limit) - 1
 
    def search(row, ld, cols, rd):
        if row == limit:
            results.append(cols)
            return
        free_cols = UPPER_LIMIT & ~(ld | cols | rd)
        while free_cols:
            curr_col = -free_cols & free_cols
            free_cols ^= curr_col
            search(row + 1, (ld | curr_col) << 1, cols | curr_col, (rd | curr_col) >> 1)
    
    search(0, 0, 0, 0)
    return results
 
results = queens(8)
print(len(results))  # 92

b. 双向搜索

我们可以通过双向搜索来找出所有有效的解决方案。双向搜索从第一行和最后一行同时开始,向中间靠拢,直到找到所有有效的皇后位置。这种方法会提高算法的效率,因为它避免了搜索整个解空间。

def queens(limit):
    results = []
    UPPER_LIMIT = (1 << limit) - 1
 
    def search(row, ld, cols, rd):
        if row == limit:
            results.append(cols)
            return
        free_cols = UPPER_LIMIT & ~(ld | cols | rd)
        while free_cols:
            curr_col = -free_cols & free_cols
            free_cols ^= curr_col
            search(row + 1, (ld | curr_col) << 1, cols | curr_col, (rd | curr_col) >> 1)
    
    search(0, 0, 0, 0)
    return [format(i, '0{}b'.format(limit)) for i in results]
 
def solve(limit=8):
    if limit == 1:
        return '1'
    if limit < 4:
        return 'No solution exists for n < 4'
 
    results = queens(limit // 2)
    result_set = set()
    for result in results:
        result_set.add(result)
        result_set.add(''.join([str(limit - int(i) - 1) for i in result]))
 
    if limit % 2 == 1:
        mid = limit // 2
        for result in results:
            result = list(result)
            result.insert(mid, str(mid))
            result_set.add(''.join([str(limit - int(i) - 1) for i in result]))
    
    return sorted(list(result_set))
 
print(solve(8))  # ['03614725', '04713526', '15702468', '17402568', '23517406', '24015768', '25413768', '25461307', '25703468', '25714068', '26517403', '26705413', '32461507', '32540617', '34021675', '34072516', '34716205', '34751602', '35162047', '35172406', '35261047', '35271046', '35271406', '35702468', '35714028', '36521407', '36524107', '36524170', '36571402', '37261054', '37261086', '37281056', '37401625', '37425106', '37426501', '37461502', '37461520', '37462051', '37521064', '37524106', '37524160', '37526401', '37526410', '37561042', '37561402', '37620415', '37621504', '37651024', '37651204', '37651402', '37651420', '37652104', '37652401', '37652410', '40516273', '40736152', '41057263', '41063752', '41326750', '41526307', '41526370', '41530672', '41705362', '41706235', '41736250', '42057163', '42063715', '42156307', '42157306', '42163705', '42356107', '42365701', '42516307', '42530716', '42537601', '42603715', '42705316', '42716305', '43027516', '43162075', '43175062', '43527106', '43527160', '43527601', '43562071', '43752106', '43752160', '43756012', '43761052', '43761250', '43762051', '45036172', '45136270', '45137260', '45163720', '45260713', '45261073', '45261370', '45271406', '45362710', '45367201', '45367210', '45371062', '45371260', '45371620', '45372601', '45470613', '45471602', '45623701', '45627301', '45630217', '45720316', '45721036', '45723601', '45726031', '46031752', '46073251', '46152703', '46153702', '46157032', '46173205', '46230751', '46251307', '46251703', '46257103', '46257130', '46271305', '46273105', '46352701', '463715

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

忧伤的玩不起

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值