Calculating complexity of js alghortim - javascript

I'm trying to caluclate complexity of below method in Big O notation
function algorithm(n,m){
let result = [];
for (let i = 0; i < n.length; i++) {
const total = m.filter((x) => x === n[i]).length;
if (PrimalityTest(total)) {
result.push(n[i]);
}
}
return result;
};
function PrimalityTest(c){
if (c <= 1) {
return false;
} else if (c === 2) {
return true;
} else {
for (let i = 2; i * i <= c; i++) {
if (c % i === 0) {
return false;
}
}
return true;
}
}
So, firstly there is loop which have O(n) and then there is nested loop and primality test function so that means complexity of all is O(n * m * sqrt(c))?
Can you please confirm If my understanding is correct?

The loop for (let i = 0; i < n.length; i++) is executed n times. The function m.filter((x) => x === n[i]).length checks every element in m, so executes m-times. So we have an execution time of O(n*m).
Considering
if (PrimalityTest(total)) {
result.push(n[i]);
}
is executed n times because it is in the same loop as above. So at worst it is O(n*sqrt(c))
To sum it up: It is O(n*m)+O(n*sqrt(c)). Because O(n*m) surpasses O(n*sqrt(c)) we get as result: O(n*m).
Your solution would mean that the filter function integrates the PrimalityTest method.

Related

How to refactor IF statements into ternary operators in javascript?

Hello I am trying to refactor my function but it doesn't seem to be working properly. It works fine normally which is the commented code. Am I writing this wrong or is there a fundamental flaw in my code?
function isPrime(num) {
// if (num <= 1){
// return false
// } else {
// for(var i = 2; i < num; i++){
// if(num % i === 0) return false;
// }
// }
num <= 1 ? false : {
for(let i = 2; i < num; i++){
num % 1 === 0 ? false
}}
return true;
}
Thanks
As far as I can tell, no language having the ternary operator allows running a block of statements, which is what you are doing. The ternary operator is a shorthand version of:
let result;
if (<condition>)
result = resultTrue;
else
result = resultFalse;
Unless JavaScript stabs be in the back on this one, for is a statement that doesn't return anything, and therefore cannot be used the way you want.
The best you could do, I guess, would be:
function isPrime(num) {
if (num <= 1) {
return false;
}
for(var i = 2; i < num; i++) {
if(num % i === 0) return false;
}
}
The else part omitted since hitting the line after the if necessarily means that the condition for it was not met.
Technically what you have can be brought to life with an IIFE:
function isPrime(num) {
return num <= 1 ? false :
(n => {
for (let i = 2; i < n; i++) {
if (n % i === 0) return false;
}
return true;
})(num)
}
for (let i = 0; i < 10; i++) {
console.log(i, isPrime(i));
}
Now it works and there is a ternary operator inside.
Even the attempt without return can be done, just you need isPrime made into an arrow function:
let isPrime = num => num <= 1 ? false :
(n => {
for (let i = 2; i < n; i++) {
if (n % i === 0) return false;
}
return true;
})(num);
for (let i = 0; i < 10; i++) {
console.log(i, isPrime(i));
}
Tadaam, the num <= 1 ? false : part is literally there in the first line, without that nasty return.
But realistically this is not how isPrime should be implemented, your very first attempt is the best one so far.
If you want an actual improvement, use the fact that divisors come in pairs. If x is a divisor of num, then num/x is also divisor of num. So if you were looking for all divisors of num, you would find them in pairs as soon as x=num/x=sqrt(num), so it's enough to check numbers between 2 and Math.floor(Math.sqrt(num)), inclusive. As you preferably don't want JS to calculate this number in every iteration (when checking the condition), you could count downwards, so
for (let i = Math.floor(Math.sqrt(num)); i >= 2; i--)

Memoization giving less performance and taking more time

