20

This question is in reference to below code:

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

When I did a dry run of the same I saw min_cost(1,3) is getting executed two times. I read in one book where the author mentioned if we had 10 stations in between, then min_cost(1, 3) would run 144 times.

How can we figure out these numbers without actually doing a dry run? I know using recursion equations we can figure out the time taken by the function, but how can we say that a specific function will be executed this many number of times?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Raghav salotra
  • 820
  • 1
  • 11
  • 23
  • 3
    Alan Turing and Gödel have proven that you can't know the number of executions of a function in a certain code without executing it. This problem is analogous to the Halting problem, that states that you cannot know in advance wether a program will halt or not during the execution. https://en.wikipedia.org/wiki/Halting_problem – David Lemon Jun 12 '18 at 14:40

3 Answers3

14

While I understand that you don't want a dry run to just count the calls, I'd like to nevertheless do it first, just to see what's going on. So, here goes:

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))

The output is:

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

So, we see that the number of calls for d-s=k is 3 to the power of (k-1). Knowing what we have to prove sometimes greatly simplifies finding the proof.


Now, to the proof itself. It will be proof by induction. First, note that the number of calls of min_cost(s, d) depends only on the value of d-s, and not on the individual values of s and d.

The base is that, for d-s=1, we get one call. For d-s>1, we make our one call, and from it the following calls:

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)

So, for d-s=k, the number of calls f(k) is:

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))

If, by the induction hypothesis, we have already proved that f(v) = 3v for all v < k, then the f(k) is:
1 + 2 * (31 + 32 + ... + 3k-1), which is trivially 3k, completing our proof.


Lastly, note that, while the presented algorithm is exponential, the underlying problem can be solved in polynomial time, most simply in O((d-s)^2) by memoizing the calls for which we already did all the work.

Gassa
  • 8,546
  • 3
  • 29
  • 49
  • 1
    I think that the idea of counter as you have done is perfect. – SaidbakR Jun 05 '18 at 17:35
  • @Gassa Yes there is a polynomial solution for this as well . My motivation over here was to come up with hypothesis using which i can predict the # of calls for some input given. Thanks for Answer. – Raghav salotra Jun 05 '18 at 17:57
  • @Gassa: very nice intuition, running the code with a counter, then proving the "guess" by induction. Of course, this works for simple formulas. Otherwise, Master Theorem (see CLRS) should have you covered, once you have the recurrence relations. – Mircea Jun 12 '18 at 12:52
3

There are several options to calculate that amount when one is working with recursion. The simplest would be to add another variable to the recursive method which is increased every time it gets recursed and in the last statement where it is returned it does not increment, but just return the last amount, which will recursive 'back' to the upper request.

Example in pseudo code:

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

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

Another way of working is assigning a variable by reference, a pointer or a global variable (depending on the programming language) and incrementing that counter.

Did this help you?

Tim B.
  • 429
  • 4
  • 11
  • I am looking for some mathematical solution if without running a program we can see and tell that same function will get executed this many number of time in activation stack of RAM . – Raghav salotra Jun 03 '18 at 15:12
  • I'm not sure, but that sounds more like a assembler/C++ kind of activity in which a debugger/monitoring is active. Maybe you have to look into applications which run as master for other applications so the master process can monitor the application calls.. but i'm not sure you will get there soon.. – Tim B. Jun 04 '18 at 15:57
  • Hope this helps you into the right direction: http://www.rohitab.com/apimonitor , https://stackoverflow.com/q/311268/3361634 or https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer – Tim B. Jun 04 '18 at 16:02
  • Above code is of recursion and if you draw recursion call stack you can 2 calls for min_cost(1, 3) using these or any metric like these i thought there should be a way to extrapolate the trace and get number of call. – Raghav salotra Jun 05 '18 at 10:10
2

I think a global variable that rests outside the function (a member of your class if in Java, or a global var in C++/ C) and increasing its value by one every time you call it, shall do the trick.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pipe Runner
  • 82
  • 2
  • 6
  • 2
    The problem setter asked for a way to figure this out without having to do the dry run. – Kevin Jun 12 '18 at 14:55