【问题标题】:How can I calculate the number of times a specific function will get executed in recursion?如何计算特定函数将在递归中执行的次数?
【发布时间】:2018-11-13 00:45:02
【问题描述】:

这个问题参考以下代码:

cost = [[1, 10, 75, 92],
         [-1, 0, 35, 50],
         [-1, -1, 0, 80],
         [-1, -1, -1, 0]]

def min_cost(source, destination):
   if s==d or s == d-1:
       return cost[s][d]
    mc = cost[s][d]
    for i in range(s+1, d):
        tmp = min_cost(s, i) + min_cost(i, d)
    if tmp < mc:
        mc = tmp
return mc

当我进行相同的试运行时,我看到 min_cost(1,3) 被执行了两次。我在一本书中读到,作者提到如果我们之间有 10 个站,那么 min_cost(1, 3) 将运行 144 次。

我们如何在不实际进行试运行的情况下计算出这些数字?我知道使用递归方程我们可以计算出函数所花费的时间,但是我们怎么能说一个特定的函数会被执行这么多次呢?

【问题讨论】:

  • 艾伦·图灵和哥德尔已经证明,如果不执行某个代码,您无法知道某个函数的执行次数。这个问题类似于 Halting 问题,即你无法提前知道程序在执行过程中是否会停止。 en.wikipedia.org/wiki/Halting_problem

标签: algorithm recursion analysis


【解决方案1】:

当一个人使用递归时,有几个选项可以计算这个数量。最简单的方法是将另一个变量添加到递归方法中,该变量每次递归时都会增加,并且在返回它的最后一个语句中它不会增加,而只是返回最后一个数量,这将递归“返回”到上部请求。

伪代码示例:

function recursionfunction(x, y, recursioncount) {
    if(expressionIsFalse) {
        recursionfunction(x, y, recursioncount+1);
    } else {
        return recursioncount;
    }
}

print recursionfunction('','', 0);

另一种工作方式是通过引用、指针或全局变量(取决于编程语言)分配变量并递增该计数器。

这对您有帮助吗?

【讨论】:

  • 我正在寻找一些数学解决方案,如果不运行程序我们可以看到并告诉相同的函数将在 RAM 的激活堆栈中执行这么多次。
  • 我不确定,但这听起来更像是调试器/监控处于活动状态的汇编程序/C++ 类型的活动。也许您必须研究作为其他应用程序的主进程运行的应用程序,以便主进程可以监视应用程序调用..但我不确定您是否会很快到达那里..
  • 上面的代码是递归的,如果你绘制递归调用堆栈,你可以使用这些或任何类似的度量来调用 min_cost(1, 3) 我认为应该有一种方法来推断跟踪和获取电话号码。
【解决方案2】:

虽然我知道您不希望只计算调用次数,但我还是想先做,看看发生了什么。所以,这里是:

def min_cost(s, d):
    global count
    count += 1
    if s==d or s == d-1:
        return cost[s][d]
    mc = cost[s][d]
    for i in range(s+1, d):
        tmp = min_cost(s, i) + min_cost(i, d)
    if tmp < mc:
        mc = tmp
    return mc

for n in range (2, 8):
    cost = [[0 for i in range (n)] for j in range (n)]
    count = 0
    min_cost(0, n-1)
    print (str (n) + ': ' + str (count))

输出是:

2: 1
3: 3
4: 9
5: 27
6: 81
7: 243

所以,我们看到 d-s=k 的调用次数是 (k-1) 的 3 次方。 知道我们必须证明什么有时会大大简化寻找证明的过程。


现在,证明本身。 它将是proof by induction。 首先,请注意min_cost(s, d) 的调用次数仅取决于d-s 的值,而不取决于sd 的单独值。

基础是,对于d-s=1,我们会接到一个电话。 对于d-s&gt;1,我们拨打了一个电话,并从中进行了以下调用:

min_cost(s, s+1) and min_cost(s+1, d)
min_cost(s, s+2) and min_cost(s+2, d)
...
min_cost(s, d-1) and min_cost(d-1, d)

所以,对于d-s=k,调用f(k) 的次数为:

f(k) = 1 +
       f(1) + f(k-1) +
       f(2) + f(k-2) +
       ... +
       f(k-1) + f(1)
     = 2 * (f(1) + f(2) + ... + f(k-1))

如果通过归纳假设,我们已经证明对于所有 v v,那么 f(k) 是:
1 + 2 * (31 + 32 + ... + 3k-1),即trivially3k,完成我们的证明。


最后,请注意,虽然所提出的算法是指数的,但基本问题可以在多项式时间内解决,最简单的是通过记忆我们已经完成所有工作的调用在 O((ds)^2) 内解决。

【讨论】:

  • 我认为你所做的计数器的想法是完美的。
  • @Gassa 是的,这也有一个多项式解决方案。我在这里的动机是提出假设,使用它我可以预测给定输入的调用次数。感谢您的回答。
  • @Gassa:非常好的直觉,用计数器运行代码,然后通过归纳证明“猜测”。当然,这适用于简单的公式。否则,一旦你有了递归关系,主定理(见 CLRS)应该会涵盖你。
【解决方案3】:

我认为一个位于函数外部的全局变量(如果在 Java 中是类的成员,或者在 C++/C 中是全局变量)并且每次调用它时将其值增加一,就可以解决问题。

【讨论】:

  • 问题制定者要求找到一种方法来解决这个问题,而无需进行试运行。
猜你喜欢
  • 1970-01-01
  • 2014-06-08
  • 2020-04-17
  • 2021-09-15
  • 1970-01-01
  • 2011-08-22
  • 2021-07-24
  • 1970-01-01
  • 2019-06-10
相关资源
最近更新 更多