Recursion Lua第四版编程中的八皇后难题
我目前正在阅读Lua第四版的编程,我已经被困在“第2章:插曲:八皇后之谜”的第一个练习中 示例代码如下所示:Recursion Lua第四版编程中的八皇后难题,recursion,lua,backtracking,Recursion,Lua,Backtracking,我目前正在阅读Lua第四版的编程,我已经被困在“第2章:插曲:八皇后之谜”的第一个练习中 示例代码如下所示: N = 8 -- board size -- check whether position (n, c) is free from attacks function isplaceok (a, n ,c) for i = 1, n - 1 do -- for each queen already placed if (a[i] == c) or
N = 8 -- board size
-- check whether position (n, c) is free from attacks
function isplaceok (a, n ,c)
for i = 1, n - 1 do -- for each queen already placed
if (a[i] == c) or -- same column?
(a[i] - i == c - n) or -- same diagonal?
(a[i] + i == c + n) then -- same diagonal?
return false -- place can be attacked
end
end
return true -- no attacks; place is OK
end
-- print a board
function printsolution (a)
for i = 1, N do -- for each row
for j = 1, N do -- and for each column
-- write "X" or "-" plus a space
io.write(a[i] == j and "X" or "-", " ")
end
io.write("\n")
end
io.write("\n")
end
-- add to board 'a' all queens from 'n' to 'N'
function addqueen (a, n)
if n > N then -- all queens have been placed?
printsolution(a)
else -- try to place n-th queen
for c = 1, N do
if isplaceok(a, n, c) then
a[n] = c -- place n-th queen at column 'c'
addqueen(a, n + 1)
end
end
end
end
-- run the program
addqueen({}, 1)
代码有很多注释,这本书也很明确,但我不能回答第一个问题:
练习2.1:修改八皇后程序,使其在
打印第一个解决方案
-- Listing 1
function addqueen (a, n)
if n > N then -- all queens have been placed?
return true -- (1)
else -- try to place n-th queen
for c = 1, N do
if isplaceok(a, n, c) then
a[n] = c -- place n-th queen at column 'c'
if addqueen(a, n + 1) then return true end -- (2)
end
end
return false -- (3)
end
end
-- run the program
a = {1}
if not addqueen(a, 2) then print("failed") end
printsolution(a)
a = {1, 4}
if not addqueen(a, 3) then print("failed") end
printsolution(a)
在本课程结束时,a
包含所有可能的解决方案;我不知道是否应该修改addqueen(n,c)
,使a
只包含一个可能的解决方案,或者是否应该修改printsolution(a)
,使其只打印第一个可能的解决方案
尽管我不确定是否完全理解回溯,但我试图实现这两个假设,但都没有成功,因此非常感谢任何帮助
在本课程结束时,a包含所有可能的解决方案
就我所理解的解决方案而言,a
从不包含所有可能的解决方案;它要么包含一个完整的解决方案,要么包含一个算法正在处理的不完整/不正确的解决方案。该算法的编写方式是简单地枚举可能的解决方案,跳过那些尽早产生冲突的解决方案(例如,如果第一个皇后和第二个皇后在同一行上,那么第二个皇后将被移动,而不检查其他皇后的位置,因为它们无论如何都不会满足解决方案)
因此,要在打印第一个解决方案后停止,只需在
printsolution(a)
行之后添加os.exit()
。清单1是实现该需求的替代方案。这三行分别用(1)、(2)和(3)注释,是对书中原始实现的修改,如问题中所列。通过这些修改,如果函数返回true
,则找到解决方案,并且a
包含该解决方案
-- Listing 1
function addqueen (a, n)
if n > N then -- all queens have been placed?
return true -- (1)
else -- try to place n-th queen
for c = 1, N do
if isplaceok(a, n, c) then
a[n] = c -- place n-th queen at column 'c'
if addqueen(a, n + 1) then return true end -- (2)
end
end
return false -- (3)
end
end
-- run the program
a = {1}
if not addqueen(a, 2) then print("failed") end
printsolution(a)
a = {1, 4}
if not addqueen(a, 3) then print("failed") end
printsolution(a)
让我从书中的练习2.2开始,它基于我过去向他人解释“回溯”算法的经验,可能有助于更好地理解原始实现和我的修改
练习2.2要求首先生成所有可能的排列。清单2中给出了一个简单直观的解决方案,它使用嵌套for循环生成所有置换,并在最内部的循环中逐个验证它们。虽然它满足了练习2.2的要求,但代码看起来确实很笨拙。此外,它是硬编码解决8x8板
-- Listing 2
local function allsolutions (a)
-- generate all possible permutations
for c1 = 1, N do
a[1] = c1
for c2 = 1, N do
a[2] = c2
for c3 = 1, N do
a[3] = c3
for c4 = 1, N do
a[4] = c4
for c5 = 1, N do
a[5] = c5
for c6 = 1, N do
a[6] = c6
for c7 = 1, N do
a[7] = c7
for c8 = 1, N do
a[8] = c8
-- validate the permutation
local valid
for r = 2, N do -- start from 2nd row
valid = isplaceok(a, r, a[r])
if not valid then break end
end
if valid then printsolution(a) end
end
end
end
end
end
end
end
end
end
-- run the program
allsolutions({})
当N=8时,清单3相当于清单2。else end块中的for循环执行清单2中的整个嵌套for循环所执行的操作。使用递归调用使代码不仅紧凑,而且灵活,即它能够解决NxN板和具有预设行的板。然而,递归调用有时确实会引起混乱。希望清单2中的代码有帮助
-- Listing 3
local function addqueen (a, n)
n = n or 1
if n > N then
-- verify the permutation
local valid
for r = 2, N do -- start from 2nd row
valid = isplaceok(a, r, a[r])
if not valid then break end
end
if valid then printsolution(a) end
else
-- generate all possible permutations
for c = 1, N do
a[n] = c
addqueen(a, n + 1)
end
end
end
-- run the program
addqueen({}) -- empty board, equivalent allsolutions({})
addqueen({1}, 2) -- a queen in 1st row and 1st column
将清单3中的代码与原始实现进行比较,区别在于它在将所有八个皇后放置在板上之后进行验证,而原始实现在每次添加皇后时都进行验证,如果新添加的皇后导致冲突,则不会进一步进入下一行。这就是“回溯”的全部含义,即它进行“暴力”搜索,一旦找到一个不会导致解决方案的节点,它就会放弃搜索分支,并且它必须到达搜索树的一个叶子以确定它是一个有效的解决方案
-- Listing 1
function addqueen (a, n)
if n > N then -- all queens have been placed?
return true -- (1)
else -- try to place n-th queen
for c = 1, N do
if isplaceok(a, n, c) then
a[n] = c -- place n-th queen at column 'c'
if addqueen(a, n + 1) then return true end -- (2)
end
end
return false -- (3)
end
end
-- run the program
a = {1}
if not addqueen(a, 2) then print("failed") end
printsolution(a)
a = {1, 4}
if not addqueen(a, 3) then print("failed") end
printsolution(a)
回到清单1中的修改
(1) 当函数到达这一点时,它将到达搜索树的一个叶子并找到一个有效的解决方案,因此让它返回true表示成功
(2) 这是停止函数进一步搜索的点。在最初的实现中,无论递归调用发生了什么,for循环都将继续。修改(1)到位后,如果找到解决方案,递归调用返回true,则函数需要停止并将成功信号传播回来;否则,它将继续for循环,搜索其他可能的解决方案
(3) 这是函数在完成for循环后返回的点。修改(1)和(2)后,这意味着当函数到达这一点时,它无法找到解决方案,因此让它显式返回false表示失败。您尝试了什么?我们可以得到一些您的代码,这样我们可以更容易地提供帮助吗?基本上,我尝试修改这两个函数的for循环条件。而且,我认为我没有正确的回溯思维;确切地说,我不理解
a
的最终状态-它是一个表格表吗?@Tolga-数组a
在每个新的解决方案上都会被重写<代码>a不包含以前的解决方案。它只是长度为8的数组。这不是一张桌子的桌子。亲爱的伊戈尔,谢谢你的澄清;下面的答案详细阐述了这一点,并为我提供了解决方案。不过,我仍然需要一些解释:如果你认为你能帮上忙,请参阅我在回答后刚刚提交的评论。:)亲爱的保罗,非常感谢你的帮助,它确实有效!不过我还是有点困惑:为什么对addqueen({},1)
的一个调用就可以生成多个解决方案,而其定义中没有os.exit()
?它是否等同于,而true do addqueen({},1)end
?此外,我们如何确定这些不同的解决方案是不同的排列,以及当定义中没有os.exit()
时,打破循环的边缘条件是什么?@Tolga-这实际上是一个,因此它生成所有的解决方案。亲爱的@xmartin,很抱歉耽搁了您的时间,感谢您花时间写下如此完整和详细的解释,这非常有帮助@托尔加,很高兴知道这有帮助。:-)