Recursive Factorial Visual Whiteboard of Rec Fac.
Recursive Fibonacci Visual Whiteboard of Rec Fib.
Big O helps gives us a precise vocabulary to talk about how our code performs.
An Example: Comparing two functions that calculate the sum of all numbers from 1 up to n.
Number of operations will grow with n. Would be O(n) or Linear Time.
Has three simple operations: 1 Multiplication 1 Addition 1 Division. (Regardless of n) Would be O(1) or Constant Time.
First we need to consider what makes one implementation better than the other?
How can we measure speed?
function countUpAndDown(n) {
console.log('going up!');
for (let i = 0; i < n; i++) {
console.log(i);
}
console.log('at the top, going down!');
for (let j = n - 1; j >=0; j--) {
console.log(j);
}
console.log('Back down, bye!);
}
Both loops are O(n) but since we just want the big picture, this entire function would be O(n);
function printAllPairs(n) {
for (var i = 0; i < n; i++) {
for (var j = 0; j < n; j++) {
console.log(i, j);
}
}
}
Nested loops are never a good thing when trying to write fast code. O(n^2) or Quadratic Time.
Constants don’t matter in big O & Smaller Terms don’t matter
Big O Shorthands
Additional Examples
O(n) Linear Time
O(1) Constant Time.
Curating Complexity: A Guide to Big-O Notation
Why is looking at runtime not a reliable method of calculating time complexity?
The real question we need to answering is: How does our performance scale?
.
Big O Notation
Smaller
Big O is better (lower time complexity)Simplifying Math Terms
We can use the following rules to simplify the our Big O functions:
Simplify Products
: If the function is a product of many terms, we drop the terms that don’t depend on n.Simplify Sums
: If the function is a sum of many terms, we drop the non-dominant terms.n
: size of the inputT(f)
: unsimplified math functionO(f)
: simplified math function.
Simplifying a Product
| Unsimplified | Big-O Simplified | | ———— | —————- | | T(5 _ n^2) | O(n^2) Quadratic | | T(100000 _ n) | O(n) Linear | | T( n / 12) | O (n) Linear | | T( 42 _ n _ log(n)) | O(nlog(n)) Log Linear | | T(12) | O(1) Constant |
Simplifying a Sum
| Unsimplified | Big-O Simplified | | ———— | —————- | | T( n3 + n2 + n ) | O(n^3) | | T( log(n) + 2n )| O(2^n) Exponential | | T( n + log(n) ) | O(n) Linear | | T( n! + 10n ) | O(n!) Polynomial |
Putting it all together
Unsimplified | Big-O Simplified |
---|---|
T( 5n2 + 99n ) | O(n^2) Quadratic |
T( 2n + nlog(n) ) | O(nlog(n)) Log Linear |
T( 2n + 5n1000) | O(2^n) Exponential |
Common Complexity Classes
There are 7 major classes in Time Complexity
Big O | Complexity Class Name |
---|---|
O(1) | Constant |
O(log(n)) | Logarithmic |
O(n) | Linear |
O(nlog(n)) | Loglinear, Linearithmetic, Quasilinear |
O(nc) - O(n2), O(n3), etc. | Polynomial |
O(cn) - O(2n), O(3n), etc. | Exponential |
O(n!) | Factorial |
O(1) Constant
O(log(n)) Logarithmic
O(n) Linear
O(nlog(n)) Log Linear Time
// O(n * log(n))
function loglinear(n) {
if (n <= 1) return;
for (let i = 1; i <= n; i++) {
console.log(i);
}
loglinear(n / 2);
loglinear(n / 2);
}
O(nc) Polynomial
// O(n^2)
function quadratic(n) {
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= n; j++) {}
}
}
// O(n^3)
function cubic(n) {
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= n; j++) {
for (let k = 1; k <= n; k++) {}
}
}
}
Example of Quadratic and Cubic runtime.
O(c^n) Exponential
// O(2^n)
function exponential2n(n) {
if (n === 1) return;
exponential_2n(n - 1);
exponential_2n(n - 1);
}
// O(3^n)
function exponential3n(n) {
if (n === 0) return;
exponential_3n(n - 1);
exponential_3n(n - 1);
exponential_3n(n - 1);
}
O(n!) Factorial
Memoizing Factorial
let memo = {};
function factorial(n) {
// if this function has calculated factorial(n) previously,
// fetch the stored result in memo
if (n in memo) return memo[n];
if (n === 1) return 1;
// otherwise, it havs not calculated factorial(n) previously,
// so calculate it now, but store the result in case it is
// needed again in the future
memo[n] = n * factorial(n - 1);
return memo[n];
}
factorial(6); // => 720, requires 6 calls
factorial(6); // => 720, requires 1 call
factorial(5); // => 120, requires 1 call
factorial(7); // => 5040, requires 2 calls
memo; // => { '2': 2, '3': 6, '4': 24, '5': 120, '6': 720, '7': 5040 }
Memoizing Fibonacci
The Memoization Formula
Rules
Things to remember
function fastFib(n, memo = {}) {
if (n in memo) return memo[n];
if (n === 1 || n === 2) return 1;
memo[n] = fastFib(n - 1, memo) + fastFib(n - 2, memo);
return memo[n];
}
fastFib(6); // => 8
fastFib(50); // => 12586269025
function fib(n) {
let mostRecentCalcs = [0, 1];
if (n === 0) return mostRecentCalcs[0];
for (let i = 2; i <= n; i++) {
const [secondLast, last] = mostRecentCalcs;
mostRecentCalcs = [last, secondLast + last];
}
return mostRecentCalcs[1];
}
function search(array, term) {
for (let i = 0; i < array.length; i++) {
if (array[i] === term) {
return i;
}
}
return -1;
}
function binarySearch(arr, x, start, end) {
if (start > end) return false;
let mid = Math.floor((start + end) / 2);
if (arr[mid] === x) return true;
if (arr[mid] > x) {
return binarySearch(arr, x, start, mid - 1);
} else {
return binarySearch(arr, x, mid + 1, end);
}
}
function merge(leftArray, rightArray) {
const sorted = [];
while (letArray.length > 0 && rightArray.length > 0) {
const leftItem = leftArray[0];
const rightItem = rightArray[0];
if (leftItem > rightItem) {
sorted.push(rightItem);
rightArray.shift();
} else {
sorted.push(leftItem);
leftArray.shift();
}
}
while (leftArray.length !== 0) {
const value = leftArray.shift();
sorted.push(value);
}
while (rightArray.length !== 0) {
const value = rightArray.shift();
sorted.push(value);
}
return sorted;
}
function mergeSort(array) {
const length = array.length;
if (length === 1) {
return array;
}
const middleIndex = Math.ceil(length / 2);
const leftArray = array.slice(0, middleIndex);
const rightArray = array.slice(middleIndex, length);
leftArray = mergeSort(leftArray);
rightArray = mergeSort(rightArray);
return merge(leftArray, rightArray);
}
function bubbleSort(items) {
var length = items.length;
for (var i = 0; i < length; i++) {
for (var j = 0; j < length - i - 1; j++) {
if (items[j] > items[j + 1]) {
var tmp = items[j];
items[j] = items[j + 1];
items[j + 1] = tmp;
}
}
}
}