pythonalgorithmrecursiontime-complexity

How to count Basic Math Operations performed in a python recursive function


I need to write a python script that counts the number of times these operations: +, -, *, //, %, >, <, ==, <=, >= are performed in a piece of python code relative to the input of N.

Python Code:

def bar(k):
    score = 0
    for i in range(2, k + 1):
        j = i - 1
        while j > 0:
            if i % j == 0:
                score = score // 2
            else:
                score = score + 10
            j = j - 1
    return score

mod = 10**9 + 7

def foo(n):
    m = n % 3
    if n == 0:
        return 1
    elif m == 0:
        v = foo(n // 3)
        t = 0
        for i in range(1, n+1):
            t = t + bar(4 * i)
        return v + t
    elif m == 1:
        v = foo(n - 1)
        return v + bar(n * n * n)
    else:
        v = foo(n - 2)
        r = 1
        for a in range(2, n):
            r = r * a % mod
            r = r + bar(n)
        return v + r

My Script:

def count_operations(n):
    if n == 0:
        return 1  # Base case: 1 operation for returning 1
    elif n % 3 == 0:
        return count_operations(n // 3) + 4
    elif n % 3 == 1:
        return 6 + count_operations(n - 1) + 4
    else:
        return 9 + 2 * count_operations(n - 2)  

I wrote the script with the understanding of when N=1,calculating m=1 % 3 takes 1 operation, and the value of m is 1. Then the code executes 3 tests of the == operation before finding one to be true, when it encounters elif m == 1:. Up to that point, 4 operations have been performed.

Then it performs a recursive call with foo(n-1) which means that 5 operations have now been performed before the recursive call (the last for the -). The recursive call now has n=0, so 2 more operations occur (n%3 and n==0), before it returns with its result of 1. That makes a total of 7 basic operations that have now been performed to the point of returning from the recursive call to foo.

The code next encounters return v + bar(nnn), which means that 2 more operations (both *) occur before the call to bar followed by 1 more (+) afterwards, to bring the total 10, not counting the number done in bar. Simulating the call to bar(1), 1 more operation (k+1) is performed when the top end of the range of the for loop is calculated, bringing the total to 11. The for loop terminates immediately since the range of its iteration is empty, and the score of 0 is returned to complete the (already counted) + operation and return statement in foo. So, that means a total of 11 basic operations were performed, and we return 11 as the answer for when N=1.

So, my script only works correctly when n=1 anyone can help me fixed my script to get an understanding of how to do the proper counting when N>1?

My inefficient code what produces the correct answer:

class Integer(int):
    n_ops = 0

def new_patch(name):
    def patch(self, *args):
        Integer.n_ops += 1
        value = getattr(int, name)(self, *args)
        if isinstance(value, int) and not (value is True or value is False):
            value = Integer(value)
        return value
    patch.__name__ = name
    return patch

methods = {
    '__le__': '\u2264',
    '__lt__': '<',
    '__ge__': '\u2265',
    '__gt__': '>',
    '__eq__': '==',
    '__add__': '+',
    '__sub__': '-',
    '__mul__': '*',
    '__floordiv__': '//',
    '__mod__': '%',
}

for name in methods:
    setattr(Integer, name, new_patch(name))
    
def bar(k):
    score = 0
    for i in range(2, k + 1):
        j = i - 1
        while j > 0:
            if i % j == 0:
                score = score // 2
            else:
                score = score + 10
            j = j - 1
    return score

mod = 10**9 + 7
Integer.n_ops+=1

def foo(n):
    m = n % 3
    if n == 0:
        return 1
    elif m == 0:
        v = foo(n // 3)
        t = 0
        for i in range(1, n+1):
            t = t + bar(4 * i)
        return v + t
    elif m == 1:
        v = foo(n - 1)
        return v + bar(n * n * n)
    else:
        v = foo(n - 2)
        r = 1
        for a in range(2, n):
            r = r * a % mod
            r = r + bar(n)
        return v + r

    def countOps(n):
       print(f'Number of operations when n={n}: {Integer.n_ops}')

Solution

  • First of all, as you want to count both % and == as operations, the first base case should be return 2 instead of return 1. Also the constant terms you add in the other return statements seem all wrong. For instance, the second return statement should count 6 instead of 4 as its constant term: there are n % 3, n == 0, m == 0, n // 3, n+1, v+t. It seems you have not counted the comparisons.

    Here is an analysis to get the correct counts:

    Analysis

    First look at bar.

    Now look at foo.

    Code

    The above analysis leads to the following code for count_operations:

    def count_operations(n):
        if n == 0:
            return 2
        elif n % 3 == 0:
            return count_operations(n // 3) + (40*n**3 + 57*n*n + 20*n + 18) // 3
        elif n % 3 == 1:
            return count_operations(n - 1) + (5*n**6 - n**3 + 14) // 2
        else:
            return count_operations(n - 2) + (5*n**3 - 11*n*n + 6*n + 4) // 2