在 Haskell 中从矩阵中提取对角线的最佳方法是什么?
我被要求编写一个函数来提取存储为列表列表的矩阵的对角线。第一个版本是通过索引列表来提取数字,但我很快就得出结论,这对于 Haskell 来说不是一个好的算法,并编写了另一个函数:
getDiagonal :: (Num a) => [[a]] -> [a]
getDiagonal [[]] = []
getDiagonal (xs:[]) = [head xs]
getDiagonal (x:xs) = head x : getDiagonal (map tail xs)
因为我才开始学习 Haskell,所以我不确定它是否是用惯用语编写的方式或者它是否会表现良好。
所以我的问题是,是否有更好的方法从存储在这种表示形式中的矩阵中提取对角线,或者如果没有,如果使用高阶 Haskell 概念(如代数类型)表示矩阵,是否可以构建更好的算法? 另外,在模式匹配中解构列表(如 ((x:_):xs) 或使用上面所示的 head 函数解构列表之间是否存在性能差异?
编辑:实际上更多的是好奇的询问而不是家庭作业,他们在这里的技术大学不教授函数式编程(我认为这很遗憾),但我会留下标签。
I was asked to write a function that would extract the diagonal of a matrix stored as a list of lists. The first version was to extract the number by indexing the lists, but I soon concluded it isn't a good algorithm for Haskell and wrote another function:
getDiagonal :: (Num a) => [[a]] -> [a]
getDiagonal [[]] = []
getDiagonal (xs:[]) = [head xs]
getDiagonal (x:xs) = head x : getDiagonal (map tail xs)
Since I've only started learning Haskell I'm not sure if it's written in an idiomatic way or if it would perform well.
So my question is is there any better way to extract a diagonal from matrix stored in such a representation or if there isn't is there a better algorithm that could be constructed if the matrix was represented using higher order Haskell concepts, like algebraic types?
Also is there any performance difference between deconstructing the list in pattern matching like so ((x:_):xs) or with the head function like shown above?
EDIT: Actually more of curious inquiry than a homework, they don't teach functional programming at technical universities here (which is a pitty I think), but I'll leave the tag.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我认为如果您可以假设参数是方阵,那么使用索引就可以了。无论如何,使用这种表示形式获取对角线的时间复杂度为 O(N2),因为您必须遍历列表。
I think using indexing is OK, if you can assume that the argument is a square matrix. Getting diagonal with this representation is O(N2) anyway, since you have to traverse the lists.
您可以将原始定义简化为:
使用索引并没有太大的错误,这可以让您进一步简化为:
基于数组的表示
您还可以使用 Data.Array 由
(i,j)
索引。这使您几乎可以逐字使用主对角线的数学定义:您可以这样使用:
效率
之前的
mainDiagonal
定义仍然效率不高:它仍然需要 O(N²) 次测试我==j。类似于 zipWith 版本,它可以被修复并概括如下:
该版本仅索引数组 O(N) 次。 (作为奖励,它也适用于矩形矩阵,并且独立于索引基数。)
You can simplify your original definition to:
There isn't much wrong with using indexing for this, which lets you simplify it further to:
Array-based representation
You can also represent matrixes using Data.Array indexed by
(i,j)
. This lets you use the mathematical definition of the main diagonal almost verbatim:You can use this like:
Efficiency
The previous definition of
mainDiagonal
is still not efficient: it still needs O(N²) tests ofi == j
. Analogous to thezipWith
version, it can be fixed and generalized as follows:This version only indexes the array O(N) times. (As a bonus, it also works with rectangular matrices, and is independent of the indexing base.)
sdcwc 已经回答了原来的问题。我想指出的是,将矩阵表示为列表的列表通常效率很低。当长度未知时,列表是很好的选择,矩阵通常具有固定大小。您可以考虑使用平面关联列表或映射来构建矩阵,以及当您实际使用此矩阵运行计算时具有恒定元素访问时间的任何内容。
Data.Array
是一个不错的选择(参见 Piet 的回答)。如果您在 Haskell 中运行数值计算,则可以使用
hmatrix
包。它有自己的矩阵数据类型,Data.Packed.Matrix
,它有一个takeDiag
函数来提取对角线。Data.Packed.Matrix 示例
例如,如果
m
是您的矩阵,那么您可以像这样提取其对角线:
sdcwc has answered the original question. I'd like to note that representing the matrix as a list of lists is usually inefficient. Lists are good where the length is unknown, matrices are usually of the fixed size. You may consider using a flat associative list or map to build the matrix, and anything with constant element access time when you actually run calculations with this matrix.
Data.Array
is a good choice (see Piet's answer).If you run numerical calculations in Haskell, you can use
hmatrix
package. It has its own matrix data type,Data.Packed.Matrix
, and it has atakeDiag
function to extract the diagonal.Data.Packed.Matrix example
For example, if
m
is your matrixthen you can extract its diagonal like this:
如果性能不那么相关并且您选择这种方法,只需补充一点 Delport 的答案:
应该注意的是,由于
head
和 <,该函数只能与您知道是方阵的矩阵一起使用代码>尾用法。对于
n X m
矩阵,您可以实现忽略额外值的函数,使[[1,2,3], [4,5,6]]
返回 < code>[1,5]:或者实现一个当你的矩阵不是正方形时会抛出错误的版本:
对于这两个答案,应该注意的是,如果你的“列表”,它们仍然可能会抛出“意外”错误由于
map tail mTail
调用,lists' 不是矩阵/不统一 ([[1,2], [1]]
)。Just complementing Delport's answer a little bit, if performance is not that relevant and you choose this approach:
It should be noted that this function can only be used with matrices you know are square, due to
head
andtail
usage.For
n X m
matrices you can either implement the function to ignore extra values, making[[1,2,3], [4,5,6]]
return[1,5]
:Or implement a version that will throw an error when your matrix is not square:
For both answers, it should be noted that they might still throw 'unexpected' errors if your 'list of lists' is not a matrix/not uniform (
[[1,2], [1]]
) due to themap tail mTail
call.