在Haskell中获取矩阵的所有对角线

时间:2015-09-08 19:31:28

标签: haskell matrix

二维列表如下:

1 | 2 | 3
- - - - -
4 | 5 | 6
- - - - -
7 | 8 | 9

或纯粹的哈斯克尔

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

diagonals [ [1,2,3], [4,5,6], [7,8,9] ]的预期输出为

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

allDiagonals(包括反对角线)是微不足道的:

allDiagonals :: [[a]] -> [[a]]
allDiagonals xss = (diagonals xss) ++ (diagonals (rotate90 xss))

我对这个问题的研究

StackOverflow上的类似问题

  • Python这个问题与Python中的问题有关,但Python和Haskell非常不同,所以这个问题的答案与我无关。

  • Only one这个问题和答案都在Haskell中,但只是关于中心对角线。

Hoogle

搜索[[a]] -> [[a]]没有给我带来任何有趣的结果。

独立思考

我认为索引遵循基数x中的一种计数,其中x是矩阵中的维数,请看:

1 | 2
- - -
3 | 4

对角线为[ [1], [3, 2], [4] ]

  • 1可以在matrix[0][0]
  • 找到
  • 3可以在matrix[1][0]
  • 找到
  • 2可以在matrix[0][1]
  • 找到
  • 1可以在matrix[1][1]
  • 找到

这类似于在基数2中计数到3,即矩阵大小减1。但这太过模糊,无法翻译成代码。

4 个答案:

答案 0 :(得分:8)

universe-base-1.0.2.1开始,您只需调用diagonals函数:

Data.Universe.Helpers> diagonals [ [1,2,3], [4,5,6], [7,8,9] ]
[[1],[4,2],[7,5,3],[8,6],[9]]

完整的实现如下:

diagonals :: [[a]] -> [[a]]
diagonals = tail . go [] where
    -- it is critical for some applications that we start producing answers
    -- before inspecting es_
    go b es_ = [h | h:_ <- b] : case es_ of
        []   -> transpose ts
        e:es -> go (e:ts) es
        where ts = [t | _:t <- b]

关键的想法是我们保留两个列表:我们尚未开始检查的矩形块,以及我们拥有的五边形块(左上三角形切割的矩形!)。对于五边形块,从每个列表中挑选出第一个元素会给我们另一个对角线。然后我们可以在删除该对角线后,从未经检查的矩形块中添加一个新行到

实现可能看起来有点不自然,但它的目的是非常高效和懒惰:我们对列表做的唯一事情是将它们分解为头尾,所以这应该是O(n)in矩阵中元素的总数;我们在完成解构后立即生成元素,因此它对垃圾收集非常懒惰/友好。它也适用于无限大的矩阵。

(我推出这个版本只是为了你:你可以得到的最接近的东西是使用diagonal,这只会给你[1,4,2,7,5,3,8,6,9]没有你想要的额外结构。)

答案 1 :(得分:4)

这是一个递归版本,假设输入总是格式正确:

diagonals []       = []
diagonals ([]:xss) = xss
diagonals xss      = zipWith (++) (map ((:[]) . head) xss ++ repeat [])
                                  ([]:(diagonals (map tail xss)))

它从列到列递归地工作。来自一列的值与来自矩阵的对角线组合,减少一列,移动一行以实际获得对角线。希望这种解释是有道理的。

例如:

diagonals [[1,2,3],[4,5,6],[7,8,9]]
= zipWith (++) [[1],[4],[7],[],[],...] [[],[2],[5,3],[8,6],[9]]
= [[1],[4,2],[7,5,3],[8,6],[9]]

另一个版本适用于行而不是列,但基于相同的想法:

diagonals []       = repeat []
diagonals (xs:xss) = takeWhile (not . null) $
    zipWith (++) (map (:[]) xs ++ repeat [])
                 ([]:diagonals xss)

与指定的结果相比,生成的对角线反转。这当然可以通过应用map reverse来解决。

答案 2 :(得分:2)

这是一种方法:

f :: [[a]] -> [[a]]
f vals = 
    let n = length vals
    in [[(vals !! y) !! x | x <- [0..(n - 1)], 
                            y <- [0..(n - 1)], 
                            x + y == k] 
        | k <- [0 .. 2*(n-1)]]

例如,在GHCi中使用它:

Prelude> let f vals = [ [(vals !! y) !! x | x <- [0..(length vals) - 1], y <- [0..(length vals) - 1], x + y == k] | k <- [0 .. 2*((length vals) - 1)]]

Prelude> f [ [1,2,3], [4,5,6], [7,8,9] ]
[[1],[4,2],[7,5,3],[8,6],[9]]

假设一个正方形n x n矩阵,将会有n + n - 1对角线(这是k迭代的对象),对于每个对角线,不变量是行和列索引总和为对角线值(从左上角的零索引开始)。您可以交换商品访问顺序(与!! y !! x交换!! x !! y)以反转矩阵上的光栅扫描顺序。

答案 3 :(得分:1)

另一个解决方案:

diagonals = map concat
          . transpose
          . zipWith (\ns xs -> ns ++ map (:[]) xs)
                    (iterate ([]:) [])

基本上,我们转向

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

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

然后是transposeconcat列表。对角线的顺序相反。

但这不是很有效,也不适用于无限列表。