I was solving project euler in freecodecamp. When solving problem no 14 i use recursion and tried to increase the performance using memorization. But without memoization it is taking less time to execute and with memoization it is taking more time. Is memoization implementation is correct? What is wrong with this code? Can any one explain?
//Memoized version of the longestCollatzSequence project euler
let t1 = Date.now();
// function recurseWrapper(n) {
// let count = 0;
// let memo = {}
// function recurseCollatzSequence(n) {
// if (n in memo) {
// return memo[n];
// } else {
// if (n === 1) {
// return;
// } else if (n % 2 === 0) {
// count++;
// memo[n / 2] = recurseCollatzSequence((n / 2))
// } else {
// count++;
// memo[(3 * n) + 1] = recurseCollatzSequence(((3 * n) + 1))
// }
// return count
// }
// }
// return recurseCollatzSequence(n);
// }
//Without memoization (better performance)
function recurseWrapper(n) {
let count = 0;
function recurseCollatzSequence(n) {
if (n === 1) {
return;
} else if (n % 2 === 0) {
count++;
recurseCollatzSequence((n / 2))
} else {
count++;
recurseCollatzSequence(((3 * n) + 1))
}
return count
}
return recurseCollatzSequence(n);
}
function longestCollatzSequence(n) {
let max = 0;
let startNum = 0;
for (let i = n; i > 1; i--) {
let changeMax = recurseWrapper(i)
if (changeMax > max) {
max = changeMax;
startNum = i;
}
}
return startNum;
}
console.log(longestCollatzSequence(54512))
let t2 = Date.now() - t1;
console.log(`time taken by first instruction ${t2}`);
console.log(longestCollatzSequence(900000));
let t3 = Date.now() - t1 - t2
console.log(`time taken by second instruction ${t3}`);
let t4 = Date.now() - t1 - t2 - t3
console.log(longestCollatzSequence(1000000))
console.log(`time taken by third instruction ${t4}`);
From my limited understanding of the collatz conjecture, when starting at a number n, with all of the operations that you do, you should never see the same number again until you reach 1 (otherwise you would end up in an infinite loop). So your memo object will always hold unique keys that will never match the current number n, so if (n in memo) will never be true.
It actually seems that you want to memoize your results for further calls recurseWrapper() within your loop, so that you can prevent computing results you've already seen. At the moment you are not doing that, as you are creating a new memo object each time you call recurseWrapper(), removing all of the memoized values. You can instead return your inner auxiliary recursive wrapper function that closes over the memo object so that you keep the one memo object for all calls of the recursive wrapper function.
But even then we will still face issues, because of how the count is being calculated. For example, if you call recurseWrapper(n) and it takes 10 iterations to call recurseCollatzSequence(k), the returned count of recurseCollatzSequence(k) is going to be 10 plus whatever number it takes to compute the Collatz sequence for k. If we memoize this number, we can face issues. If we again call recurseWrapper on another number m, recurseWrapper(m), and this time it takes 20 iterations to get to the same call of recurseCollatzSequence(k), we will use our memoized value for k. But this value holds an additional count for the 10 that it took to get from n to k, and not just the count that it took to get from k to 1. As a result, we need to change how you're computing count in your recursive function so that it is pure, so that calling a function with the same arguments always produces the same result.
As Vincent also points out in a comment, you should be memoizing the current number, ie: memo[n] and not the number you're about to compute the Collatz count for (that memoization is done when you recurse):
function createCollatzCounter() {
const memo = {};
return function recurseCollatzSequence(n) {
if (n in memo) {
return memo[n];
} else {
if (n === 1) {
memo[n] = 0;
} else if (n % 2 === 0) {
memo[n] = 1 + recurseCollatzSequence(n / 2);
} else {
memo[n] = 1 + recurseCollatzSequence((3 * n) + 1);
}
return memo[n];
}
}
}
function longestCollatzSequence(n) {
let max = 0;
let startNum = 0;
const recurseWrapper = createCollatzCounter();
for (let i = n; i > 1; i--) {
let changeMax = recurseWrapper(i)
if (changeMax > max) {
max = changeMax;
startNum = i;
}
}
return startNum;
}
console.time("First");
console.log(longestCollatzSequence(54512));
console.timeEnd("First");
console.time("Second");
console.log(longestCollatzSequence(900000));
console.timeEnd("Second");
console.time("Third");
console.log(longestCollatzSequence(1000000));
console.timeEnd("Third");
In comparison, the below shows times without memo:
//Without memoization (better performance)
function recurseWrapper(n) {
let count = 0;
function recurseCollatzSequence(n) {
if (n === 1) {
return;
} else if (n % 2 === 0) {
count++;
recurseCollatzSequence((n / 2))
} else {
count++;
recurseCollatzSequence(((3 * n) + 1))
}
return count
}
return recurseCollatzSequence(n);
}
function longestCollatzSequence(n) {
let max = 0;
let startNum = 0;
for (let i = n; i > 1; i--) {
let changeMax = recurseWrapper(i)
if (changeMax > max) {
max = changeMax;
startNum = i;
}
}
return startNum;
}
console.time("First");
console.log(longestCollatzSequence(54512))
console.timeEnd("First");
console.time("Second");
console.log(longestCollatzSequence(900000));
console.timeEnd("Second");
console.time("Third");
console.log(longestCollatzSequence(1000000));
console.timeEnd("Third");
Here is a streamlined version of the solution with minimal noise around memoization and recursion.
let memo = {};
function collatzSequence(n) {
if (! (n in memo)) {
if (n == 1) {
memo[n] = 1;
}
else if ((n % 2) === 0) {
memo[n] = 1 + collatzSequence(n/2);
} else {
memo[n] = 1 + collatzSequence(3*n + 1);
}
}
return memo[n];
}
function longestCollatzSequence(n) {
let max = 0;
let startNum = 0;
for (let i = n; i > 1; i--) {
let changeMax = collatzSequence(i)
if (changeMax > max) {
max = changeMax;
startNum = i;
}
}
return startNum;
}
console.log(longestCollatzSequence(14))
And if you're willing to accept a bit of golf, here is a shorter version still of the first function.
let memo = {1: 1};
function collatzSequence(n) {
if (!(n in memo)) {
memo[n] = 1 + collatzSequence(0 === n%2 ? n/2 : 3*n+1);
}
return memo[n];
}
The reason why this matters has to do with how we think. As long as code reads naturally to us, how long it takes to write and think about it is directly correlated with how long it is. (This has been found to be true across a variety of languages in many places. I know that Software Estimation: Demystifying the Black Art certainly has it.) Therefore learning to think more efficiently about code will make it faster for you to write.
And that is why learning how to use techniques without talking about the technique you're using makes you better at that technique.
After spending some time figuring out I found a working solution.
The code below improved time complexity. Thanks all for helping me.
//Memoized version of the longestCollatzSequence project euler
let memo = {}
function recurseWrapper(n) {
let count = 0;
if (n in memo) {
return memo[n];
} else {
function recurseCollatzSequence(n) {
if (n === 1) {
return;
} else if (n % 2 === 0) {
count++;
recurseCollatzSequence((n / 2))
} else {
count++;
recurseCollatzSequence(((3 * n) + 1))
}
return count
}
let c = recurseCollatzSequence(n);
memo[n] = c;
return c;
}
}
function longestCollatzSequence(n) {
let max = 0;
let startNum = 0;
for (let i = n; i > 1; i--) {
let changeMax = recurseWrapper(i)
if (changeMax > max) {
max = changeMax;
startNum = i;
}
}
return startNum;
}
longestCollatzSequence(14)

