如何在Haskell中扩展矩阵

如何在Haskell中扩展矩阵,haskell,matrix,Haskell,Matrix,我一直在尝试创建一个函数,该函数返回给定的矩阵,并添加一行和一列,其数字是旁边行/列中数字的总和 数字是列中数字的总和 例如 到 我的第一个想法是 > extend1 :: Int -> Array (Int,Int) Int > extend1 ((a,b),(c,d)) = n where > n = array ((a,b),(c,d),(n,n)) 因为我必须使用“数组”。现在我不知道如何继续下去。这是正确的还是完全错误的?使用

我一直在尝试创建一个函数,该函数返回给定的矩阵,并添加一行和一列,其数字是旁边行/列中数字的总和 数字是列中数字的总和

例如

我的第一个想法是

> extend1 :: Int -> Array (Int,Int) Int
> extend1 ((a,b),(c,d))  = n where
>                 n = array ((a,b),(c,d),(n,n))

因为我必须使用“数组”。现在我不知道如何继续下去。这是正确的还是完全错误的?

使用元组表示法,而不太注意一般性,这里有一个解决问题的方法

type Square2 a = ((a,a), (a,a))
type Square3 a = ((a,a,a), (a,a,a), (a,a,a))

extend1 :: Num a => Square2 a -> Square3 a
extend1 ((a,b), (c,d)) = 
  ( (a,  b,  ab  )
  , (c,  d,  cd  )
  , (ac, bd, abcd) ) 

  where
    ab   = a  + b
    cd   = c  + d
    ac   = a  + c
    bd   = b  + d
    abcd = ab + cd
请注意,我如何使用从输入模式和
where
子句中的定义中挑选的变量定义。还要注意我是如何在构建一个全新的输出时销毁和使用输入的——一些元素被重用,但结构本身被销毁了。最后请注意,我如何选择元组内部的类型为常量,并受
Num
typeclass的约束,该类允许我使用
(+)
进行添加

一个类似的函数,一般化并使用
Array
s,其类型如下

extend1A :: Num a => Array (Int,Int) a -> Array (Int, Int) a
我们无法知道
数组
的确切大小,但这表明我们的函数获取一个大小不同的
数组
,并返回另一个索引相同且包含相同
Num
约束类型的
数组

array
函数的第一个参数是一组边界。我们需要根据输入数组的
边界
更新这些值

