Haskell中的Pascal三角

zzoitvuj  于 2023-02-04  发布在  其他
关注(0)|答案(5)|浏览(156)

我刚到 haskell ,我真的需要一些帮助!
我必须编写一个程序,其中包含一个递归函数,使用帕斯卡三角形技术生成n=12次幂的二项式系数列表。
我有一些想法在我的头上,但因为我刚刚开始,我不知道如何实现这一 haskell ?!
有人能帮帮我吗?

first row: (a+b)^0 = 1
second row: (a+b)^1 = 1a+1b
third row: (a+b)^2 = 1a^2+2ab+1b^2

等等......这是我的主要想法。但是我甚至不能尝试这个,因为我不知道我怎么把这个放进Haskell......总是出错

xmq68pz9

xmq68pz91#

首先为三角形中的每个元素分配一个索引:

| 0   1   2   3   4   5   6
--+--------------------------
0 | 1
1 | 1   1
2 | 1   2   1
3 | 1   3   3   1
4 | 1   4   6   4   1
5 | 1   5  10  10   5   1
6 | 1   6  15  20  15   6   1

这里我只是简单地把三角形放在一边,这样我们就可以给它们编号了,所以这里我会说在(6, 4)的元素是15,而(4, 6)不存在,现在重点写一个函数

pascal :: Integer -> Integer -> Integer
pascal x y = ???

这样你就可以生成这个三角形了。你可以写

pascal x y
    | x == 0 = 1
    | x == y = 1
    | x <  y = error "Not a valid coordinate for Pascal's triangle."
    | otherwise = pascal ? ? + pascal ? ?

请注意,这里不需要计算哪些元素应该通过对角线相加,而是可以通过直角坐标来计算。这里,您会注意到y是您所在的三角形中的哪一行,x是该行中元素的位置。您需要做的就是计算用什么来代替?
一旦你让它工作起来,我就有了一个更高效的三角形的一行程序,它可以一次生成整个三角形,同时仍然使用递归:

import Data.List (scanl1)

pascals :: [[Integer]]
pascals = repeat 1 : map (scanl1 (+)) pascals

不要试图把这个解交给你的教授,这不是他们想要的,如果你只做了一个星期的Haskell,这会让你很明显有人给了你这个解,然而,它确实显示了Haskell在这类问题上的强大功能,我将展示如何索引pascals来获得给定的(n, k)值,但是这样做也会给予你太多的解决简单递归的提示。
由于存在一些混淆,所以我给出这个解决方案的原因是在它和斐波那契数列的懒惰实现之间画一个平行线:

fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

fib 0 = 1
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)

这个定义生成了一个包含所有斐波那契数的无限列表,并且非常有效(从CPU的Angular 来看,RAM就是另一回事了)。它在前2个元素中编码了基本情况,然后是一个递归表达式,可以计算其余的。对于斐波那契,你需要2个值来开始你的工作,但对于帕斯卡三角形,你只需要一个值,这个值恰好是一个无限列表。在我上面发布的网格中,列之间有一个很容易看到的模式,scanl1 (+)函数正好利用了这个模式,并允许我们非常容易地生成它,但这是生成三角形的对角线,而不是行。要获得行,您可以索引这个列表,也可以使用takedrop和其他类似函数来实现一些有趣的技巧,但这是以后的练习。

xqkwcwgp

xqkwcwgp2#

从三角形本身开始:

1
    1 1
   1 2 1
  1 3 3 1
 1 4 6 4 1
    ...

您应该注意到,要写下下一行,必须应用以下规则:对前面行的相邻元素求和,对孤立的边缘元素使用0

0   1   0
     \+/ \+/
  0   1   1   0
   \+/ \+/ \+/
0   1   2   1   0
 \+/ \+/ \+/ \+/
  1   3   3   1
       ...

从操作上看,它看起来像这样:

For row 0:
[1]  (it's a given; i.e. base case)

For row 1:
[0, 1]   <- row 0 with a zero prepended ([0] ++ row 0)
 +  +
[1, 0]   <- row 0 with a zero appended  (row 0 ++ [0])
 =  =
[1, 1]   <- element-wise addition

For row 2:
[0, 1, 1]
 +  +  +
[1, 1, 0]
 =  =  =
[1, 2, 1]

Generally, for row N:

element-wise addition of:
  [0] ++ row(N-1)
  row(N-1) ++ [0]

记住,Haskell中列表的元素加法是zipWith (+)
因此,我们得出如下的 haskell 定义:

pascal 0 = [1]
pascal n = zipWith (+) ([0] ++ pascal (n-1)) (pascal (n-1) ++ [0])

或者用一种类似于著名的“懒惰谎言”的方式:

pascals = [1] : map (\xs -> zipWith (+) ([0] ++ xs) (xs ++ [0])) pascals
6vl6ewon

6vl6ewon3#

另一种可能的解决方案(在我看来更适合初学者):

pascal :: Integer -> [Integer]
pascal 0 = [1]
pascal 1 = [1, 1]
pascal n = let p = pascal (n - 1)
    in [1] ++ pascalStep p ++ [1]

pascalStep :: [Integer] -> [Integer]
pascalStep [] = []
pascalStep [_] = []
pascalStep (x:y:xs) = x + y : pascalStep (y : xs)

使用let可以避免more space usagepascal递归调用以查找所有前面的行,使用它们获取下一行,直到到达所需的行。
输出:

*Main> pascal 3
[1,3,3,1]
*Main> pascal 4
[1,4,6,4,1]
*Main> pascal 5
[1,5,10,10,5,1]
ssm49v7z

ssm49v7z4#

从基础案例开始。

pascal 0 0 = 1

然后处理边缘情况

pascal n 0 = 1
pascal n r | n == r = 1

现在使用递归步骤展开

pascal n r = pascal (n - 1) (r - 1) + pascal (n - 1) r

如果需要特定行的列表,请编写 Package 器

binom n = map (pascal n) [0..n]

弄清楚类型应该不难

pascal :: Integral a => a -> a -> a
binom :: Integral a => a -> [a]
yizd12fk

yizd12fk5#

我正在用手机,所以请原谅我的错误,但是你可以在这里以一种非常酷的方式使用Haskell的懒惰求值。

pascals :: [[Int]]
pascals = [1]:map (\r -> zipWith (+) (0:r) (r++[0])) pascals

你可以用叉子把它弄出来,但它相当深奥。

pascals :: [[Int]]
pascals = [1]:map ((zipWith (+) -<) (0:) (++[0])) pascals

但我个人真的很喜欢这段代码,认为它值得一看-

pascals :: [[Int]]
pascals = [1]:map next pascals
    where next = (zipWith (+) -<) (0:) (++[0])

但是这样的组合符可能会有点混乱,不管我有多喜欢无点编程。

相关问题