Haskell脚本解决单位矩阵

时间:2018-05-16 15:14:17

标签: haskell

如何使用Haskell脚本解决矩阵的身份? 例如,如果使用此给定类型

 type Matrice a = [[a]]
 identity :: Int -> Maybe (Matrice Int)

如何返回给定大小的标识矩阵?我知道单位矩阵是一个方形矩阵,除了左上角到右下角的值都是1之外,所有值都为零。条件为,如果大小小于1,则不定义标识矩阵并返回Nothing。

例如说,

Prelude > identity 5
          Just [[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1]]
Prelude > identity 2
          Just [[1,0],[0,1]]

我试过

identity1 :: Int -> Int -> [Int]
identity1 a b
    | a == 0 []
    | b == 0 (1:identity (a-1) (-1))
    | otherwise = (0:identity' (a-1) (b-1))

identity2 :: Int -> Int -> Matrice Int
identity2 a b
    | b == 0 []
    | otherwise = (0:identity1 (a-1) (b-1) : identity2 a (b-1) 

4 个答案:

答案 0 :(得分:6)

一种简短的方法是将“无限”单位矩阵定义为

ii = (1 : repeat 0) : fmap (0:) ii

第一行是1 0 0 ...;每个后续行都是它上面的一行,前面加上一个0。

很明显,无限单位矩阵的前n列的前n行是 I n

1 | 0 | 0 | 0 | 0 | 0 |
--+   |   |   |   |   |
0   1 | 0 | 0 | 0 | 0 |
------+   |   |   |   |
0   0   1 | 0 | 0 | 0 |
----------+   |   |   |  ...
0   0   0   1 | 0 | 0 |
--------------+   |   |
0   0   0   0   1 | 0 |
------------------+   |
0   0   0   0   0   1 |
----------------------+
           .            .
           .              .
           .                .

鉴于此,我们只使用take来获得适当大小的子矩阵。应用于每一行的take n将返回第一个n列,应用于结果的take n只会返回第一行n

type Matrix a = [[a]]

identity :: Int -> Maybe (Matrix Int)
identity n | n <= 0 = Nothing
           | otherwise = let ii = (1:repeat 0) : (fmap (0:) ii)
                         in Just $ take n (take n <$> ii)

如果递归定义的无限列表将你的大脑绑定在结中,你也可以定义一个enlarge函数来生成 n +1 < / sub> from I n 。为此,可以方便地假设 I 0 存在并表示为空列表。

enlarge :: Matrix Int -> Matrix Int
enlarge [] = [[1]]
enlarge i@(r:_) = (1:(0<$r)) : fmap (0:) i

然后,您可以通过索引无限的标识矩阵列表来定义identity :: Int -> Matrix Int

identity n | n <= 0 = Nothing
identity n = Just (identities !! n)

其中identities :: [Matrix Int]使用iterate

构建
identities = iterate enlarge []

Data.List.unfoldr

identities = unfoldr (\x -> Just (x, enlarge x)) []

值得注意的是,无限单位矩阵是enlarge的固定点:

import Data.Function
ii = fix enlarge

答案 1 :(得分:4)

实现这一目标的一种方法是通过递归。

我会问你一些重要的问题,因为我还没看到你到目前为止所做的事情:

  • 1x1矩阵的标识是什么?
  • 鉴于nxn矩阵的身份,你需要添加什么才能为(n + 1)x(n + 1)矩阵创建身份?

或伪代码:

identity 1 = Just $ _1x1_identity
  -- _1x1_identity :: [[Int]]

identity n_plus_1 | n_plus_1 > 1 = fmap _alter_identity (identity n)
  where n = n_plus_1 - 1
  -- _alter_identity :: [[Int]] -> [[Int]]

identity n | n < 1 = Nothing

如果您不熟悉fmap,则此处将其用于打开/重新包装从另一个Maybe调用返回的identity值。

我可以像

那样更加冗长
identity n_plus_1 | n_plus_1 > 1 = case identity n of
    Nothing     -> Nothing
    Just matrix -> Just (_alter_identity matrix)
  where n = n_plus_1 - 1

你在评论中的方法试图逐行构建整个矩阵,这也是可行的。

更直接地实现该方法的一种方法是通过列表理解。

列表推导使得定义新列表和列表列表变得容易:

Prelude> [ i | i <- [0..4] ]
[0,1,2,3,4]
Prelude> [ [(i,j) | j <- [0..3]] | i <- [0..2] ]
[ [(0,0),(0,1),(0,2),(0,3)]
, [(1,0),(1,1),(1,2),(1,3)]
, [(2,0),(2,1),(2,2),(2,3)]
]

上面我们可以看到我们可以使用列表推导来生成坐标矩阵 - 值(i,j)显示在i&#39行和j第二栏。

列表推导允许您在|的左侧放置任意表达式,因此我可以i + j代替(i,j)来获得一个非常不同的矩阵:< / p>

Prelude> [ [i + j | j <- [0..3]] | i <- [0..2] ]
[ [0,1,2,3]
, [1,2,3,4]
, [2,3,4,5]
]

这是一个矩形矩阵。方阵将使用ij的相同边界。

如果您要使用这样的列表推导来创建方形矩阵,那么您将在|的左侧放置什么表达式来创建单位矩阵?换句话说,你可以用ij来表达行ij的身份矩阵值吗?

答案 2 :(得分:2)

如果在Haskell中需要一些迭代方法,则使用递归。这意味着我们需要定义基本案例和归纳案例。

这里有两个基本案例:

  1. 该值小于或等于零,在这种情况下,值为Nothing;
  2. 值恰好为1的情况,在这种情况下,我们返回带有1×1矩阵的Just
  3. 1

    有一个归纳案例:如果数字大于1,我们首先为n-1生成单位矩阵,然后我们在顶部添加行,在左侧添加一列:

    1 0 0 0
    0 1 0 0
    0 0 1 0
    0 0 0 1

    这意味着我们需要在0之前添加前一个矩阵的所有行,并在矩阵前面加上一个包含1n-1个零的列表。

    让我们首先忘记第一个基本情况(n小于或等于零),并假设n总是严格为正。在这种情况下,不需要将值包装在Maybe中,因此我们首先构造一个函数:

    identity' :: Int -> Matrix Int
    identity' = ...
    

    所以基本情况是参数为1

    identity' 1 = ...
    

    和归纳案例有形:

    identity' n = first_row : map prepend_zero (identity (n-1))
        where first_row = ...
              prepend_zero = ...
    

    现在我们可以用identity来构造identity',只检查一下这个值是否小于或等于零:

    identity :: Int -> Maybe (Matrix Int)
    identity n | n <= 0 = Nothing
               | otherwise = Just (identity' n)
        where identity' 1 = ...
              identity' n = first_row : map prepend_zero (identity (n-1))
                  where first_row = ...
                        prepend_zero = ...
    

    我将表达式(...)作为一个应该合理的练习。

答案 3 :(得分:1)

解构

identity n = splitEvery n $ (concat $ replicate (n-1) $ 1: replicate n 0)++[1]

无言证明

[[1,0,0,0,0],[0,1,0,0,0],[0,0,1,0,0],[0,0,0,1,0],[0,0,0,0,1]]  ~
[1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1]            ~
[1,0,0,0,0,0],[1,0,0,0,0,0],[1,0,0,0,0,0],[1,0,0,0,0,0] ++ [1] ~
[1,0,0,0,0,0]{4} ++ [1]                                        ~
(1:[0]{5}){4} ++ [1]

您需要处理特殊情况(n<0n==1