使用haskell的最大正子矩阵

时间:2017-06-26 10:13:11

标签: list haskell matrix functional-programming submatrix

我有以下问题:

你得到矩阵m * n,你必须找到从(1,1)到(x,y)的最大正数(子矩阵的所有元素应该> 0)子矩阵。< / p>

当你有以下矩阵时,maximal是什么意思:

 [[1,2,3,4],[5,6,7,8],[9,10,-11,12],[13,14,15,16]]

然后最大正子矩阵是:

[[[1,2,3,4],[5,6,7,8]],[[1,2],[5,6],[9,10],[13,14]]]

即。前两行是一种解决方案,前两列是第二种解决方案。

另一个例子:矩阵

[[1,2,3,-4],[5,6,7,8],[-9,10,-11,12],[13,14,15,16]]

和解决方案是:

[[[1,2,3],[5,6,7]]]

这是我的Haskell程序解决了它:

import Data.List hiding (insert)

import qualified Data.Set as Set

unique :: Ord a => [a] -> [a]
unique = Set.toList . Set.fromList


subList::[[Int]] ->[[[Int]]]
subList matrix = filter (allPositiveMatrix) $  [ (submatrix matrix 1 1 x y) |   x<-[1..width(matrix)], y<-[1..height(matrix)]]


maxWidthMat::[[[Int]]] -> Int
maxWidthMat subList =length ((foldl (\largestPreviousX nextMatrix -> if (length (nextMatrix!!0)) >(length (largestPreviousX !!0)) then nextMatrix  else largestPreviousX ) [[]] subList)!!0)


maxWidthSubmatrices:: [[[Int]]] -> Int ->[[[Int]]]
maxWidthSubmatrices subList maxWidth = filter (\x -> (length $x!!0)==maxWidth) subList

height matrix = length matrix

width matrix = length (matrix!!0)

maximalPositiveSubmatrices matrix =  maxWidthSubmatrices (subList matrix) (maxWidthMat   (filter (\x -> (length $x!!0)==( maxWidthMat $ subList matrix )) (subList matrix)))


allPositiveList list = foldl (\x y -> if (y>0)&&(x==True) then True else False) True list

allPositiveMatrix:: [[Int]] -> Bool
allPositiveMatrix matrix = foldl (\ x y -> if (allPositiveList y)&&(x==True) then True else False  )  True matrix


submatrix matrix x1 y1 x2 y2 = slice ( map (\x -> slice x x1 x2) matrix) y1 y2

slice list x y = drop (x-1)  (take y list)



maximalWidthSubmatrix mm =  maximum $ maximalPositiveSubmatrices mm
maximalHeigthSubmatrix mm = transpose $ maximum $ maximalPositiveSubmatrices $ transpose mm

-- solution
solution matrix =unique $ [maximalWidthSubmatrix matrix]++[maximalHeigthSubmatrix matrix]

你可以看到它非常冗长和丑陋。 它可能也不是最快。

您能否向我展示更多优雅更快更短解决方案(可能还有解释)?

1 个答案:

答案 0 :(得分:0)

提议的算法

我认为,为了解决这个问题,我们首先要更好地进行降维:

reduce_dim :: (Num a,Ord a) => [[a]] -> [Int]
reduce_dim = map (length . takeWhile (>0))  -- O(m*n)

对于每一行,我们计算项目数 - 从左边开始 - 是正数。所以对于给定的矩阵:

 1   2   3   4  | 4
 5   6   7   8  | 4
 9  10 -11  12  | 2
13  14  15  16  | 4

第二行因此映射到2,因为第三行是-11。

或者你的其他矩阵:

 1   2   3  -4  | 3
 5   6   7   8  | 4
-9  10 -11  12  | 0
13  14  15  16  | 4

由于第一行在第4列有-4,第三行在第1列有。

现在我们可以在这些行上获得scanl1 min

Prelude> scanl1 min [4,4,2,4] -- O(m)
[4,4,2,2]
Prelude> scanl1 min [3,4,0,4] -- O(m)
[3,3,0,0]

现在每次数字减少(并且结束时),我们知道我们在上面的行中找到了一个最大子矩阵。因为这意味着我们现在使用从哪里开始的行,列数更少。一旦我们达到零,我们就知道进一步的评估是没有意义的,因为我们正在处理一个有0列的矩阵。

因此,基于该列表,我们可以简单地生成最大子矩阵大小的元组列表

max_sub_dim :: [Int] -> [(Int,Int)]
max_sub_dim = msd 1  -- O(m)
    where msd r []             = []
          msd r (0:_)          = []
          msd r [c]            = [(r,c)]
          msd r (c1:cs@(c2:_)) | c2 < c1 = (r,c1) : msd (r+1) cs
                               | otherwise = msd (r+1) cs

因此,对于您的两个矩阵,我们获得:

*Main> max_sub_dim $ scanl1 min $ reduce_dim [[1,2,3,4],[5,6,7,8],[9,10,-11,12],[13,14,15,16]]
[(2,4),(4,2)]
*Main> max_sub_dim $ scanl1 min $ reduce_dim [[1,2,3,-4],[5,6,7,8],[-9,10,-11,12],[13,14,15,16]]
[(2,3)]

现在我们只需要自己获取这些子矩阵。我们可以使用takemap而不是take来执行此操作:

construct_sub :: [[a]] -> [(Int,Int)] -> [[[a]]]
construct_sub mat = map (\(r,c) -> take r (map (take c) mat))  -- O(m^2*n)

现在我们只需要在solve

中将它们全部链接在一起
-- complete program

reduce_dim :: (Num a,Ord a) => [[a]] -> [Int]
reduce_dim = map (length . takeWhile (>0))

max_sub_dim :: [Int] -> [(Int,Int)]
max_sub_dim = msd 1
    where msd r []             = []
          msd r (0:_)          = []
          msd r [c]            = [(r,c)]
          msd r (c1:cs@(c2:_)) | c2 < c1 = (r,c1) : msd (r+1) cs
                               | otherwise = msd (r+1) cs

construct_sub :: [[a]] -> [(Int,Int)] -> [[[a]]]
construct_sub mat = map (\(r,c) -> take r (map (take c) mat))

solve :: (Num a,Ord a) => [[a]] -> [[[a]]]
solve mat = construct_sub mat $ max_sub_dim $ scanl1 min $ reduce_dim mat

然后生成:

*Main> solve [[1,2,3,4],[5,6,7,8],[9,10,-11,12],[13,14,15,16]]
[[[1,2,3,4],[5,6,7,8]],[[1,2],[5,6],[9,10],[13,14]]]
*Main> solve [[1,2,3,-4],[5,6,7,8],[-9,10,-11,12],[13,14,15,16]]
[[[1,2,3],[5,6,7]]]

时间复杂度

算法在 O(m×n)中运行 m 行数, n 列数,以构建矩阵的维度。对于每个定义的函数,我在评论中写下了时间复杂度。

O(m 2 ×n)带到构造所有子矩阵。所以算法运行在 O(m 2 ×n)

我们可以转置方法并在列而不是行上运行。因此,如果我们使用矩阵与行数相差很大的矩阵,我们可以先计算最小值,可选择转置,从而使 m 成为两者中最小的一个。

潜在优化点

我们可以通过构建子矩阵来加快算法的速度,同时构建max_sub_dim来节省一些工作。