【问题标题】:Optimal algorithm to calculate the result of a continued fraction计算连分数结果的最佳算法
【发布时间】:2010-07-24 18:45:23
【问题描述】:

连分数是一系列这样的除法:

depth   1    1+1/s

depth   2    1+1/(1+1/s)

depth   3    1+1/(1+1/(1+1/s))
  .     .      .           
  .     .      .      
  .     .      . 

深度是整数,但s 是浮点数。

对于这种具有大深度的分数,计算结果的最佳算法(性能方面)是什么?

【问题讨论】:

  • 这不是作业,因为我的学校去年七月放假了,现在我坐在家里,我只是想解决一些问题
  • 由于您很可能希望自己解决这个问题,为了获得最佳的学习效果,我认为将这个问题当作家庭作业来处理是很好的。

标签: c algorithm math


【解决方案1】:

提示:使用基本代数展开每个公式。你会看到一个模式出现。

我将向您展示最初的步骤,以便显而易见:

f(2,s) = 1+1/s = (s+1)/s
f(3,s) = 1+1/f(2,s) = 1+(s/(s+1)) = (1*(s+1) + s)/(s+1) = (2*s + 1) / (s + 1)
         /* You multiply the first "1" by denominator */
f(4,s) = 1+1/f(3,s) = 1+(s+1)/(2s+1) = (1*(2*s+1) + (s+1))/(2*s+1) = (3*s + 2) / (2*s + 1)
f(5,s) = 1+1/f(4,s) = 1+(2s+1)/(3s+2) = (1*(3*s+2) + (2s+1))/(3*s+2) = (5*s + 3) / (3*s + 2)

...

提示2:如果您没有看到上述明显的模式,最佳算法将涉及计算斐波那契数(因此您需要谷歌搜索最佳斐波那契#生成器)。

【讨论】:

  • 注意:没有学习过英语的基本代数,我希望我没有在上面的评论中混淆“分母”和“提名者” - 希望这些公式对自己来说足够明显,即使我做了混淆术语。
  • 不,我相信你说得对。分子是除法“顶部”的数字,分母在“底部”。 IE, 2/3 -> 分子 = 2;分母 = 3
【解决方案2】:

我想详细说明一下DVK's excellent answer。我将坚持使用他的符号f(d,s) 来表示深度的寻求值d

如果您为大的d 计算值f(d,s),您会注意到这些值随着d 的增加而收敛。

令 φ=f(∞,s)。也就是说,φ 是d 接近无穷大时的极限,是完全展开的连分数。请注意,φ 包含其自身的副本,因此我们可以写成 φ=1+1/φ。两边都乘以φ并重新排列,我们得到二次方程

φ2 - φ - 1 = 0

可以解决得到

φ = (1 + √5)/2。

这是famous golden ratio

随着 d 变大,您会发现 f(d,s) 非常接近 φ。

但是等等。还有更多!

正如 DVK 所指出的,f(d,s) 的公式涉及斐波那契数列中的项。特别是,它涉及斐波那契数列的连续项的比率。有一个closed form expression for the nth term of the sequence,即

n-(1-φ)n)/√5.

由于 1-φ 小于 1,因此 (1-φ)n 会随着 n 变大而变小,因此第 n 个斐波那契项的良好近似值是 φn/√5.回到 DVK 的公式,斐波那契数列中连续项的比率将趋向于 φn+1n = φ。

因此,这是了解此问题中的连分数计算为 φ 的事实的第二种方法。

【讨论】:

    【解决方案3】:

    闻起来像尾递归(recursion(recursion(...)))。

    (换句话说 - 循环它!)

    【讨论】:

    • 一般C不支持尾递归。
    • @James Black 但是任何递归算法都可以展开 :-) 它不会改变运行时复杂度。 (我并不是说这是“最佳”算法的好答案。)
    • @James Black:正如@pst 提到的,我提倡将尾递归问题(在概念级别)转换为循环(在代码级别),这可证明是一个相当微不足道的过程。这就是我的“循环它”评论的用武之地,尽管我想它本来可以更清楚。
    • @Chris - 考虑到他使用的语言,我只是不想让他开始将尾递归视为一种可能的解决方案。但循环播放可能是最好的选择。
    • @James Black:大多数现代 C 编译器都可以很好地进行尾递归优化,在某些情况下甚至会将您的非尾递归代码转换为尾递归形式。
    【解决方案4】:

    我将从计算1/s 开始,我们将其称为a

    然后使用 for 循环,因为如果使用递归,在 C 中可能会遇到堆栈溢出。

    由于这是作业,我不会给出太多代码,但是,如果你从一个简单的循环开始,从 1 开始,然后不断增加它,直到达到 4,然后你可以只循环 n 次。

    由于您总是要对1/s 进行除法,而且除法成本很高,因此只需执行一次即可提高性能。

    我希望,如果你想出办法,你实际上可以找到一种模式来帮助你进一步优化。

    您可能会发现这样的文章:http://www.b-list.org/weblog/2006/nov/05/programming-tips-learn-optimization-strategies/,对您有所帮助。

    我假设在性能方面你的意思是你希望它很快,不管使用的内存是多少,顺便说一句。

    您可能会发现,如果您在每一步缓存您计算的值,您可以重复使用它们,而不是重新进行昂贵的计算。

    我个人会手动做 4-5 步,写下每一步的方程式和结果,看看是否出现了任何规律。

    更新:

    GCC 增加了尾递归,但我从来没有注意到它,因为我试图在 C 中严格限制递归,这是出于习惯。但是这个答案对 gcc 基于优化级别所做的不同优化有一个很好的快速解释。

    http://answers.yahoo.com/question/index?qid=20100511111152AAVHx6s

    【讨论】:

    • 您的算法绝对不是最优的,尽管它是该特定算法的最优实现。
    • @DVK - 我不是想给出最佳算法,因为它被列为家庭作业,而是指出一种可能找到更好算法的方法,这就是为什么我建议做几个逐步找到模式,以便找到比我建议的更好的解决方案。
    猜你喜欢
    • 2010-10-08
    • 2013-03-17
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-23
    • 1970-01-01
    相关资源
    最近更新 更多