Haskell中用列表相乘实现麦克劳林级数近似sin(x)的问题
嘿,你这个用麦克劳林级数近似sin(x)的练习思路超清晰啊!已经搭了部分代码框架,我来帮你把剩下的部分补全,顺便解释清楚每一步的逻辑~
先理清楚麦克劳林级数的核心公式
sin(x)的麦克劳林展开式是这样的:
sin(x) = x - x³/3! + x⁵/5! - x⁷/7! + ... = Σ(n从0到∞)[ (-1)^n * x^(2n+1) / (2n+1)! ]
你拆分的三个列表思路完全命中要点:
- 交替的正负系数序列:1, -1, 1, -1,...
- x的奇次幂序列:x¹, x³, x⁵,...
- 奇数的阶乘序列:1!, 3!, 5!,...
一步步完善你的代码
1. 先补全阶乘函数
你只写了开头的递归基例,完整的递归阶乘实现应该是这样:
factorial :: Integral a => a -> a factorial 0 = 1 factorial n = n * factorial (n - 1)
加上类型签名能让代码更严谨,也方便后续和其他函数兼容。
2. 生成三个核心序列
奇数序列(你已经写对了)
oddl :: Integral a => [a] oddl = [1, 3..]
这会生成无限的奇数序列:1,3,5,7,...
交替正负序列
用cycle函数就能轻松生成无限循环的正负交替序列:
alternatingSigns :: Num a => [a] alternatingSigns = cycle [1, -1]
x的奇次幂序列
你原来的powerl x = map (x^) oddl没问题,但如果x是浮点数的话,用**更适配浮点数幂运算,加上类型签名和类型转换更稳妥:
powerl :: Floating a => a -> [a] powerl x = map (\n -> x ** fromIntegral n) oddl
fromIntegral把整数类型的奇数转成浮点数,适配**的参数要求。
奇数的阶乘序列(转成浮点数方便后续除法)
先把奇数列表映射成阶乘,再转成浮点数:
oddFactorialsF :: Floating a => [a] oddFactorialsF = map (fromIntegral . factorial) oddl
3. 组合序列计算级数项
用zipWith3同时遍历三个序列,计算每一项的值:
sinTerms :: Floating a => a -> [a] sinTerms x = zipWith3 (\sign power fact -> sign * power / fact) alternatingSigns (powerl x) oddFactorialsF
这个函数会生成无限的级数项序列:x, -x³/3!, x⁵/5!, ...
4. 取前30项求和得到近似值
最后取前30项相加,就是sin(x)的近似结果:
approxSin :: Floating a => a -> a approxSin x = sum $ take 30 $ sinTerms x
完整代码整合
把所有部分拼起来就是完整的实现:
-- 生成无限奇数序列 oddl :: Integral a => [a] oddl = [1, 3..] -- 阶乘计算函数 factorial :: Integral a => a -> a factorial 0 = 1 factorial n = n * factorial (n - 1) -- 生成奇数的阶乘(转浮点数) oddFactorialsF :: Floating a => [a] oddFactorialsF = map (fromIntegral . factorial) oddl -- 生成x的奇次幂序列 powerl :: Floating a => a -> [a] powerl x = map (\n -> x ** fromIntegral n) oddl -- 生成交替正负系数序列 alternatingSigns :: Num a => [a] alternatingSigns = cycle [1, -1] -- 生成sin级数的每一项 sinTerms :: Floating a => a -> [a] sinTerms x = zipWith3 (\sign power fact -> sign * power / fact) alternatingSigns (powerl x) oddFactorialsF -- 取前30项求和得到sin(x)近似值 approxSin :: Floating a => a -> a approxSin x = sum $ take 30 $ sinTerms x -- 测试示例 main = do print $ approxSin (pi/2) -- 输出接近1.0 print $ approxSin pi -- 输出接近0.0
小优化建议(可选)
如果想让阶乘计算更高效,可以用递推的方式生成阶乘序列,避免重复递归计算:
-- 递推生成所有数的阶乘序列:1!,2!,3!,4!... factorials :: Integral a => [a] factorials = scanl (*) 1 [1..] -- 提取奇数位置的阶乘:1!,3!,5!... oddFactorialsF' :: Floating a => [a] oddFactorialsF' = map fromIntegral $ everyOther factorials where everyOther (_:y:xs) = y : everyOther xs everyOther _ = []
替换之前的oddFactorialsF即可,惰性求值下性能会更好一些。
内容的提问来源于stack exchange,提问作者user3076623
相关产品推荐
相关产品推荐

