Friday, August 7, 2020

recursion x^y ( X power Y) where X and y are input.

 

/*A recursive algorithm is an algorithm that calls itself.

 * A recursive algorithm has Base case:

 * output computed directly on small inputs

 

Recursive algorithms can be used to solve some problems such as Towers of Hanoi (TOH),

Inorder/Preorder/Postorder Tree Traversals, DFS of Graph, easily.

Recursion techniques can be also divided into following types:

1. Linear Recursion

2. Binary Recursion

3. Tail Recursion

4. Mutual Recursion

5. Nested Recursion

*

* Given Problem Statement.

*Write a program to calculate using recursion x^y ( X power y) . X and y are input.

 

Calculate time complexity  of written code.

 

Write a better solution in terms of time complexity.

 

Calculate time complexity again for a better solution

 

*/

 

////////*  Solution *//////

//As the output depends on every element of the input. O(n) or O(n*log(n)) are the best results of time complexities in this case.

//For small number of inputs the O(n log n) < O(n) ? Let n = 10 then 10*log(10) =10*1=10 which is obviously same value.

//Let n = 5 then 5*log5 = 3.49 which is obviously smaller than 5,which is better than O(5).

//Aiming for O(1) which is constant time but for the recursion problems it is difficult to achieve.

 

/*

 *

 * one big concern is the recursion depth (how many times the algorithm calls itself) .

 * If the depth is small, recursive algorithms are often a good solution,and  O(n*log(n)) Time complexity  holds good.

 * If the recursion depth is large, then the better time complexity tends to O(n),when the input is large number.

 * If the recursion depth is huge, then running out of stack memory becomes a real concern,

 * hence Iterative solutions (using loops) can be preferred to recursive algorithms.

 *

 *

 */

////////*  Solution *//////

 

 

 

#include <chrono>

#include <sstream> // for std::stringstream

#include <iostream>

 

using namespace std;

using namespace std::chrono;

 

typedef high_resolution_clock Clock;

typedef Clock::time_point ClockTime;

 

//Iterative Solution Non-Recursive [Time-Complexity O(log(n))]

long pow(int x, int n)

{

  long pow = 1;

  while ( n )

  {

         if ( n & 1 )

         {

           pow = pow * x;

           --n;

         }

         x = x*x;

         n = n/2;

  }

  return pow;

}

 

// Iterative solution to calculate pow(x, n) using binary operators [Time-Complexity O(log(n))]

 

int powB(int x, unsigned n)

{

    // initialize result by 1

    int pow = 1;

 

    // do till n is not zero

    while (n)

    {

        // if n is odd, multiply result by x

        if (n & 1)

            pow *= x;

 

        // divide n by 2

        n = n >> 1;

 

        // multiply x by itself

        x = x * x;

    }

 

    // return result

    return pow;

}

 

 

//Tail Recursion [Time-Complexity O(n)]

 

float power(int x, unsigned n)

{

 

    if (x==0)

    {

        return 0;

 

    }

    else if(n==0)

    {

        return 1;

 

    }

    else if (n>0)

    {

        return( x* power(x,n-1));

    }

    else

    {

        return ((1/x)*power(x,n+1));

    }

}

void printExecutionTime(ClockTime start_time, ClockTime end_time);

 

int main(int argc , char*argv[])

{

 

////////////////////////////////////////////////////////

     //X and y are inputs.//reading x,y input from Commandline.

    cout << "Program name " <<argv[0];

    std::stringstream convertx{ argv[1]};

    std::stringstream convertn{ argv[2]};

 

    int x;

    unsigned int n;

        if (!(convertx >> x) && !(convertn >> n))  {// do the conversion

            x = 0; // if conversion fails, set x,n to a default value

            n = 0;

        }

////////////////////////////////////////////////////////

 

    ClockTime start_time = Clock::now();

 

   // power(x,n);

   // pow(x,n);

    powB(10,10);

////////////////////////////////////////////////////////

    ClockTime end_time = Clock::now();

 

    printExecutionTime(start_time, end_time);

}

 

void printExecutionTime(ClockTime start_time, ClockTime end_time)

{

    auto execution_time_ns = duration_cast<nanoseconds>(end_time - start_time).count();

    auto execution_time_ms = duration_cast<microseconds>(end_time - start_time).count();

    auto execution_time_sec = duration_cast<seconds>(end_time - start_time).count();

    auto execution_time_min = duration_cast<minutes>(end_time - start_time).count();

    auto execution_time_hour = duration_cast<hours>(end_time - start_time).count();

 

    cout << "\nExecution Time: ";

    if(execution_time_hour > 0)

    cout << "" << execution_time_hour << " Hours, ";

    if(execution_time_min > 0)

    cout << "" << execution_time_min % 60 << " Minutes, ";

    if(execution_time_sec > 0)

    cout << "" << execution_time_sec % 60 << " Seconds, ";

    if(execution_time_ms > 0)

    cout << "" << execution_time_ms % long(1E+3) << " MicroSeconds, ";

    if(execution_time_ns > 0)

    cout << "" << execution_time_ns % long(1E+6) << " NanoSeconds, ";

}

 

 

//Understanding

The iterative solution  performs much better than the recursive one, the recursive solution  need more memory for function call stacks so slow.