python空数组报错_Python 求解三阶幻方

三阶幻方

三阶幻方是最简单的幻方,又叫九宫格,是由1,2,3,4,5,6,7,8,9九个数字组成的一个三行三列的矩阵(如右图示),其对角线、横行、纵向的

的和都为15,称这个最简单的幻方的幻和为15。中心数为5。

三阶幻方共有八组结果

8

276

951

438

294

753

618

438

951

276

492

357

816

618

753

294

672

159

834

816

357

492

834

159

672

使用深度优先搜索算法,暴力搜索所有解空间,找到符合要求的一组数,放入ans数组

注意

1.浅拷贝的问题

a = [1, 2, 3]

b = a

c = a.copy()

b[0] = 111

c[0] = 222

print(a, b, c)

[111, 2, 3] [111, 2, 3] [222, 2, 3]

2.与js不同,Python中对空数组执行pop操作会报错,但js不会

js

a = []

console.log(a.pop());

undefined

py

a=[]

a.pop()

IndexError: pop from empty list

实现代码

'''

三阶幻方8个

四阶幻方7040个

'''

# 幻方阶数

n = 3

# 幻和,即每行,每列和对角线的和

s = n * n * (n * n + 1) // 2 // n

# 数组

a = []

# 使用过的数字

used = set()

# 答案数组

ans = []

# 检查是否满足幻方条件

def check():

flag = True

for i in range(n):

# 行的幻和

r = sum([a[i * n + j] for j in range(n)])

# 列的幻和

c = sum([a[i + j * n] for j in range(n)])

flag = flag and r == c == s

# 主对角线和次对角线的幻和

s1 = sum([a[i * n + i] for i in range(n)])

s2 = sum([a[n * i - i] for i in range(1, n + 1)])

flag = flag and s1 == s2 == s

return flag

# 搜索算法,参数表示准备放入第m个数字

def dfs(m):

# 表示已经放入足够的数字了,进行检验

if m == n * n:

if check():

print(a)

ans.append(a.copy())

# 弹出最后一位,继续递归,并且移除used集合中的数

used.remove( a.pop())

return

# 行剪枝

if m > 0 and m % n == 0 and sum([a[int(m / n - 1) * n + i] for i in range(n)]) != s:

used.remove(a.pop())

return

# 对角线剪枝

if m == n * (n - 1) + 1:

if sum([a[n * i - i] for i in range(1, n + 1)]) != s:

used.remove(a.pop())

return

# 列剪枝,在放入最后一行时,进行列剪枝

if m > n * (n - 1):

c = m - n * (n - 1) - 1

if sum([a[c + i * n] for i in range(n)]) != s:

used.remove(a.pop())

return

# 数组长度不够时

for i in range(1, n * n + 1):

if i not in used:

a.append(i)

used.add(i)

dfs(m + 1)

# print(a, m)

if len(a) > 0:

used.remove(a.pop())

def show(arr):

s = ''

for index, i in enumerate(arr):

s += str(i)

if index % n == n - 1:

s += '\n'

return s

dfs(0)

print(len(ans))

for i in ans:

print(show(i))

执行结果分析

该方法完全采用暴力搜索,查看各个函数的执行次数

53ee7b6454d38205847ea3b7f02698af.png

其中check函数只有当数组元素个数大于9时才会执行,所以执行次数为

9!=362880

其中dfs函数构造数组由一位开始所以总执行次数为

3ccfb11186287c57ae4b31f51774057d.png

使用scipy求解

from scipy.special import comb, perm

print(

sum([perm(9, i) for i in range(10)])

)

986410.0

现在进行剪枝操作

减少搜索范围

第一种剪枝方法,在dfs函数开始处进行剪枝,此时可以看到,dfs和check函数的执行次数已经显著下降了,我们只用了每行的和必须为幻和这个条件

872377595f997c7241a09314b1e5eb6a.png

# 搜索算法,参数表示准备放入第m个数字

def dfs(m):

if m > 0 and m % n == 0 and sum([a[int(m / n - 1) * n + i] for i in range(n)]) != s:

t = a.pop()

used.remove(t)

return

# 表示已经放入足够的数字了,进行检验

if m == n * n:

if check():

ans.append(a.copy())

t = a.pop()

# 弹出最后一位,继续递归,并且移除used集合中的数

used.remove(t)

return

# 数组长度不够时

for i in range(1, n * n + 1):

if i not in used:

a.append(i)

used.add(i)

dfs(m + 1)

# print(a, m)

if len(a) > 0:

t = a.pop()

used.remove(t)

第二种剪枝方法

直接在放入每行最后一个数字的时候进行剪枝,该数字应该为幻和减去每行之和,相比于暴力搜索同样减少了很多

4686244a7b4d7ce165e3a8fb3aee07cc.png

# 搜索算法,参数表示准备放入第m个数字

def dfs(m):

# 表示已经放入足够的数字了,进行检验

if m == n * n:

if check():

ans.append(a.copy())

t = a.pop()

# 弹出最后一位,继续递归,并且移除used集合中的数

used.remove(t)

return

# 准备放入每行的最后一个数字时

if m > 0 and m % n == n - 1:

r = int(m / n)

t = s - sum([a[r * n + i] for i in range(n - 1)])

# 如果t已经被使用了,则说明该行之和不可能为幻和则回溯

if t in used:

used.remove(a.pop())

return

else:

a.append(t)

used.add(t)

dfs(m + 1)

else:

for i in range(1, n * n + 1):

if i not in used:

a.append(i)

used.add(i)

dfs(m + 1)

# print(a, m)

if len(a) > 0:

t = a.pop()

used.remove(t)

加上列剪枝

dd72772f623dc2776028fb67e3ede94c.png

加上对角线剪枝

15a6f5b5587ff702910032a40fe103de.png

完整剪枝代码

# 搜索算法,参数表示准备放入第m个数字

def dfs(m):

# 表示已经放入足够的数字了,进行检验

if m == n * n:

if check():

ans.append(a.copy())

# 弹出最后一位,继续递归,并且移除used集合中的数

used.remove( a.pop())

return

# 行剪枝

if m > 0 and m % n == 0 and sum([a[int(m / n - 1) * n + i] for i in range(n)]) != s:

used.remove(a.pop())

return

# 对角线剪枝

if m == n * (n - 1) + 1:

if sum([a[n * i - i] for i in range(1, n + 1)]) != s:

used.remove(a.pop())

return

# 列剪枝,在放入最后一行时,进行列剪枝

if m > n * (n - 1):

c = m - n * (n - 1) - 1

if sum([a[c + i * n] for i in range(n)]) != s:

used.remove(a.pop())

return

# 数组长度不够时

for i in range(1, n * n + 1):

if i not in used:

a.append(i)

used.add(i)

dfs(m + 1)

# print(a, m)

if len(a) > 0:

used.remove(a.pop())

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值