八皇后问题——python解法

八皇后问题的具体背景就不重复叙述了,下面直接给出解法。
该解法与巨著《structure and interpretation of computer program》中用Scheme代码提供的解法几乎是相同,具备函数式编程的风格。

“函数式编程的思路是自上而下的。它首先提出一个大问题,在最高层用一个函数来解决这个大问题。在这个函数内部,再用其他函数来解决小问题。再这样递归式的分解下,直到问题得到解决。”–Vamei《从Python开始学编程》

递归是解决此类问题的利器。递归的思想是找到 f ( n ) f(n) f(n) f ( n + 1 ) f(n+1) f(n+1)之间的递归关系,以及边界值。
具体实践中,我自己总结的方法论是“小而大之”和“大而小之”两个步骤。
所谓小而大之,就是将具体问题扩展成一般问题,而待求解的问题只是更一般问题的一个具体情况。
大而小之,就是要对这个一般问题,考虑它非常小的情形。从小的情况得到边界值以及递归关系的信息。对解决问题是非常有帮助的。

首先,对于 n n n皇后问题,有 n n n个皇后,且棋盘的大小也是 n n n.

当规模确定后,可以把 n n n皇后的解法问题看成是逐步生成的。首先放置第一列,解为 f ( 1 ) f(1) f(1),然后放置第二列,解为 f ( 2 ) f(2) f(2),直至求出 f ( n ) f(n) f(n) f ( n ) f(n) f(n)返回的是列表的列表,每个子列表代表一种解法。

对于 f ( n ) f(n) f(n),当 n = 1 n=1 n=1时,代表放置第一列,那么将有 n n n种解法,结果为 f ( 1 ) f(1) f(1):
[[1], [2], [3], …, [n]]
n > 1 n>1 n>1时,对 f ( n − 1 ) f(n-1) f(n1)中的每一种解法添加第 n n n列的放法,首先将所有可能的放法都添加进去,将得到 n f ( n − 1 ) nf(n-1) nf(n1)种放法,然后过滤掉其中不满足要求的放法,就可以得到 f ( n ) f(n) f(n)

Big question 的解法如下:

def solution(n):
	scale = n
    def f(k):
        if k == 1:
            return [[x] for x in range(1, scale + 1)]
        else:
            last = f(k-1)
            return list(filter(safe, [x+[y] for x in last for y in range(1, scale+1)]))
    return f(n)

其中safe函数用来判断当前的放法是否满足要求,联想函数式编程的思想,safe只要具备判断一解法是否合法即可,与其具体实现是无关的。

在由 f ( n ) f(n) f(n) f ( n + 1 ) f(n+1) f(n+1)时,要判断新添加的列是否满足要求,只需满足两个条件:

  • 不与之前的任一个皇后同行
  • 不与之前的任一个皇后在同一斜线上
    下面是safe函数的定义:
def safe(x):
    for i in range(len(x)-1):
        if (x[-1] == x[i]) or (abs(x[-1] - x[i]) == (len(x)-1) - i):
            return False
    return True

然后我们把safe函数的定义放到solution函数的内部即可,以形成一种所谓的块结构

def solution(n):
	scale = n
	
	def safe(x):
    	for i in range(len(x)-1):
        	if (x[-1] == x[i]) or (abs(x[-1] - x[i]) == (len(x)-1) - i):
           	 return False
    	return True
    
    def f(k):
        if k == 1:
            return [[x] for x in range(1, scale + 1)]
        else:
            last = f(k-1)
            return list(filter(safe, [x+[y] for x in last for y in range(1, scale+1)]))
    return f(n)

运行如下:

>>>len(solution(8))
92
>>>[len(solution(x)) for x in range(1, 10)]
[1, 0, 0, 2, 10, 4, 40, 92, 352]
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值