List 利用haskell的最大正子矩阵
我有以下问题: 给你矩阵m*n,你必须找到从(1,1)到(x,y)的最大正(子矩阵的所有元素都应该大于0)子矩阵 我所说的最大值是指,当你有以下矩阵时:List 利用haskell的最大正子矩阵,list,haskell,matrix,functional-programming,submatrix,List,Haskell,Matrix,Functional Programming,Submatrix,我有以下问题: 给你矩阵m*n,你必须找到从(1,1)到(x,y)的最大正(子矩阵的所有元素都应该大于0)子矩阵 我所说的最大值是指,当你有以下矩阵时: [[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]]] i、 e.前两行是一个解决方案,前两列是第二个解决方案 另一个例子:矩阵是 [[1,2,3,-4],[5,6,7,8
[[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]]]
i、 e.前两行是一个解决方案,前两列是第二个解决方案
另一个例子:矩阵是
[[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]
导入数据。列表隐藏(插入)
导入符合条件的数据。设置为集合
唯一::Ord a=>[a]->[a]
unique=Set.toList。Set.fromList
子列表::[[Int]]->[[[Int]]]
子列表矩阵=过滤器(所有正矩阵)$[(子矩阵1 x y)| x如果(长度(nextMatrix!!0))>(长度(largestPreviousX!!0)),则nextMatrix else largestPreviousX)[[]]子列表!!0)
maxWidthSubmatrices::[[[Int]]]->Int->[[[Int]]]
maxWidthSubmatrices子列表maxWidth=filter(\x->(长度$x!!0)=maxWidth)子列表
高度矩阵=长度矩阵
宽度矩阵=长度(矩阵!!0)
maximalPositiveSubmatrices矩阵=maxWidthSubmatrices(子列表矩阵)(maxWidthMat(过滤器(\x->(长度$x!!0)=(maxWidthMat$子列表矩阵))(子列表矩阵)))
allPositiveList=foldl(\x y->if(y>0)和&(x==True)则为True,否则为False)True list
allPositiveMatrix::[[Int]]->Bool
allPositiveMatrix=foldl(\x y->if(allPositiveList y)&&(x==True)则为True,否则为False)真矩阵
子矩阵x1 y1 x2 y2=切片(映射(\x->切片x x1 x2)矩阵)y1 y2
切片列表XY=放置(x-1)(取y列表)
maximalWidthSubmatrix mm=最大$maximalPositiveSubmatrices mm
MaximalHeightsSubmatrix mm=转置$Maximal$maximalPositiveSubmatrices$转置mm
--解决方案
解矩阵=唯一$[maximalWidthSubmatrix]+[maximalHeigthSubmatrix]
正如你所看到的,它极其冗长和丑陋。
它可能也不是最快的
你能给我展示更优雅、更快和更短的解决方案(可能有解释)吗
我认为为了解决这个问题,我们首先最好进行降维:
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列有-4
现在我们可以在这些行上获得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
现在我们只需要获得这些子矩阵本身。我们可以通过使用take
和map
overtake
来实现这一点:
construct_sub :: [[a]] -> [(Int,Int)] -> [[[a]]]
construct_sub mat = map (\(r,c) -> take r (map (take c) mat)) -- O(m^2*n)
现在我们只需要在一个解算
:
-- 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
时间复杂性
该算法在O(m×n)中运行,m为行数,n为列数,以构造矩阵的维度
所有的子矩阵都需要O(m2×n)来构造,因此算法在O(m2×n)中运行
我们可以转换方法,在列而不是行上运行。因此,如果我们使用的是行数与列数相差很大的矩阵,我们可以首先计算最小值(可选地进行转换),从而使m成为两个矩阵中最小的一个
潜在优化点
我们可以通过在构造max\u sub\u dim
的同时构造子矩阵来加快算法的速度。提出的算法
我认为为了解决这个问题,我们首先最好进行降维:
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列有-4
现在我们可以在这些行上获得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
现在我们只需要获得这些子矩阵本身。我们可以通过使用take
和map
overtake
来实现这一点:
construct_sub :: [[a]] -> [(Int,Int)] -> [[[a]]]
construct_sub mat = map (\(r,c) -> take r (map (take c) mat)) -- O(m^2*n)
现在我们只需要在一个解算
:
-- 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
时间复杂性
该算法在O(m×n)中运行,m为行数,n为列数,以构造矩阵的维度
所有的子矩阵都需要O(m2×n)来构造,因此算法在O(m2×n)中运行
我们可以转换方法,在列而不是行上运行。因此,如果我们使用的是行数与列数相差很大的矩阵,我们可以首先计算最小值(可选地进行转换),从而使m成为两个矩阵中最小的一个
潜在优化点
我们可以通过构造子映射使算法更快