简体   繁体   中英

Time complexity of a non-trivial recursive function

I wrote a recursive function that takes an array of positive integers (> 0) ( denominations ) and an integer value ( amount ) and returns the number of ways it is possible to obtain the value using only the integers in the array. So for example, the array of integers can be thought of as change and the integer value as the amount of money you have to pay. And the function returns the number of ways you can pay the cashier. For example:

amount = 4 and denominations = [1,2,3,4]

This should return 5

I have tested my function and it works fine. When calling the function, the arguments acc and index are passed as 0. Here is the function:

public int count_number_of_ways(int amount, int acc, int index,  int[] denominations) {

        if (acc > amount) return 0;

        if (amount == acc) return 1;


        int len = denominations.length;
        int count = 0;

        while (acc < amount) {


            for (int i = index + 1; i < len; i++) {


                count += count_number_of_ways(amount, acc + denominations[i], i, denominations);


            }

            acc += denominations[index];

            if (acc > amount) return count + 0;

            if (acc == amount) return count + 1;

        }

        return count + 0;


}

I tried to compute the time complexity of this function but hit a brick wall. The recursive call is inside a for loop which is inside a while loop and that made things confusing for me. Could anyone help me determine the time complexity for this function ?

Generally speaking in worst case you will spend time proportional to ( amount / denominations[0] ) * ( amount / denominations[1] ) * ... * ( amount / denominations[n] ), where n - number of denominations.

This is because you look over all sets of counts for each of your denominations. And maximum count for particular denomination number i is amount / denominations[i] .

In real case you stop the process earlier but I don't think it changes O((amount/geometric_average_denomination)^n).

It's possible to avoid exponential time though. For that you can use dynamic programming approach. Here is my version of your algorithm requiring O(amount * n) time:

public static int count_number_of_ways(int amount, int index, Integer[][] cache, int[] denominations) {
    if (cache == null)
        cache = new Integer[denominations.length][amount + 1];
    if (amount == 0) {
        int ret = 1;
        cache[index][amount] = ret;
        return ret;
    }
    if (cache[index][amount] != null) {
        return cache[index][amount];
    }
    int ret = 0;
    if (index + 1 < denominations.length)
        ret += count_number_of_ways(amount, index + 1, cache, denominations);
    if (amount >= denominations[index])
        ret += count_number_of_ways(amount - denominations[index], index, cache, denominations);
    cache[index][amount] = ret;
    return ret;
}

The idea is to fill matrix of size [n][amount] where each element (i,j) is number of solutions for amount=j using subset of denominations with indexes>=i. You fill each element of this cache matrix only once and reuse it later if it's defined already.

Use it like:

int ret = count_number_of_ways(amount, 0, null, denominations)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM