I'm attempting to get better with optimizing algorithms and understanding big-o, etc.
I threw together the below function to calculate the n-th Fibonacci number. This works (for a reasonably high input). My question is, how can I improve this function? What are the drawbacks of calculating the Fibonacci sequence this way?
function fibo(n) {
var i;
var resultsArray = [];
for (i = 0; i <= n; i++) {
if (i === 0) {
resultsArray.push(0);
} else if (i === 1) {
resultsArray.push(1);
} else {
resultsArray.push(resultsArray[i - 2] + resultsArray[i - 1]);
}
}
return resultsArray[n];
}
I believe my big-o for time is O(n), but my big-o for space is O(n^2) due to the array I created. Is this correct?
If you don't have an Array then you save on memory and .push calls
function fib(n) {
var a = 0, b = 1, c;
if (n < 3) {
if (n < 0) return fib(-n);
if (n === 0) return 0;
return 1;
}
while (--n)
c = a + b, a = b, b = c;
return c;
}
Performance Fibonacci:
var memo = {};
var countInteration = 0;
var fib = function (n) {
if (memo.hasOwnProperty(n)) {
return memo[n];
}
countInteration++;
console.log("Interation = " + n);
if (n == 1 || n == 2) {
result = 1;
} else {
result = fib(n - 1) + fib(n - 2);
}
memo[n] = result;
return result;
}
//output `countInteration` = parameter `n`
Related
I am trying to get the sum of an array of prime numbers, and I understand there are more elegant ways to do that and have seen the links to those solutions.
My problem is something that's wrong within this specific script, and I'm trying to understand what's causing THIS code to fail.
The issue is that the numbers 9, 15 and many others are being being added into the primes array, even though they all, correctly, fail a test to check if they're prime numbers. I can't wrap my head around what in the script is causing the numbers to push to the array despite failing that test. Again, I'm not looking for a completely different/better approach to summing the primes, but some help in identifying what exactly is wrong in this script would be really appreciated.
function totalPrime(num) {
var nums = [];
var primes = [];
for (var i = 1;
(num - i) > 1; i++) {
nums.push(num - i);
}
nums.forEach(isPrime);
function isPrime(n) {
var a = [];
var test;
if (n === 1) {} else if (n === 2) {
primes.push(n);
} else {
for (var i = 1;
(n - i) > 1; i++) {
a.push(n - i);
}
a.forEach(function(x) {
if ((n % x) === 0) {
test = false;
} else {
test = true;
}
});
if (test) {
primes.push(n);
} else {}
};
}
console.log(primes.reduce(function(a, b) {
return a + b
}));
}
totalPrime(5);
Same script with logging I was using to debug:
function totalPrime(num) {
var nums = [];
var primes = [];
for (var i = 1;
(num - i) > 1; i++) {
nums.push(num - i);
}
nums.forEach(isPrime);
function isPrime(n) {
var a = [];
var test;
if (n === 1) {
console.log(n + ' is NOT a prime number');
} else if (n === 2) {
console.log(n + ' IS a prime number');
primes.push(n);
} else {
for (var i = 1;
(n - i) > 1; i++) {
a.push(n - i);
}
a.forEach(function(x) {
if ((n % x) === 0) {
test = false;
console.log(n + ' % ' + x + ' equals 0');
console.log(x + ' fails check');
} else {
test = true;
console.log(n + ' % ' + x + ' does NOT equal 0');
console.log(x + ' passes check');
}
});
if (test) {
console.log(n + ' IS a prime number.');
primes.push(n);
} else {
console.log(n + ' is NOT a prime number.');
}
};
}
console.log(primes);
console.log(primes.reduce(function(a, b) {
return a + b
}));
}
totalPrime(5);
Your test value in each test override the previous check. Thus, actualy only the last check (divide in 2) become relevant, and all the odd primes fail.
You can correct it by change the default of test to true, and remove the exist line in the code test = true;.
The corrected code:
function isPrime(n) {
var a = [];
var test = true;
if (n === 1) {} else if (n === 2) {
primes.push(n);
} else {
for (var i = 1;
(n - i) > 1; i++) {
a.push(n - i);
}
a.forEach(function(x) {
if ((n % x) === 0) {
test = false;
}
});
if (test) {
primes.push(n);
}
};
}
Trying really hard to figure out how to solve this problem. The problem being finding nth number of Fibonacci with O(n) complexity using javascript.
I found a lot of great articles how to solve this using C++ or Python, but every time I try to implement the same logic I end up in a Maximum call stack size exceeded.
Example code in Python
MAX = 1000
# Create an array for memoization
f = [0] * MAX
# Returns n'th fuibonacci number using table f[]
def fib(n) :
# Base cases
if (n == 0) :
return 0
if (n == 1 or n == 2) :
f[n] = 1
return (f[n])
# If fib(n) is already computed
if (f[n]) :
return f[n]
if( n & 1) :
k = (n + 1) // 2
else :
k = n // 2
# Applyting above formula [Note value n&1 is 1
# if n is odd, else 0.
if((n & 1) ) :
f[n] = (fib(k) * fib(k) + fib(k-1) * fib(k-1))
else :
f[n] = (2*fib(k-1) + fib(k))*fib(k)
return f[n]
// # Driver code
// n = 9
// print(fib(n))
Then trying to port this to Javascript
const MAX = 1000;
let f = Array(MAX).fill(0);
let k;
const fib = (n) => {
if (n == 0) {
return 0;
}
if (n == 1 || n == 2) {
f[n] = 1;
return f[n]
}
if (f[n]) {
return f[n]
}
if (n & 1) {
k = Math.floor(((n + 1) / 2))
} else {
k = Math.floor(n / 2)
}
if ((n & 1)) {
f[n] = (fib(k) * fib(k) + fib(k-1) * fib(k-1))
} else {
f[n] = (2*fib(k-1) + fib(k))*fib(k)
}
return f[n]
}
console.log(fib(9))
That obviously doesn't work. In Javascript this ends up in an infinite loops. So how would you solve this using Javascript?
Thanks in advance
you can iterate from bottom to top (like tail recursion):
var fib_tail = function(n){
if(n == 0)
return 0;
if(n == 1 || n == 2)
return 1;
var prev_1 = 1, prev_2 = 1, current;
// O(n)
for(var i = 3; i <= n; i++)
{
current = prev_1 + prev_2;
prev_1 = prev_2;
prev_2 = current;
}
return current;
}
console.log(fib_tail(1000))
The problem is related to scope of the k variable. It must be inside of the function:
const fib = (n) => {
let k;
You can find far more good implementations here list
DEMO
fibonacci number in O(n) time and O(1) space complexity:
function fib(n) {
let prev = 0, next =1;
if(n < 0)
throw 'not a valid value';
if(n === prev || n === next)
return n;
while(n >= 2) {
[prev, next] = [next, prev+next];
n--;
}
return next;
}
Just use two variables and a loop that counts down the number provided.
function fib(n){
let [a, b] = [0, 1];
while (--n > 0) {
[a, b] = [b, a+b];
}
return b;
}
console.log(fib(10));
Here's a simpler way to go about it, using either iterative or recursive methods:
function FibSmartRecursive(n, a = 0, b = 1) {
return n > 1 ? FibSmartRecursive(n-1, b, a+b) : a;
}
function FibIterative(n) {
if (n < 2)
return n;
var a = 0, b = 1, c = 1;
while (--n > 1) {
a = b;
b = c;
c = a + b;
}
return c;
}
function FibMemoization(n, seenIt = {}) {//could use [] as well here
if (n < 2)
return n;
if (seenIt[n])
return seenIt[n];
return seenIt[n] = FibMemoization(n-1, seenIt) + FibMemoization(n-2, seenIt);
}
console.log(FibMemoization(25)); //75025
console.log(FibIterative(25)); //75025
console.log(FibSmartRecursive(25)); //75025
You can solve this problem without recursion using loops, runtime O(n):
function nthFibo(n) {
// Return the n-th number in the Fibonacci Sequence
const fibSeq = [0, 1]
if (n < 3) return seq[n - 1]
let i = 1
while (i < n - 1) {
seq.push(seq[i - 1] + seq[i])
i += 1
}
return seq.slice(-1)[0]
}
// Using Recursion
const fib = (n) => {
if (n <= 2) return 1;
return fib(n - 1) + fib(n - 2);
}
console.log(fib(4)) // 3
console.log(fib(10)) // 55
console.log(fib(28)) // 317811
console.log(fib(35)) // 9227465
I have this sum:
Obviously, I have to get sum of that depending on what N is. I need to do it in three different ways.
First is for loop:
function lab(n) {
var S = 0;
let VS
if (n == 0) {
VS = 0;
return 0;
}
if (n == 1) {
VS = 4;
return Math.pow(3 / 5, 1);
} else {
for (let i = 0; i < n; i++) { //
S += 1 / n * Math.pow(3 / 5, n);
t = 4 * n;
}
return S;
}
}
Second one is recursion:
function lab(n) {
let vs = 0;
if (n <= 1)
return 0;
else {
vs += 4 * n // vs is how many actions it takes to make this calculation. I’m sure in for loop this is right, but I’m not sure about the recursion approach
return lab(n - 1) + 1 / n * Math.pow(3 / 5, n)
}
}
The third way is use recursion with the condition that in order to get S(n) I need to use S(n-1).
I am stuck on this.
Also I get different sums with the same Ns from first and second function.
I am not sure what you are asking for.
If you are asking for a recursive function then take a look at the following:
function summation(n, sum = 0) {
if (n <= 0) {
return sum;
}
sum += (1/n) * Math.pow(3/5, n);
return summation(n - 1, sum);
}
console.log(summation(1));
console.log(summation(2));
console.log(summation(3));
console.log(summation(4));
console.log(summation(5));
Another recursive method without passing sum as parameter:
function summation(n) {
if (n <= 0) {
return 0;
}
return ((1/n) * Math.pow(3/5, n)) + summation(n - 1);
}
console.log(summation(1));
console.log(summation(2));
console.log(summation(3));
console.log(summation(4));
console.log(summation(5));
Also, for the loop method, the following will suffice
function summation(n) {
var sum = 0;
while (n > 0) {
sum += (1/n) * Math.pow(3/5, n);
n -= 1;
}
return sum;
}
console.log(summation(1));
console.log(summation(2));
console.log(summation(3));
console.log(summation(4));
console.log(summation(5));
I'm working on a game where I only need to check if there's a distance of 0 or 1 between two words and return true if that's the case. I found a general purpose levenshtein distance algorithm:
function levenshtein(s, t) {
if (s === t) { return 0; }
var n = s.length, m = t.length;
if (n === 0 || m === 0) { return n + m; }
var x = 0, y, a, b, c, d, g, h, k;
var p = new Array(n);
for (y = 0; y < n;) { p[y] = ++y; }
for (;
(x + 3) < m; x += 4) {
var e1 = t.charCodeAt(x);
var e2 = t.charCodeAt(x + 1);
var e3 = t.charCodeAt(x + 2);
var e4 = t.charCodeAt(x + 3);
c = x; b = x + 1; d = x + 2; g = x + 3; h = x + 4;
for (y = 0; y < n; y++) {
k = s.charCodeAt(y);
a = p[y];
if (a < c || b < c) { c = (a > b ? b + 1 : a + 1); }
else { if (e1 !== k) { c++; } }
if (c < b || d < b) { b = (c > d ? d + 1 : c + 1); }
else { if (e2 !== k) { b++; } }
if (b < d || g < d) { d = (b > g ? g + 1 : b + 1); }
else { if (e3 !== k) { d++; } }
if (d < g || h < g) { g = (d > h ? h + 1 : d + 1); }
else { if (e4 !== k) { g++; } }
p[y] = h = g; g = d; d = b; b = c; c = a;
}
}
for (; x < m;) {
var e = t.charCodeAt(x);
c = x;
d = ++x;
for (y = 0; y < n; y++) {
a = p[y];
if (a < c || d < c) { d = (a > d ? d + 1 : a + 1); }
else {
if (e !== s.charCodeAt(y)) { d = c + 1; }
else { d = c; }
}
p[y] = d;
c = a;
}
h = d;
}
return h;
}
Which works, but this spot is going to be a hotspot and be run potentially hundreds of thousands of times a second and I want to optimize it because I don't need a general purpose algorithm, just one that checks if there's a distance of 0 or 1.
I tried writing it and came up with this:
function closeGuess(guess, word) {
if (Math.abs(word.length - guess.length) > 1) { return false; }
var errors = 0, guessIndex = 0, wordIndex = 0;
while (guessIndex < guess.length || wordIndex < word.length) {
if (errors > 1) { return false; }
if (guess[guessIndex] !== word[wordIndex]) {
if (guess.length < word.length) { wordIndex++; }
else { guessIndex++; }
errors++;
} else {
wordIndex++;
guessIndex++;
}
}
return true;
}
But after profiling it I found that my code was twice as slow, which surprised me because I think the general purpose algorithm is O(n*m) and I think mine is O(n).
I've been testing the performance difference on this fiddle: https://jsfiddle.net/aubtze2L/3/
Are there any better algorithms I can use or any way I can optimize my code to be faster?
I don't see a more elegant way which is at the same time faster than the good old for-loop:
function lev01(a, b) {
let la = a.length;
let lb = b.length;
let d = 0;
switch (la - lb) {
case 0: // mutation
for (let i = 0; i < la; ++i) {
if (a.charAt(i) != b.charAt(i) && ++d > 1) {
return false;
}
}
return true;
case -1: // insertion
for (let i = 0; i < la + d; ++i) {
if (a.charAt(i - d) != b.charAt(i) && ++d > 1) {
return false;
}
}
return true;
case +1: // deletion
for (let i = 0; i < lb + d; ++i) {
if (a.charAt(i) != b.charAt(i - d) && ++d > 1) {
return false;
}
}
return true;
}
return false;
}
console.log(lev01("abc", "abc"));
console.log(lev01("abc", "abd"));
console.log(lev01("abc", "ab"));
console.log(lev01("abc", "abcd"));
console.log(lev01("abc", "cba"));
Performance comparison (Chrome):
80.33ms - lev01 (this answer)
234.84ms - lev
708.12ms - close
Consider the following cases:
If the difference in lengths of the terms is greater than 1, then
the Levenshtein distance between them will be greater than 1.
If the difference in lengths is exactly 1, then the shortest string must be equal to the longest string, with a single deletion (or insertion).
If the strings are the same length then you should
consider a modified version of Hamming distance which returns false
if two, different characters are found:
Here is a sample implementation:
var areSimilar;
areSimilar = function(guess, word) {
var charIndex, foundDiff, guessLength, lengthDiff, substring, wordLength, shortest, longest, shortestLength, offset;
guessLength = guess.length;
wordLength = word.length;
lengthDiff = guessLength - wordLength;
if (lengthDiff < -1 || lengthDiff > 1) {
return false;
}
if (lengthDiff !== 0) {
if (guessLength < wordLength) {
shortest = guess;
longest = word;
shortestLength = guessLength;
} else {
shortest = word;
longest = guess;
shortestLength = wordLength;
}
offset = 0;
for (charIndex = 0; charIndex < shortestLength; charIndex += 1) {
if (shortest[charIndex] !== longest[offset + charIndex]) {
if (offset > 0) {
return false; // second error
}
offset = 1;
if (shortest[charIndex] !== longest[offset + charIndex]) {
return false; // second error
}
}
}
return true; // only one error
}
foundDiff = false;
for (charIndex = 0; charIndex < guessLength; charIndex += 1) {
if (guess[charIndex] !== word[charIndex]) {
if (foundDiff) {
return false;
}
foundDiff = true;
}
}
return true;
};
I've updated your fiddle to include this method. Here are the results on my machine:
close: 154.61
lev: 176.72500000000002
sim: 32.48000000000013
Fiddle: https://jsfiddle.net/dylon/aubtze2L/11/
If you know that you are looking for distance 0 and 1, then the general purpose DP algorithm does not make sense (and by the way the algorithm you showed looks convoluted, take a look at a better explanation here).
To check that the distance is 0, all you need is to check whether 2 strings are the same. Now if the distance is one, it means that either insertion, deletion or substitution should have happened. So generate all possible deletion from the original string and check whether it is equal to second string. So you will get something like this:
for (var i = 0; i < s_1.length; i++) {
if s_2 == s_1.slice(0, i) + s_1.slice(i + 1) {
return true
}
}
For insertions and substitution you will need to know the alphabet of all characters. You can define it as a big string var alphabet = "abcde....". Now you do a similar thing, but when you introduce substitution or insertion, you also iterate over all elements in your alphabet. I am not planning to write the whole code here.
A couple of additional things. You can make a lot of micro-optimizations here. For example if the length of two strings are different by more than 1, they clearly can't have a distance 1. Another one relates to the frequencies of underlying characters in the string.
Here is the challenge:
Using the JavaScript language, have the function ArithGeoII(arr) take the array of numbers stored in arr and return the string "Arithmetic" if the sequence follows an arithmetic pattern or return "Geometric" if it follows a geometric pattern. If the sequence doesn't follow either pattern return "neither". An arithmetic sequence is one where the difference between each of the numbers is consistent, where as in a geometric sequence, each term after the first is multiplied by some constant or common ratio. Arithmetic example: [2, 4, 6, 8] and Geometric example: [2, 6, 18, 54].
Here is my solution:
function arithGeoII(arr) {
var x = "none of the above";
for (i = 0; i < arr.length; i++) {
if ((arr[i + 1]) - (arr[i])) === ((arr[i + 2]) - (arr[i + 1])) {
x = "arithmetic";
} else if ((arr[i + 1]) / (arr[i])) === ((arr[i + 2]) / (arr[i + 1])) {
x = "geometric";
}
}
return x;
}
I've been able to find several solutions to this that make sense but I'm curious why mine isn't working? I'm obviously a beginner (so the answer might seem obvious to most), but I really appreciate any help!
The problem is that, once a condition fails somewhere, you must exit the loop immediately.
Otherwise, you are only checking the last numbers.
For example, your code will say that [0,0,0,0,0,0,1,2] is an arithmetic sequence because 1-0 === 2-1. But it isn't, because the condition arr[i+1]-arr[i] === arr[i+2]-arr[i+1] does not hold everywhere.
Some working examples:
function arith(arr) {
for (var i = 2; i < arr.length; ++i)
if (arr[i-1] - arr[i-2] !== arr[i] - arr[i-1])
return false;
return true;
}
function geom(arr) {
for (var i = 2; i < arr.length; ++i)
if (arr[i-1] / arr[i-2] !== arr[i] / arr[i-1])
return false;
return true;
}
function arithGeoII(arr) {
var a = arith(arr), g = geom(arr);
if(a && g) return "both";
if(a) return "aritmetic";
if(g) return "geometric";
return "none";
}
function arithGeoII(arr) {
var arit = true, geom = true;
for (var i = 2; i < arr.length && (arit || geom); ++i) {
if (arit && arr[i-1] - arr[i-2] !== arr[i] - arr[i-1])
arit = false;
if (geom && arr[i-1] / arr[i-2] !== arr[i] / arr[i-1])
geom = false;
}
if(arit && geom) return "both";
if(arit) return "aritmetic";
if(geom) return "geometric";
return "none";
}