Euler's Totient Function in JavaScript

I am trying to implement Euler's Totient Function (phi) in Javascript. So far this is what I have:
function phi(n) {
var result = n;
for (let i=2; i*i<=n; i++) {
if (n % i === 0) {
while (n % i === 0) {
n /= i;
result -= result / i;
}
}
}
if (n > 1) {
result -= result / n;
}
return result;
}
Unfortunately it all goes wrong when it comes up to multiples of 4. How do I improve this?
Inspired by https://www.geeksforgeeks.org/eulers-totient-function/
function phi(n) {
// return Greater Common Denominator of two given numbers
function gcd(a, b) {
if (a === 0) {
return b;
}
return gcd(b % a, a);
}
// init
var result = 1;
// walk through all integers up to n
for (let i = 2; i < n; i++) {
if (gcd(i, n) === 1) {
result++;
}
}
return result;
}
You should implement result = 1, then result ++ whenever you encounter a number coprime to the number you input. For that, you have to find the gcd function and that can be done with various methods, such as ArrayLists (like in Java) or with recursive functions.
Not the most eficient way, but rather straightforward:
function phi(n) {
let divArr = []; // this is an array for the common divisors of our n
let primeCount = 0; // this is a counter of divisors
for (let i = 0; i <= n - 1; i++) {
if (n % i === 0) {
divArr.push(i);
}
}
for (let j = n - 1; j > 0; j--) { //j is all potential coprimes
for (let k = divArr.length - 1; k >= 0; k--) { //we get the indeces of the divArr and thus we can loop through all the potentail divisors
//here we check if our potential coprimes are comprimes or not
//run possible coprimes through the list of divisors
if (j % divArr[k] === 0 && divArr[k] !== 1) { //if a potential coprime can be divided by any element of the array of n's divisors we break the arra's loop i. e. k and go the j++
break
} else if (j % divArr[k] !== 0) { //if a potential coprime j cannot be divided by any element of divArray then it's ok and we simply stick to the next k and waiting for 2 possible cases: either it will reach 1 and we primeCount++ or eventually be divided and then we break the loop
continue
} else if (divArr[k] === 1) { //if can be divided without a remainder, greatest common divisor is not zero so we should break the loop
primeCount++;
}
}
}
console.log(divArr, primeCount)
}

