贪心算法的改进

时间:2013-02-05 15:45:53

标签: algorithm haskell optimization recursion greedy

我一直在研究一种使用Haskell的抽象国际象棋算法(试图扩展我对不同范式的理解),并且我遇到了一个我几周来一直在思考的挑战。

问题在于:

  

给定一个板(由整数列表表示;每个   整数表示后续点值),维度为n x n,   确定提供最多点的路径。如果有平局   为了获得最佳路径,请返回其中任何一个。

以下是具体细节:

A = [[5,4,3,1],[10,2,1,0],[0,1,2,0],[2,3,4,20]] 

呈现为:

R1: 5  4  3  1, R2: 10 2 1 0, R3: 0 1 2 0, R4: 2 3 4 20.

规则是:

  1. 您可以从第一行的任何位置开始

  2. 您可以一次移动一个方格,可以是直下,左下(对角线),也可以是右下(对角线)。

  3. 输出必须是整数元组。

  4. 第一个元素是表示列与行的列表,第二个元素是总点数。例如。对于上面的板,最好的解决方案是从左上角(5)行进并沿对角线行进剩余的步骤(直到20点的正方形)。这将导致元组([1,2,3,4], 29)

    请记住,这一切都在Haskell中,因此它是一个功能范式的递归问题。起初,我正在考虑使用贪婪算法,即选择r1中的最高值,并通过比较接下来的3种可能性进行递归;选择3中的最高值。然而,垮台是贪婪算法无法在下一行之前看到潜力。

    我该怎么做?我不是在寻找代码本身,因为我喜欢自己解决问题。但是,非常感谢伪代码或一些算法指导!

4 个答案:

答案 0 :(得分:3)

保留刚刚到达的行中每列的每个列的路径列表,其中包含该单元格的最高分数。

您将使用列表

开始(在您的示例中)
[([1],5), ([2],4), ([3],3), ([4],1)]

然后,当检查下一行时,对于每一列,您选择上一行中可以到达该列的得分最高的路径,在这里,对于第二行,在第1列和第2列中,您选择在上面一行的第1列中结束的路径,在第3列中,您将选择在第4列中的第2列中结束的路径,在第4列中,路径以上一行中的第3列结尾,这样就可以你

[([1,1],15), ([1,2],7), ([2,3],5), ([3,4],3)]

对于第三行[0,1,2,0],您将再次选择前两列中以第1列结尾的路径,第三列中的路径以第2列结束,第3列中的路径以第3列结尾第四,

[([1,1,1],15), ([1,1,2],16), ([1,2,3],9), ([2,3,4],5)]

对于第四行[2,3,4,20],您将选择前三列中以第2列结尾的路径,以及最后一列中以第3列结尾的路径,

[([1,1,2,1],18), ([1,1,2,2],19), ([1,1,2,3],20), ([1,2,3,4],29)]

然后,当您到达最后一行时,选择总数最高的路径。

为什么会这样:

让得分最高的路径在专栏c中结束。最后一列上方的部分必须是在倒数第二行的一列c-1, c, c+1中结束的最高得分路径,因为最后一行中的列c只能从那些列中到达。

答案 1 :(得分:3)

我在同一主题上看到了您之前的问题,我开始研究它 由于您不想直接解决方案,我可以向您提供我对您的问题的反思,我想它可以帮助您。

一些基本属性:
1.移动的数量总是与列表的长度相比 m =长度A
2.起始点的数量是列表头部长度的长度 n =长度(头部A)
3.目前的立场永远不会消极,那么:
- 如果当前位置为0,您可以向下或向右 - 否则你可以去左,右或右

这导致我们使用这个伪代码

generate_path :: [[Int]] -> [[Int]]
generate_path [] = [[]] 
generate_path A =  ... -- You have to put something here
        where 
              m = length A
              n = length (head A)

这件事看起来像这个

move pos0 count0
    | count0 == 0 =   
        | pos0 == 0 = move (down count) ++ move (right count)  
        | otherwise = move (left count) ++ move (down count) ++ move (right count)  
            where 
                count = count0 - 1
                down  = position0 
                left  = position0 - 1
                right = position0 + 1

事实上,记住所有这些并添加(!!)运算符,我们不应该是解决方案的目标。说服你使用 A + list comprehension + !! ,作为

[A !! x !! y | x <- [1..2], y <- [0..2]] -- I take random range 

或者使用其他版本:

[[A !! x !! y | x <- [1..2]] | y <- [0..2]]] -- I take random range 

实际上你有两个递归主要工作在参数n = length(头A),你重复相同的动作从0到(n-1)at(n-1)检索结果,这个递归嵌入另一个在m上工作,从0到(m-1)重复相同的动作。

希望有所帮助。 祝你好运。

答案 2 :(得分:3)

最好的解决方案不是自上而下的贪婪算法,而是从最后一行开始并开始工作的方法:

import Data.Function
import Data.List

-- All elements of Board are lists of equal lengths
-- valid b = 1 == length (group (map length b))
type Value = Int
type Board = [[Value]]
type Index = Int
type Result = ([Index], Value)

p :: Board
p = [[5,4,3,1],[10,2,1,0],[0,1,2,0],[2,3,4,20]] 

best_from :: Board -> Result
best_from [] = undefined
best_from xs | any null xs = undefined
best_from b = best_of . best_list $ b

best_list :: Board -> [Result]
best_list b = foldr1 layer (map label b)
  where label = zipWith (\index value -> ([index],value)) [1..]
        layer new rest =  zipWith (\(i1,v1) (i2,v2) -> (i1++i2, v1+v2)) new best
          where temp = head rest : map best_pair (zip rest (tail rest))
                best = map best_pair (zip temp (tail rest)) ++ [last temp]

best_pair :: (Result,Result) -> Result
best_pair (a@(_,a1), b@(_,b1)) | a1 >=b1 = a
                               | otherwise = b

best_of :: [Result] -> Result
best_of = maximumBy (compare `on` snd)

main = do
  print (best_from p)

如果有一行,很容易解决。因此,这会使用简单的[#]解决方案路径将每行转换为Result列表。

鉴于rest行下面的puzzel new,然后添加new行就是从best找到rest解决方案(按检查,向左,向右,并与new行结合。

这会使foldrfoldr1成为自然结构。

答案 3 :(得分:0)

我选择了不同的路径,没有双关语。我列出了允许的索引组合并将其映射到它们。也许有人可以找到一种方法将它推广到任何规模的电路板上。

import Data.List
import Data.Ord
import Data.Maybe

a = [[5,4,3,1],[10,2,1,0],[0,1,2,0],[2,3,4,20]]
r1 = a !! 0
r2 = a !! 1
r3 = a !! 2
r4 = a !! 3

i = [0,1,2,3]
index_combinations = [[a,b,c,d] | a <- i, b <- i, c <- i, d <- i, 
                      abs (b-a) < 2, abs (c-b) < 2, abs (d-c) < 2]

mapR xs = [r1 !! (xs !! 0), r2 !! (xs !! 1), 
           r3 !! (xs !! 2), r4 !! (xs !! 3)]

r_combinations = map mapR index_combinations
r_combinations_summed = zip r_combinations $ map (foldr (+) 0) r_combinations

result = maximumBy (comparing snd) r_combinations_summed
path = index_combinations !! fromJust (elemIndex result r_combinations_summed)