如何使用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)
答案 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)
实现这一目标的一种方法是通过递归。
我会问你一些重要的问题,因为我还没看到你到目前为止所做的事情:
或伪代码:
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]
]
这是一个矩形矩阵。方阵将使用i
和j
的相同边界。
如果您要使用这样的列表推导来创建方形矩阵,那么您将在|
的左侧放置什么表达式来创建单位矩阵?换句话说,你可以用i
和j
来表达行i
列j
的身份矩阵值吗?
答案 2 :(得分:2)
如果在Haskell中需要一些迭代方法,则使用递归。这意味着我们需要定义基本案例和归纳案例。
这里有两个基本案例:
Nothing
; Just
:1
有一个归纳案例:如果数字大于1
,我们首先为n-1
生成单位矩阵,然后我们在顶部添加行,在左侧添加一列:
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
这意味着我们需要在0
之前添加前一个矩阵的所有行,并在矩阵前面加上一个包含1
和n-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<0
和n==1
)