How to fix "Maximum call stack size exceeded" error with a function that needs to loop 1 million times

I'm doing a coding challenge where I need to first create a function that shows how many iterations of the Collatz conjecture it takes to get to 1, and then find the largest number of iterations for a number within 1 million.
This is the challenge: https://projecteuler.net/problem=14
And here's the code:
// Collatz Conjecture
function collatz(n) {
if (n <= 1) {
throw "the number needs to be greater than 1";
}
for(var i = 0; n != 1; i++) {
if (n % 2 == 0) {
n /= 2;
} else {
n = (n * 3) + 1;
}
}
return i;
}
// Greatest Collatz Within 1,000,000
function largestCollatz() {
var arr = [];
for(var i = 2; i <= 1000000; i++) {
arr.push(collatz(i));
}
return Math.max(...arr);
}
The collatz() function works as expected, but when I try to use largestCollatz(), it throws "Maximum call stack size reached.". I tried changing the 1 million to only a hundred thousand, and it works, but that doesn't satisfy the conditions of the challenge.
the problem is you use brute force algorithm that is inefficient.this is my solution to problem 14 from project Euler. it takes a few second to run. the key is you should save previous results in a dictionary so you don't have to compute those results again.:
#problem 14 project euler
import time
start=time.time()
has2={}
def collatz(x):
seq=[]
seq.append(x)
temp=x
while(temp>1):
if temp%2==0:
temp=int(temp/2)
if temp in has2:
seq+=has2[temp]
break
else:
seq.append(temp)
else:
temp=3*temp+1
if temp in has2:
seq+=has2[temp]
break
else:
seq.append(temp)
has2[x]=seq
return len(seq)
num=0
greatest=0
for i in range(1000000):
c=collatz(i)
if num<c:
num=c
greatest=i
print('{0} has {1} elements. calculation time ={2} seconds.'.format(greatest,num,time.time()-start))
Instead of spreading into Math.max, you can call Math.max on every iteration instead:
// Collatz Conjecture
function collatz(n) {
if (n <= 1) {
throw "the number needs to be greater than 1";
}
for (var i = 0; n != 1; i++) {
if (n % 2 == 0) {
n /= 2;
} else {
n = (n * 3) + 1;
}
}
return i;
}
// Greatest Collatz Within 1,000,000
function largestCollatz() {
var arr = [];
let maxSoFar = 0;
for (var i = 2; i <= 1000000; i++) {
maxSoFar = Math.max(maxSoFar, collatz(i));
}
return maxSoFar;
}
console.log(largestCollatz());
If you wanted to increase the performance of the code, you could create a lookup table for the number of iterations required for each number, and use that lookup table instead of brute force when possible.
const map = new Map();
function collatz(n) {
if (n <= 1) {
throw "the number needs to be greater than 1";
}
if (map.has(n)) {
return map.get(n);
}
const next = n % 2 === 0 ? n / 2 : (n * 3) + 1;
if (next === 1) {
return 1;
}
const result = 1 + collatz(next);
map.set(n, result);
return result;
}
// Greatest Collatz Within 1,000,000
function largestCollatz() {
var arr = [];
let maxSoFar = 0;
for (var i = 2; i <= 1000000; i++) {
maxSoFar = Math.max(maxSoFar, collatz(i));
}
return maxSoFar;
}
console.log(largestCollatz());