extend1A ary0 = array bounds1 ... where
  ((minX, minY), (maxX, maxY)) = bounds ary0
  (maxX1, maxY1) = (succ maxX, succ maxY)
  bounds1 = ((minX, minY, (maxX1, maxY1))
然后,
array
将“assocs”列表作为其第二个参数。我们可以将任何
数组ix a
视为(大致)等同于一个assocs列表
[(ix,a)]
,其中列出的值表示“在索引
ix
处,值为
a
”。要做到这一点,我们必须知道我们刚才管理的
ix
类型的
界限

要使用旧数组中的信息更新数组,我们可以修改旧数组的
assocs
,以包含新信息。具体地说,这意味着
extend1A
看起来有点像

extend1A ary0 = array bounds1 (priorInformation ++ extraInformation) where
  priorInformation = assocs ary0
  extraInformation = ...
  ((minX, minY), (maxX, maxY)) = bounds ary0
  (maxX1, maxY1) = (succ maxX, succ maxY)
  bounds1 = ((minX, minY, (maxX1, maxY1))
如果
额外信息
为空(
[]
),则
扩展的1a位
在输入的
范围内的所有标记上等于
,在其范围外的所有标记上等于
未定义的
。我们需要用求和信息填写
额外信息

extend1A ary0 = array bounds1 (priorInformation ++ extraInformation) where
  priorInformation = assocs ary0 
  extraInformation = xExtension ++ yExtension ++ totalExtension
  xExtension = ...
  yExtension = ...
  totalExtension = ...
  ((minX, minY), (maxX, maxY)) = bounds ary0
  (maxX1, maxY1) = (succ maxX, succ maxY) 
  bounds1 = ((minX, minY, (maxX1, maxY1))
如果我们考虑将数组扩展为三个部分,则
extend1
中由
ab
cd
标记的
xExtension
,然后,我们可以依次计算
extend1
中由
ac
bd
标记的
yExtension
extend1
中由
abcd
标记的
totalExtension

totalExtension
最简单——它只是
prioriformation
中每个
(i,a)
对的“值分量”之和。它也可以是
xExtension
yExtension
的“值组件”之和,但为了尽可能明显地正确,我们将选择第一个选项并将其安装在右下角

extend1A ary0 = array bounds1 (priorInformation ++ extraInformation) where
  priorInformation = assocs ary0
  extraInformation = xExtension ++ yExtension ++ totalExtension
  sumValues asscs = sum (map snd asscs)
  xExtension = ...
  yExtension = ...
  totalExtension = [((maxX1, maxY1), sumValues priorInformation)]
  ((minX, minY), (maxX, maxY)) = bounds ary0
  (maxX1, maxY1) = (succ maxX, succ maxY)
  bounds1 = ((minX, minY), (maxX1, maxY1))
请注意,我们可以使用
where
子句来定义新函数,如
sumValues
,它将反复出现

然后,我们可以将扩展计算为
优先信息
上的列表理解。我们需要在旧的ASSOC上收集一种特殊的求和——在一个索引固定的情况下,求所有值的和

xExtension = [( (maxX1, yix)
              , sumValues (filter (\((_, j), _) -> j == yix) priorInformation)
              )
             | yix <- [minY .. maxY]
             ]

yExtension = [( (xix, maxY1)
              , sumValues (filter (\((i, _), _) -> i == xix) priorInformation)
              )
             | xix <- [minX .. maxX]
             ]
xExtension=[((maxX1,yix)
,sumValues(过滤器(\((\uj),\ux)->j==yix)优先信息)
)
|yix i==xix)优先信息)
)
|xix j==yix)优先信息)
)
|yix i==xix)优先信息)
)

|xix首先,您应该了解如何与haskell数组进行接口。
数组
数据类型可在
数据.数组
中找到,因此有关完整的详细信息,请查看该模块的文档

请注意,我省略了对所有这些函数的
ixi
约束,这对于这种情况并不重要

bounds::Array i e->(i,i)
:此函数返回数组的最小和最大索引。对于1D数组,这些只是数字。对于2D数组,它位于左上角和右下角(对于矩阵)

array::(i,i)->[(i,e)]->array i e
:此函数从最小/最大对为边界创建一个数组,以及一个关联列表;即从索引到值的映射。您的初始示例可以写成
array((0,0),(1,1))[(0,0),1),(0,1),2),(1,0),3),(1,1),4)]

assocs::Array i e->[(i,e)]
:这是
Array
的“反面”。因此,
arr==Array(bounds arr)(assocs arr)

现在,函数:

