Computational Cost#

Analyzing running time#

TL;DR

In computer science, data structures and algorithms are fundamental concepts used to efficiently store and manipulate data. Data structures are ways of organizing and storing data in a computer’s memory, while algorithms are step-by-step procedures for performing specific tasks. The computational cost of data structures and algorithms refers to the amount of time and resources required to execute a particular operation or task.

When designing or choosing data structures and algorithms, it’s important to consider their computational cost. Some operations may require a lot of time and resources, which can impact the overall performance of a system. The computational cost can be analyzed using various measures such as time complexity, space complexity, and big-O notation.

Time complexity measures the number of operations required to perform a task as a function of the input size. Space complexity measures the amount of memory required to store data structures and algorithms. Big-O notation is used to express the upper bound of the computational cost in terms of the input size.

By understanding the computational cost of data structures and algorithms, developers can make informed decisions about which ones to use in a particular context. For example, if the system needs to perform a lot of searches, a data structure with fast search time complexity like a hash table may be a better choice than a binary search tree. Similarly, if memory usage is a concern, a data structure with low space complexity like an array may be a better choice than a linked list.

Empirical Model
  • Run algorithm

  • Measure actual time

Mathematical Model
  • Analyze algorithm

  • Develop Model

Theoretical Models#

Mathematical Model
  • High-level analysis

    • no need to implement

  • Independent of hardware / software

  • Based on counts of basic instructions

    • additions, multiplications, comparisons,etc

    • exact definition not important but must be relevant to the problem

Basic assumptions
  • In order to use a formal framework, we will make certain assumptions

    • count basic instructions: additions, multiplications, comparisons, assignments

    • each instruction takes one time unit

    • instructions are executed sequentially

    • infinite memory

  • Focus on analyzing running time

Examples#

What to count?

  • only relevant instructions?

  • all instructions?

1for (int i = 0; i < n; i++) {
2    std::cout << (2 * i);
3}

Time Complexity & Why…

The time complexity of this code is \(O(n)\), because the operation within the loop is done n times.

Application
#include <iostream>
using namespace std;

int main()
{
  int i, n = 8;
  for (i = 1; i <= n; i++) {
    cout << "Hello World !!!\n";
  }
  return 0;
}

Output

Hello World !!!
Hello World !!!
Hello World !!!
Hello World !!!
Hello World !!!
Hello World !!!
Hello World !!!
Hello World !!!
Time Complexity

In the above code “Hello World !!!” is printed only n times on the screen, as the value of n can change. So, the time complexity is linear: \(O(n)\) i.e. every time, a linear amount of time is required to execute code.

Auxiliary Space

\(O(1)\)

1for (int i = 0; i < n; i++) {
2    for (int j = 0; j < n; j++) {
3        std::cout << (i * j);
4    }
5}
1for (int i = 0; i < n; i++) {
2    for (int j = 0; j < n * n; j++) {
3        std::cout << (i * j);
4    }
5}
1for (int i = 0; i < n; i++) {
2    for (int j = 0; j < i; j++) {
3        sum = sum * j;
4    }
5}
1for (int i = 0; i < n; i++) {
2    for (int j = 0; j < n; j++) {
3        for (int k = 0; k < n; k++) {
4            //count 1 instruction
5        }
6    }
7}
1for (int i = 0; i < n; i++) {
2    for (int j = 0; j < i * i; j++) {
3        for (int k = 0; k < j; k++) {
4            // count 1 instruction
5        }
6    }
7}

Some rules…#

Single loops
  • essentially, \(total\ iterations\ \times total\ instructions\ performed\ per\ iteration\)

Nested loops
  • count instructions inside out

  • careful with the range of the loop

  • when possible, multiplications can be used for counts from each loop

Consecutive statements
  • just add the counts

Conditionals
  • consider the branch with the highest count

Computational cost#

Number of basic instructions required by the algorithm to process an input of a certain size \(n\)

\[\begin{split}\begin{align} T(n) \\ \end{align}\end{split}\]
  • basic instructions are always relevant to the problem

    • ex: find max in an array

      • # of comparisons

    • ex: sum elements in an array

      • # of additions

Comparing computational cost#

\(Cost\)

\(find (x,y,z)s.t.x+y+z = k\)

\(find (x,y)s.t.x+y = k\)

\(find\ x = k\)

Size of input

\(n^3\)

\(n^2\)

\(n\)

\(n = 1\)

1

1

1

\(n = 10\)

1000

100

10

\(n = 100\)

1000000

10000

100

\(n = 1000\)

1000000000

1000000

1000

\(n = 10000\)

1000000000000

100000000

10000

\(n = 100000\)

1000000000000000

10000000000

100000

\(n = 1000000\)

1000000000000000000

1000000000000

1000000

\(n = 10000000\)

1000000000000000000000

100000000000000

10000000

Growth Rate#

Table 1 \(Growth\ of\ T(n)\ as\ n\to\infty\)#

\(Constant\)

\(Log-Logarithmic\)

\(Logarithmic\)

\(Linear\)

\(Linearithmic\)

\(Quadratic\)
\((Polynomial)\)

\(Cubic\)
\((Polynomial)\)

\(Exponential\)

\(n\)

\(log\ log\ n\)

\(log\ n\)

\(n\)

\(n\ log\ n\)

\(n^2\)

\(n^3\)

\(2^n\)

\(16\)

\(2\)

\(4\)

\(2^4\)

\(4 \times 2^4 = 2^6\)

\(2^8\)

\(2^{12}\)

\(2^{16}\)

\(256\)

\(3\)

\(8\)

\(2^8\)

\(8 \times 2^8 = 2^{11}\)

\(2^{16}\)

\(2^{24}\)

\(2^{256}\)

\(1024\)

\(\approx 3.3\)

\(10\)

\(2^{10}\)

\(10 \times 2^{10} = 2^{13}\)

\(2^{20}\)

\(2^{32}\)

\(2^{1024}\)

\(64K\)

\(4\)

\(16\)

\(2^{16}\)

\(16 \times 2^{16} = 2^{20}\)

\(2^{32}\)

\(2^{48}\)

\(2^{64K}\)

\(1M\)

\(\approx 4.3\)

\(20\)

\(2^{20}\)

\(20 \times 2^{20} = 2^{24}\)

\(2^{40}\)

\(2^{60}\)

\(2^{1M}\)

\(1G\)

\(\approx 4.9\)

\(30\)

\(2^{30}\)

\(30 \times 2^{30} = 2^{35}\)

\(2^{60}\)

\(2^{90}\)

\(2^{1G}\)