目录
1、博客介绍
lua学习笔记之一,Lua版八皇后问题的写法,回溯算法的典型案例,编辑器使用Sublime
注:本篇博客部分源码摘自Lua程序设计(第四版)梅隆魁译
2、内容
--检查(n,c)是否不会被攻击
function isplaceok(a,n,c)
for i=1,n-1 do
if (a[i] == c) or --同一行
(a[i]-i == c-n) or --对角线
(a[i] + i == c+n) then --对角线
return false
end
end
return true --不会被攻击
end
--打印棋盘
function printsolution(a)
for i=1,8 do
for j=1,8 do
io.write(a[i]==j and "X" or "-"," ")
end
io.write("\n")
end
io.write("\n")
end
function addQueen(a,n)
if n > 8 then
printsolution(a)
else
for c=1,8 do
if isplaceok(a,n,c) then
a[n] = c
addQueen(a,n+1)
end
end
end
end
addQueen({},1)
-----------------------输出示例
X - - - - - - -
- - - - X - - -
- - - - - - - X
- - - - - X - -
- - X - - - - -
- - - - - - X -
- X - - - - - -
- - - X - - - -
内容很简单同学们直接看源码吧,最后一个函数addQueen为核心,运行后将在控制台输出所有符合八皇后规则的排列组合,该解法虽然能够输出所有符合规则的组合,但是我们观察一下核心代码addQueen,相当于从第一列开始每一列每一行都去尝试排列,如果不符合就舍弃之前的内容,这样算是去穷举而不符合回溯的优选法,以下我们对代码略作修改。
-----------------------------回溯算法 输出第一种满足情况
--当前列所处的位置
local colIndexList = {}
--上次回溯记录
local lastSucRow = 0
-- 检查(n,c)是否不会被攻击
function isplaceok(a,n,c)
for i=1,n-1 do
if (a[i] == c) or --同一行
(a[i]-i == c-n) or --对角线
(a[i] + i == c+n) then --对角线
return false
end
end
return true --不会被攻击
end
--打印棋盘
function printsolution(a)
for i=1,8 do
for j=1,8 do
io.write(a[i]==j and "X" or "-"," ")
end
io.write("\n")
end
io.write("\n")
end
function addQueen(col,recallTag)
if col<1 then return end --小于1的列不执行
if recallTag then --判断本次执行是否为回溯
colIndexList[col] = nil --回溯则当前序列表内值置空
if lastSucRow == 8 then --回溯点已为当前列最大值则继续向前回溯
lastSucRow = colIndexList[col-1] or 0
addQueen(col-1,true)
return
end
end
for i=lastSucRow+1,8 do
if isplaceok(colIndexList,col,i) then --符合要求停止循环
colIndexList[col] = i
if col == 8 then --已够8列则输出
printsolution(colIndexList)
else --不够则继续添加
lastSucRow = 0 --设置回溯点
addQueen(col+1)
end
break
else
if i == 8 then --改列没有满足的要求
lastSucRow = colIndexList[col-1] or 0
addQueen(col-1,true) --回溯到上一列
end
end
end
end
addQueen(1)
我们稍微改善了一下addQueen的写法,在这里我们新增了两个变量,一个是用来记录当前数据的表colIndexList,另一个则是用来记录回溯点的lastSucRow,addQueen的核心思想就是,我从第一列第一行的数据去开始判断,如果满足八皇后规则的条件则将该点的数据记录在colIndexList内,并且将该店作为回溯点记录,然后去判断下一列的点,如果下一列有满足的点,则继续,没有满足的点就可以回溯到上一列的回溯点去继续判断,若是数据长度到了8则就输出方阵,该写法输出满足条件的第一组数据,我们在稍加改动可以输入任意数量满足规则的数据。
--当前列所处的位置
local colIndexList = {}
--上次回溯记录
local lastSucRow = 0
local targetSucIndex = 100
local sucIndex = 0
-- 检查(n,c)是否不会被攻击
function isplaceok(a,n,c)
for i=1,n-1 do
if (a[i] == c) or --同一行
(a[i]-i == c-n) or --对角线
(a[i] + i == c+n) then --对角线
return false
end
end
return true --不会被攻击
end
--打印棋盘
function printsolution(a)
for i=1,8 do
for j=1,8 do
io.write(a[i]==j and "X" or "-"," ")
end
io.write("\n")
end
io.write("\n")
end
function addQueen(col,recallTag)
if col<1 then return end --小于1的列不执行
if recallTag then --判断本次执行是否为回溯
colIndexList[col] = nil --回溯则当前序列表内值置空
if lastSucRow == 8 then --回溯点已为当前列最大值则继续向前回溯
lastSucRow = colIndexList[col-1] or 0
addQueen(col-1,true)
return
end
end
for i=lastSucRow+1,8 do
if isplaceok(colIndexList,col,i) then --符合要求停止循环
colIndexList[col] = i
if col == 8 then --已够8列则输出
sucIndex = sucIndex + 1
printsolution(colIndexList)
if sucIndex < targetSucIndex then --不够则继续回溯
lastSucRow = i
addQueen(col,true) --回溯到上一列
end
else --不够则继续添加
lastSucRow = 0 --设置回溯点
addQueen(col+1)
end
break
else
if i == 8 then --改列没有满足的要求
lastSucRow = colIndexList[col-1] or 0
addQueen(col-1,true) --回溯到上一列
end
end
end
end
addQueen(1)
大家看上述代码当中,我们较前一组代码进行了微小的改动,新增了两个变量targetSucIndex用来确定输出方阵的数量,sucIndex用来记录当前输出数据的数量,我们在addQueen方法中做了改动,本来在满足8数据长度后就会输出并且停止运算,现在改动后,会在满足8长度后,将当前点作为回溯点去继续运算,这样当输出数据数量小于目标数量时就会继续计算,这里注意,满足条件一共有92种,所以你目标数据无论多大,在输出完所有符合规则的数据后依然会停止运算。
3、推送
Github:https://github.com/KingSun5
4、结语
若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。
QQ交流群:806091680(Chinar)
该群为CSDN博主Chinar所创,推荐一下!我也在群里!