I managed to solve the problem but I am exceeding the time limit.
What to fix: If the given number n is very large it takes ridiculous amount of time.
Question: Is there a way I can tweak the code to get the last prime number faster?
const n = 126;
let lastPrime = 0;
for (let j = 2; j <= n; j++) {
let counter = 0;
for (let i = 1; i <= j; i++) {
if (j % i === 0) counter++;
}
if (counter === 2) lastPrime = j;
}
print(lastPrime); // Biggest prime number of 126 is 113
Thanks!
A basic question I ask myself when looking at sieve code is: "does this code use the modulo (%) operator?" If it does, it isn't the Sieve of Eratosthenes. You're doing trial division, which is vastly slower. Now there are certainly places and reasons for trial division, but based on your question title, you intended to use the SoE.
See Wikipedia pseudocode for example. The only operations are the two loops that involve only simple additions, a test, and a set. That's it. No multiplies, no divides, no modulos. This is absolutely key to why the algorithm is fast. The inner loop also quickly becomes sparse, in the sense that the increment keeps getting larger, so we actually run the inner loop code fewer times as we go on. Contrast to the initial code you posted, where the inner loop is running more times.
To do the basic SoE, you need a small array, then exactly and only 4 lines of code to do the sieving, with the outer loop going to sqrt(n). Then you can examine the array which will have only primes left marked. In your case, you can walk it backwards and return when you find the first occurrence. There are countless methods for optimization, but it's surprising how fast the simple basic SoE is for relatively small n (e.g. under 10^9, after which you really need to do a segmented SoE).
All that said, you got working code, which is a great first step. It's much easier to try different methods once you have something working.
There are plenty of optimizations to be done here - a quick read here (https://math.stackexchange.com/questions/889712/the-fastest-way-to-count-prime-number-that-smaller-or-equal-n/893767) would help.
But for starters, you can change a few things in your code to make it trivially faster:
Step 1: Reduce the number of outer iterations, since we know all even numbers are non-prime.
for (let j = 3; j <= n; j += 2) {
...
}
Step 2: Reduce the inner-loop by only iterating up to a max of Sqrt of the max number. Also break out of the inner loop once we find even one factor. No need to iterate till the end. These two will give you the biggest wins.
let prime = true;
for (let i = 2; i <= Math.sqrt(j); i++) {
if (j % i === 0) {
prime = false;
break;
}
}
if (prime) {
lastPrime = j;
}
Step 3: Stop computing Math.sqrt(j) since you already know the previous max value. sqrt is a (relatively) expensive operation. We can avoid it by making use of previous value.
let maxBound = 2;
let maxSquare = maxBound * maxBound;
for (let j = 3; j <= n; j += 2) {
if (maxSquare < j) {
maxBound++;
maxSquare = maxBound * maxBound;
}
for (let i = 2; i <= maxBound; i++) {
...
}
}
Step 4: If all you want is the biggest prime, walk the loop backwards and break as soon as you find one prime.
And here's the finished program which should be approximately 2 orders of magnitude faster than yours. Note that while I provided some trivial optimizations for your program, this will always pale in comparison to algorithmic optimizations that you can find here: https://math.stackexchange.com/a/893767
function getMaxPrime(n) {
for (let j = n; j >= 3; j --) {
let prime = true;
for (let i = 2; i <= Math.sqrt(j); i++) {
if (j % i === 0) {
prime = false;
break;
}
}
if (prime) {
maxPrime = j;
break;
}
}
console.log(maxPrime);
}
Related
I found the code for solving this problem, but I cannot figure out the logic of the solution.
let n = 10;
nextPrime:
for (let i = 2; i <= n; i++) {
for (let j = 2; j < i; j++) {
if (i % j == 0) continue nextPrime;
}
alert( i );
}
I can't understand how the second for works, if it has an increment like the first, then the result should have been all numbers from 2 to 10, since the beginning of both for is the same number (2) ..
Can you please explain each iteration, for example why 4 is not displayed?
It’s using label to break the inner loop when it finds number is not prime . Outer loop is iterating the number and inner loop checks if it’s divisible between 2 to number.
Label break
the continue nextPrime used for just continue in the loop to the next iteration without finish all the code inside the loop.
like:
data:
for (let i = 1; i <= 3; i++) {
if(i === 2)
continue data;
console.log(i)
}
here when I get to I equal to 2 the code continue to the next number without continue to the rest of the code
It is supposed to print prime n numbers. The for loop will run from 2 to x which will iterate each time. if i == x then it means that the number was not divisible and so it should be printed as prime
var n;
var x = 2;
var i;
function prime(n) {
while (n) {
for (i = 2; i < x; i++) {
if (x % i == 0) {
break;
}
if (i == x) {
document.write(i + " ");
n--;
}
x++;
}
}
}
prime(10);
When you try to execute this code, this will never get into the for loop and goes into an infinite while loop. You have got:
i = 2; i < x;
The i will never be less than x. And it doesn't enter the for loop and comes out. And n will always be 10, that goes on into an infinite loop.
You need to use the modulus operator to check if a number is divisible by them.
Maybe change your approach a bit and try to find the first X prime number using just for loops.
var n;
var x = 2;
var i;
function prime(n) {
if (n <= 0) return;
var i, j, p_no = 0, res = [];
for (i = 2; ; i++) {
var ifPrime = true;
for (j = 2; ifPrime && j <= Math.sqrt(i); j++) {
if (i % j === 0) ifPrime = false;
}
if (ifPrime) {
res.push(i);
console.log(i + ' ');
p_no++;
if (p_no === n) return res.toString();
}
}
}
document.getElementById('prime').innerHTML = prime(10);
<p id="prime"></p>
What's happening when the code runs is what Praveen describes. I want to address how you got to your algorithm in the first place.
It looks like you're trying to print all primes less than a specific number n. You've jumbled different aspects of your algorithm together. Specifically, you've combined a loop that exists to find whether a number is prime with a loop over all numbers less than n.
The first thing you can do to help manage this complexity is to use methods. If you had a method isPrime(k) that returns true or false if a given number is prime, then your function's main loop looks much simpler, and separates the two problems from each other:
function prime(n) {
for (var i = n; i > 1; i--) {
if (isPrime(i)) {
document.write(i + " ");
}
}
}
Then you can focus on defining the isPrime method separately, without getting its parts confused with the main loop:
function isPrime(k) {
for (var i = 2; i < k; i++) {
if (k % i == 0) {
return false;
}
}
return true;
}
Methods are a fantastic way of keeping algorithms simpler by isolating their components. They're building blocks one can use to make more complex systems without having to keep track of the whole. It lets you make smaller changes, each encapsulated from other concerns, meaning you have less to keep in your mind while you're making those changes. The less you have to keep in your mind, the easier it is to spot mistakes.
for (let i = 0; i < array.length; i += 1) {
const row = array[i];
for (let j = 0; j < row.length; j += 1) {
const el = row[j];
}
}
Would be a typical way to iterate through a 2D array nxn matrix and I'd consider that O(n^2) time complexity.
If I did this instead
let count = 0;
let i = 0;
let j = 0;
const n = arr.length;
const max = n * n;
while (count !== max) {
const ele = arr[i][j];
if (j === n - 1) {
j = 0;
i += 1;
} else j += 1;
count += 1;
}
Would it still be O(n^2)? Kind of a stupid question, I think the answer is yes, but I just want to double check. Obviously, the first method is much clearer, but then lower time complexity is good as well.
Well, it's not really O(n2) in the first place.
Big-O tells you the worst-case performance of an algorithm by giving an idea of how its time of execution scales with the number of elements handled by the algorithm grows.
In the case of a 2-D matrix, while the matrix is, indeed, square (or, at least, rectangular), it's not really appropriate to use the length of the matrix as n here. Rather, you should be using the number of cells in the matrix (i x j).
A 2-D matrix is, effectively, an array of arrays and your algorithm is simply stepping through each cell once, making it O(n) in both cases. You could say that it's O(ixj), but it's still a linear algorithm.
Given n = max(array.length, array[0].length):
Yes - they are both O(n^2). Even though it's one loop, the number of elements that the while loop goes through is the same as the number of elements that the 2 for loops go through.
In other words, with the for loop you're going through (approximately) n-sized chunks n times, and with the while loop you're going through an n^2-sized chunk once.
I am attempting to determine all possible sums from rolling n dice, where n is not known at compile time.
For two dice, the solution is straightforward, just iterate through both dice and add each possible side to each other. If passing in 2 6-sided dice, the sorted results would be: [2,3,3,4,4,4,5,5,5,5,6,6,6,6,6,7,7,7,7,7,7,8,8,8,8,8,9,9,9,9,10,10,10,11,11,12]
I tried to expand this solution to any n dice, but I realized that I need n for loops.
for(let i = 0; i < numDice; i++)
{
dice.push(sides);
}
for(let i = 0; i < numSides; i++)
{
for(let j = 1; j < dice.length; j++)
{
for(let k = 0; k < numSides; k++)
{
results.add(dice[0][i] + dice[j][k]);
}
}
}
I also attempted a recursion-based approach as the first question below suggested. I believe it will loop the correct number of times, but I couldn't figure out how to define my summing function without introducing yet more loops.
function doCallMany(numDice, numSides, sumFunc)
{
if(numDice == 0)
{
sumfunc(numDice, numSides) //?
}
else
{
for(let i = 0; i < numSides; i++)
{
doCallMany(numDice--, numSides, sumFunc)
}
}
}
I looked at similar questions here and here but they do not answer my question. The first doesn't because the action I need to perform in the loops is not independent. The second is close, but the answers rely on Python-specific answers.
The comment about the complexity of the solutions is correct. It gets big quickly. Having said that, to address your original question, you can do this with a fairly simple recursive function for small input. Basically you start with an array of dice, pop one off add it to a sum and recurse with that sum and the rest of the array.
For example:
function sums(dice, sum = 0, ans = []) {
if (dice.length === 0) ans.push(sum) // edge case, no more dice
else {
let d = dice[0]
for (let i = 1; i <= d; i++) {
sums(dice.slice(1), sum + i, ans) // recurse with remaining dice
}
return ans
}
}
// two six-sided dice
let ans = sums([6, 6])
console.log(JSON.stringify(ans.sort((a, b) => a - b)))
// three three-sided dice
ans = sums([3, 3, 3])
console.log(JSON.stringify(ans.sort((a, b) => a - b)))
I suggest you use the backtracking method.
It lets you vary the number of loops you want to execute. You can even execute a random number of loops, as the number of loops can be held in a variable.
I made a code to extract every odd numbers from one number, and it works for numbers that are not too long such as "1341" (which give me the numbers "1,13,1341,341,41,1") but oddly doesn't work for very long numbers.
function solve(s) {
var newarray = [];
for (var i = 0; i <= s.length; i++) {
for (var j = 0; j <= s.length; j++) {
var slicing = s.slice(i, j);
if (slicing % 2 !== 0) {
newarray.push(slicing);
}
}
}
return newarray.length;
}
Despite putting s.length, it slices until a certain point. For example:
With "93711892377292643581488317", it slices until "9371189237729", then when it starts from 3 it slices until "93711892377292643" (until the next odd number)
With "65266112954758467", from the start it slices until "6526611295475", then when it starts from 5, it slices until "65266112954758467" (until the next odd number).
What's going on?
slicing % 2 doesn't work properly when slicing is large. Javascript treats large numbers as floating-point numbers, which means it's not accurate to know the value to the nearest integer - in binary, the units bit becomes 0, so it's a multiple of 2.
You want to count all odd numeric substrings within a numeric string.
First, consult the documentation of str.slice(beginIndex[, endIndex]).
Then, in order to gain a better understanding of your code, it is helpful to slowly iterate through a few steps of your loops and write down the expected vs. the observed output.
I recommend to use the debugger built into all modern browsers:
Add a debugger; statement into the inner for-loop:
function solve(s) {
var newarray = [];
for (var i = 0; i <= s.length; i++) {
for (var j = 0; j <= s.length; j++) {
var slicing = s.slice(i, j);
debugger; // <-- we want to break here and check our values
if (slicing % 2 !== 0) {
newarray.push(slicing);
}
}
}
return newarray.length;
}
Press [F12] and run this code in your browser's console for some exemplary input.
The debugger tab should now pop up. Press [F8] to step through your code and keep track of the value of your slicing variable.
You will probably notice that slicing is empty at the beginning. You should start your inner loop from j = i + 1 to fix that.
Also, you might notice that your i iterates one time too many, so that slicing is empty during the final iterations of the inner for-loop. You need to terminate your outer loop one step earlier.
Then, for the problematic input "93711892377292643581488317" you will notice that large numeric slices such as "93711892377292643" will not be recognized as odd. "93711892377292643" % 2 evaluates to 0 instead of 1. In order to understand this, you need to know that JavaScript numbers are internally represented as limited precision floating point values. If you put 93711892377292643 into your browser console, it will evaluate to 93711892377292640 - an even number! JavaScript can only handle integer numbers up to Number.MAX_SAFE_INTEGER == 9007199254740991 without introducing such truncation errors.
Now, how to solve this issue? Well, a number is odd if and only if the last digit is odd. So we don't have to inspect the full number, just the last digit:
function solve(s) {
var newarray = [];
for (var i = 0; i < s.length; i++) {
for (var j = i; j < s.length; j++) {
var digit = s.slice(j, j + 1);
if (digit % 2 !== 0) {
var slicing = s.slice(i, j + 1);
newarray.push(slicing);
}
}
}
return newarray.length;
}
console.log(solve("1234567890")); // 25
Once you have sufficient understanding of this code, you could start improving it. You could for example replace the newarray with a simple counter, as you are only interested in the number of off digits, not the digits themselves.
A faster solution could be written down as follows:
function solve(str) {
let count = 0;
for (let i = 0; i < str.length; i++) {
if (str[i] % 2) count += i + 1;
}
return count;
}
console.log(solve("1234567890")); // 25
Or, written in a more declarative way:
const solve = (str) =>
str.split('').reduce((count, chr, i) => chr % 2 ? count + i + 1 : count, 0);
console.log(solve("1234567890")); // 25