我是Haskell的新手,我真的需要一些帮助!
我必须编写一个包含递归函数的程序,使用Pascal的三角形技术生成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中的......一直都在犯错误
答案 0 :(得分:8)
首先为三角形中的每个元素指定一个索引:
| 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)
值,但这样做也会给你太多提示来解决天真的递归。
由于存在一些混淆,我给出这个解决方案的原因是在它与Fibonacci序列经常显示的惰性实现之间画一个平行线:
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
与
相比fib 0 = 1
fib 1 = 1
fib n = fib (n - 1) + fib (n - 2)
这个定义生成了所有Fibonacci数的无限列表,并且非常有效(从CPU的角度来看,RAM是一个不同的故事)。它在前两个元素中编码基本情况,然后是一个可以计算其余部分的递归表达式。对于Fibonaccis,您需要2个值才能启动,但对于Pascal的三角形,您只需要一个值,该值恰好是无限列表。我在上面发布的网格中的列中有一个易于查看的模式,scanl1 (+)
函数只是利用了这种模式并允许我们非常容易地生成它,但这会产生三角形的对角线而不是比行。要获取行,您可以为此列表编制索引,或者您可以使用take
,drop
和其他此类函数执行一些花哨的技巧,但这是另一天的练习。
答案 1 :(得分:4)
从三角形本身开始:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
...
你应该注意到要记下下一行,你必须应用这个规则:总结前面的行&#39;相邻元素,使用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])
或者以类似着名的&#34; lazy fibs&#34;:
的方式pascals = [1] : map (\xs -> zipWith (+) ([0] ++ xs) (xs ++ [0])) pascals
答案 2 :(得分:0)
从基础案例开始。
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
如果您想要特定行的列表,请编写包装器
binom n = map (pascal n) [0..n]
确定类型不应该很难
pascal :: Integral a => a -> a -> a
binom :: Integral a => a -> [a]
答案 3 :(得分:0)
另一种可能的解决方案(在我看来更适合初学者):
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 usage。
pascal
以递归方式调用以查找所有先前的行,使用它们获取下一行,直到到达所需的行。
输出:
*Main> pascal 3
[1,3,3,1]
*Main> pascal 4
[1,4,6,4,1]
*Main> pascal 5
[1,5,10,10,5,1]