extendArray :: Num e => Array (Int, Int) e -> Array (Int, Int) e
extendArray arr = 
    let
      arr' = assocs arr
      ((xMin, yMin), (xMax, yMax)) = bounds arr
      newCol = [ ((n, yMax + 1) , sum [ v | ((x,_),v) <- arr', x == n] ) | n <- [xMin .. xMax]]
      newRow = [ ((xMax + 1, n) , sum [ v | ((_,y),v) <- arr', y == n] ) | n <- [yMin .. yMax]]
      newCorner = [((xMax + 1, yMax + 1), sum $ map snd arr')]
      newArr = array ((xMin, yMin), (xMax + 1, yMax + 1)) (arr' ++ newCol ++ newRow ++ newCorner)

    in newArr
请注意,此实现效率不高(无论多么简单)。在
newRow
中,我们遍历整个矩阵
xMax-xMin
次(每个
n
值一次)。因为
assocs
总是以相同的顺序返回元素(行从左到右,列从上到下)最好将列表
arr'
拆分为每个列表的长度
yMax-yMin
;这样可以得到一个行列表。但我将把这个优化留给您。

使用hmatrix:

import Numeric.LinearAlgebra
import Numeric.LinearAlgebra.Util(col,row)

m = (2><2) [1 , 2
           ,3 , 4] :: Matrix Double


extend m = fromBlocks [[m ,sr]
                      ,[sc, s]]
  where
    sr = col $ map sumElements (toRows m)
    sc = row $ map sumElements (toColumns m)
    s  = scalar (sumElements sr)


main = do
    print m
    print $ extend m


(2><2)
 [ 1.0, 2.0
 , 3.0, 4.0 ]
(3><3)
 [ 1.0, 2.0,  3.0
 , 3.0, 4.0,  7.0
 , 4.0, 6.0, 10.0 ]
导入Numeric.linearlgebra
导入Numeric.LinearAlgebra.Util(列,行)
m=(2>大多数矩阵库(据我所知)使用固定大小的数组,因此为了扩展一个数组,您必须构造一个正确大小的数组,并将原始数组的内容复制到新数组中。这基本上就是您在这里要做的事情,尽管这不会编译,因为您将
n
重新用作数组和el
extend1A ary0 = array bounds1 (priorInformation ++ extraInformation) where
  priorInformation = assocs ary0
  extraInformation = xExtension ++ yExtension ++ totalExtension
  xExtension = [( (maxX1, yix)
                , sumValues (filter (\((_, j), _) -> j == yix) priorInformation)
                )
               | yix <- [minY .. maxY]
               ]
  yExtension = [( (xix, maxY1)
                , sumValues (filter (\((i, _), _) -> i == xix) priorInformation)
                )
               | xix <- [minX .. maxX]
               ]
  totalExtension = [((maxX1, maxY1), sum xExtension)]
  ((minX, minY), (maxX, maxY)) = bounds ary0
  (maxX1, maxY1) = (succ maxX, succ maxY)
  bounds1 = ((minX, minY), (maxX1, maxY1))
extendArray :: Num e => Array (Int, Int) e -> Array (Int, Int) e
extendArray arr = 
    let
      arr' = assocs arr
      ((xMin, yMin), (xMax, yMax)) = bounds arr
      newCol = [ ((n, yMax + 1) , sum [ v | ((x,_),v) <- arr', x == n] ) | n <- [xMin .. xMax]]
      newRow = [ ((xMax + 1, n) , sum [ v | ((_,y),v) <- arr', y == n] ) | n <- [yMin .. yMax]]
      newCorner = [((xMax + 1, yMax + 1), sum $ map snd arr')]
      newArr = array ((xMin, yMin), (xMax + 1, yMax + 1)) (arr' ++ newCol ++ newRow ++ newCorner)

    in newArr
>let x = array ((0,0),(1,1)) [((0,0),1),((0,1),2),((1,0),3),((1,1),4)]
>x
array ((0,0),(1,1)) [((0,0),1),((0,1),2),((1,0),3),((1,1),4)]
>extendArray x
array ((0,0),(2,2)) [((0,0),1),((0,1),2),((0,2),3),((1,0),3),((1,1),4),((1,2),7),((2,0),4),((2,1),6),((2,2),10)]
import Numeric.LinearAlgebra
import Numeric.LinearAlgebra.Util(col,row)

m = (2><2) [1 , 2
           ,3 , 4] :: Matrix Double


extend m = fromBlocks [[m ,sr]
                      ,[sc, s]]
  where
    sr = col $ map sumElements (toRows m)
    sc = row $ map sumElements (toColumns m)
    s  = scalar (sumElements sr)


main = do
    print m
    print $ extend m


(2><2)
 [ 1.0, 2.0
 , 3.0, 4.0 ]
(3><3)
 [ 1.0, 2.0,  3.0
 , 3.0, 4.0,  7.0
 , 4.0, 6.0, 10.0 ]