Least Common Multiple - for loop breaks down - javascript

I'm taking a course on FreeCodeCamp.org and the assignment is to find "Smallest Common Multiple". So I came up with a solution I think works and I does up to a certain point. Then the code just seems like it's breaking down. Here is my code:
function smallestCommons(arr) {
arr = arr.sort((a,b) => {return a - b;});
console.log(arr);
var truesec = false;
for(var a = arr[1]; truesec != true; a++){
for(var e = 1; e <= arr[1]; e++){
//console.log(a % e + " " + e);
if(a % e != 0){
truesec = false;
break;
}else{
truesec = true;
}
}
//console.log(truesec + " " + a);
if(truesec == true){
return a;
}
}
return a;
}
console.log(smallestCommons([23,18]));
This should return 6056820 according to their checklist but every time I check I get a different result I've gotten both 114461 & 122841 from the same code. Can somebody please tell me what is wrong with this?
Here is the assignment if it helps:
Intermediate Algorithm Scripting: Smallest Common Multiple
What your algorithm trying to do is find the common multiple between 1 and the greater number in the array, which might take a very long time. However, the question from the FreeCodeCamp asks you to find the common multiple between the two numbers in the array, so the result calculated from your algorithm does not match the tests.
To make your solution works, you can change
from for (var e = 1; e <= arr[1]; e++)
to for (var e = arr[0]; e <= arr[1]; e++)
in order to loop between two numbers in the array.
I would take a different approach to this problem:
create function to get all prime factors
create array of prime factor of all number between a[0] and a[1]
reduce the array as the biggest power for each prime factor.
multiple all the prime factor left in the array
Your approach will take O(k*a[1]) when k is the answer - and k can be very high... This approach will take O((a[1])^2)
Consider the following code:
function smallestCommons2(arr) {
arr.sort((a,b) => {return a - b;});
let factors = [];
for(let i = arr[0]; i <= arr[1]; i++)
factors.push(findPrimeFactors(i));
let reduced = reduceFactors(factors);
let ans = 1;
for (let i in reduced)
ans *= Math.pow(i, reduced[i]);
return ans;
}
function reduceFactors(factorsArr) {
let factorObject = {};
for (let i in factorsArr) {
for(let key in factorsArr[i]) {
if (!(key in factorObject) || factorObject[key] < factorsArr[i][key])
factorObject[key] = factorsArr[i][key];
}
}
return factorObject;
}
function findPrimeFactors (num) {
var primeFactors = [];
while (num % 2 === 0) {
primeFactors.push(2);
num = num / 2;
}
var sqrtNum = Math.sqrt(num);
for (var i = 3; i <= sqrtNum; i++) {
while (num % i === 0) {
primeFactors.push(i);
num = num / i;
}
}
if (num > 2)
primeFactors.push(num);
let factorObject = {};
for (let item of primeFactors) {
if (item in factorObject)
factorObject[item] += 1;
else factorObject[item] = 1;
}
return factorObject;
}
console.log(smallestCommons2([23,18]));
This code will output 6056820 in sec
Edited - found a post that do the same thing in a better way

Categories