JS - Prime numbers script - explanation - javascript

I'm learning JS and stumbled upon a script to output the prime numbers < 100 . But I'm not sure I fully understood how it works. If somebody could explain it, I would be glad. :)
So here it is:
for (var counter = 0; counter <= 100; counter++)
{
for (var i = 2; i <= counter-1; i++)
if (counter%i === 0) break;
if(i === counter)
console.log(counter);
}

Primes are by definition only divisible by themselves and 1.
The outer loop for var(counter = 0; ...) loops through the numbers from 0 to 100.
The inner loop for (var i = 2; ...) then tries to divide the outer number by every number between 2 and the counter's value. If any of them divide the number without a remainder the loop is broken. That's the if (counter%i === 0) break; line.
If we broke out of loop and i === counter, it's a prime, else it's not - by definition.

/* For every number from 0 to 100, do the following: */
for (var counter = 0; counter <= 100; counter++)
{
/* Loop through values from 2 to 1 before the counter. */
for (var i = 2; i <= counter-1; i++) {
/* if the remainder of dividing counter by the current value of `i` is zero,
* we know we don't have a prime, so break out of the loop:
*/
if (counter%i === 0) break;
}
/* If the loop completed and `i` is equal to the counter, that means counter is not
* divisible by anything except for 1 and itself, making it prime
*/
if(i === counter)
console.log(counter);
}

I'm not sure how much JavaScript you know, so some of this might be really basic.
First, remember that a prime number is one whose only factors are 1 and itself.
So the first line says it loops from 0 to 100, with counter as the variable holding this number.
The next line for (var i = 2; i <= counter-1; i++) loops from 2 to counter-1 (notice it excludes 1 and counter) and uses i to hold this number.
Next, if (counter%i === 0) break; tests whether counter is divisible by i: the remainder operator % returns the remainder (like from long division), e.g. 11 % 3 will return 2 because 11 divided by 3 gives 3 remainder 2; if the remainder counter % i is 0 (=== is a stricter equality than ==, but that's a different answer altogether) that means that counter is divisible by i and so is not prime. The break statement tells it to exit the innermost loop (that is, the loop over i), so the program increments counter and goes on.
If the previous line did not break for any value of i, then counter is prime ā€” specifically, at the end of the loop body, i is incremented (now equal to counter), the statement i <= counter-1 evaluates to false, and the loop ends. So now i === counter is true, so console.log(counter) shows this value in the console.

Related

Write a function that flips a coin and returns the number of times landed heads?

Here is what I have so far. It keeps returning 0 no matter how many times I try to "flip" it.
**var n = prompt('How many times do you want to flip the coin?')
parseInt(n)
function coinFlip(n){
var numberOfHeads = 0
var numberOfTails = 0
for (a = 0; a === n; a++){
var flippedCoin = Math.random()
if (flippedCoin > .5)
numberOfHeads = 1 + numberOfHeads
else
numberOfTails = 1 + numberOfTails
}
return numberOfHeads**
}
console.log(coinFlip(n))
The second section of a for loop is the condition that must be true for the body of the loop to run.
You have said a === n so there are two possibilities:
The value entered is 0
a starts at 0, so a === n, the loop runs once, then a becomes 1 and it no longer matches so the loop exits.
The value entered is not 0
Since a starts at 0 it never matches and the loop is never entered.
The condition you test against should be while a < n.
change for statement to this
for (a = 0; a < n; a++)

Trying to optimize my code to either remove nested loop or make it more efficient

A friend of mine takes a sequence of numbers from 1 to n (where n > 0)
Within that sequence, he chooses two numbers, a and b
He says that the product of a and b should be equal to the sum of all numbers in the sequence, excluding a and b
Given a number n, could you tell me the numbers he excluded from the sequence?
Have found the solution to this Kata from Code Wars but it times out (After 12 seconds) in the editor when I run it; any ideas as too how I should further optimize the nested for loop and or remove it?
function removeNb(n) {
var nArray = [];
var sum = 0;
var answersArray = [];
for (let i = 1; i <= n; i++) {
nArray.push(n - (n - i));
sum += i;
}
var length = nArray.length;
for (let i = Math.round(n / 2); i < length; i++) {
for (let y = Math.round(n / 2); y < length; y++) {
if (i != y) {
if (i * y === sum - i - y) {
answersArray.push([i, y]);
break;
}
}
}
}
return answersArray;
}
console.log(removeNb(102));
.as-console-wrapper { max-height: 100% !important; top: 0; }
I think there is no reason for calculating the sum after you fill the array, you can do that while filling it.
function removeNb(n) {
let nArray = [];
let sum = 0;
for(let i = 1; i <= n; i++) {
nArray.push(i);
sum += i;
}
}
And since there could be only two numbers a and b as the inputs for the formula a * b = sum - a - b, there could be only one possible value for each of them. So, there's no need to continue the loop when you find them.
if(i*y === sum - i - y) {
answersArray.push([i,y]);
break;
}
I recommend looking at the problem in another way.
You are trying to find two numbers a and b using this formula a * b = sum - a - b.
Why not reduce the formula like this:
a * b + a = sum - b
a ( b + 1 ) = sum - b
a = (sum - b) / ( b + 1 )
Then you only need one for loop that produces the value of b, check if (sum - b) is divisible by ( b + 1 ) and if the division produces a number that is less than n.
for(let i = 1; i <= n; i++) {
let eq1 = sum - i;
let eq2 = i + 1;
if (eq1 % eq2 === 0) {
let a = eq1 / eq2;
if (a < n && a != i) {
return [[a, b], [b, a]];
}
}
}
You can solve this in linear time with two pointers method (page 77 in the book).
In order to gain intuition towards a solution, let's start thinking about this part of your code:
for(let i = Math.round(n/2); i < length; i++) {
for(let y = Math.round(n/2); y < length; y++) {
...
You already figured out this is the part of your code that is slow. You are trying every combination of i and y, but what if you didn't have to try every single combination?
Let's take a small example to illustrate why you don't have to try every combination.
Suppose n == 10 so we have 1 2 3 4 5 6 7 8 9 10 where sum = 55.
Suppose the first combination we tried was 1*10.
Does it make sense to try 1*9 next? Of course not, since we know that 1*10 < 55-10-1 we know we have to increase our product, not decrease it.
So let's try 2*10. Well, 20 < 55-10-2 so we still have to increase.
3*10==30 < 55-3-10==42
4*10==40 < 55-4-10==41
But then 5*10==50 > 55-5-10==40. Now we know we have to decrease our product. We could either decrease 5 or we could decrease 10, but we already know that there is no solution if we decrease 5 (since we tried that in the previous step). So the only choice is to decrease 10.
5*9==45 > 55-5-9==41. Same thing again: we have to decrease 9.
5*8==40 < 55-5-8==42. And now we have to increase again...
You can think about the above example as having 2 pointers which are initialized to the beginning and end of the sequence. At every step we either
move the left pointer towards right
or move the right pointer towards left
In the beginning the difference between pointers is n-1. At every step the difference between pointers decreases by one. We can stop when the pointers cross each other (and say that no solution can be obtained if one was not found so far). So clearly we can not do more than n computations before arriving at a solution. This is what it means to say that the solution is linear with respect to n; no matter how large n grows, we never do more than n computations. Contrast this to your original solution, where we actually end up doing n^2 computations as n grows large.
Hassan is correct, here is a full solution:
function removeNb (n) {
var a = 1;
var d = 1;
// Calculate the sum of the numbers 1-n without anything removed
var S = 0.5 * n * (2*a + (d *(n-1)));
// For each possible value of b, calculate a if it exists.
var results = [];
for (let numB = a; numB <= n; numB++) {
let eq1 = S - numB;
let eq2 = numB + 1;
if (eq1 % eq2 === 0) {
let numA = eq1 / eq2;
if (numA < n && numA != numB) {
results.push([numA, numB]);
results.push([numB, numA]);
}
}
}
return results;
}
In case it's of interest, CY Aries pointed this out:
ab + a + b = n(n + 1)/2
add 1 to both sides
ab + a + b + 1 = (n^2 + n + 2) / 2
(a + 1)(b + 1) = (n^2 + n + 2) / 2
so we're looking for factors of (n^2 + n + 2) / 2 and have some indication about the least size of the factor. This doesn't necessarily imply a great improvement in complexity for the actual search but still it's kind of cool.
This is part comment, part answer.
In engineering terms, the original function posted is using "brute force" to solve the problem, iterating every (or more than needed) possible combinations. The number of iterations is n is large - if you did all possible it would be
n * (n-1) = bazillio n
Less is More
So lets look at things that can be optimized, first some minor things, I'm a little confused about the first for loop and nArray:
// OP's code
for(let i = 1; i <= n; i++) {
nArray.push(n - (n - i));
sum += i;
}
??? You don't really use nArray for anything? Length is just n .. am I so sleep deprived I'm missing something? And while you can sum a consecutive sequence of integers 1-n by using a for loop, there is a direct and easy way that avoids a loop:
sum = ( n + 1 ) * n * 0.5 ;
THE LOOPS
// OP's loops, not optimized
for(let i = Math.round(n/2); i < length; i++) {
for(let y = Math.round(n/2); y < length; y++) {
if(i != y) {
if(i*y === sum - i - y) {
Optimization Considerations:
I see you're on the right track in a way, cutting the starting i, y values in half since the factors . But you're iterating both of them in the same direction : UP. And also, the lower numbers look like they can go a little below half of n (perhaps not because the sequence start at 1, I haven't confirmed that, but it seems the case).
Plus we want to avoid division every time we start an instantiation of the loop (i.e set the variable once, and also we're going to change it). And finally, with the IF statements, i and y will never be equal to each other the way we're going to create the loops, so that's a conditional that can vanish.
But the more important thing is the direction of transversing the loops. The smaller factor low is probably going to be close to the lowest loop value (about half of n) and the larger factor hi is probably going to be near the value of n. If we has some solid math theory that said something like "hi will never be less than 0.75n" then we could make a couple mods to take advantage of that knowledge.
The way the loops are show below, they break and iterate before the hi and low loops meet.
Moreover, it doesn't matter which loop picks the lower or higher number, so we can use this to shorten the inner loop as number pairs are tested, making the loop smaller each time. We don't want to waste time checking the same pair of numbers more than once! The lower factor's loop will start a little below half of n and go up, and the higher factor's loop will start at n and go down.
// Code Fragment, more optimized:
let nHi = n;
let low = Math.trunc( n * 0.49 );
let sum = ( n + 1 ) * n * 0.5 ;
// While Loop for the outside (incrementing) loop
while( low < nHi ) {
// FOR loop for the inside decrementing loop
for(let hi = nHi; hi > low; hi--) {
// If we're higher than the sum, we exit, decrement.
if( hi * low + hi + low > sum ) {
continue;
}
// If we're equal, then we're DONE and we write to array.
else if( hi * low + hi + low === sum) {
answersArray.push([hi, low]);
low = nHi; // Note this is if we want to end once finding one pair
break; // If you want to find ALL pairs for large numbers then replace these low = nHi; with low++;
}
// And if not, we increment the low counter and restart the hi loop from the top.
else {
low++;
break;
}
} // close for
} // close while
Tutorial:
So we set the few variables. Note that low is set slightly less than half of n, as larger numbers look like they could be a few points less. Also, we don't round, we truncate, which is essentially "always rounding down", and is slightly better for performance, (though it dosenit matter in this instance with just the single assignment).
The while loop starts at the lowest value and increments, potentially all the way up to n-1. The hi FOR loop starts at n (copied to nHi), and then decrements until the factor are found OR it intercepts at low + 1.
The conditionals:
First IF: If we're higher than the sum, we exit, decrement, and continue at a lower value for the hi factor.
ELSE IF: If we are EQUAL, then we're done, and break for lunch. We set low = nHi so that when we break out of the FOR loop, we will also exit the WHILE loop.
ELSE: If we get here it's because we're less than the sum, so we need to increment the while loop and reset the hi FOR loop to start again from n (nHi).

Why is this while statement creating an infinite loop?

Without the if statement this loops works fine, however as soon as I add the if statement it turns it into an infinite loop-why? From my understanding continue should make the loop skip an iteration and then run as normal?
let num=0;
while(num<10){
if(num===4){console.log("skipping "+num);
continue;
}
console.log(num++);
}
You need also to increment the num in the if block. Without it after the if statement it never reaches to the num++ and you never change the value of num, so it stays 4 and every time goes into the if. You can add ++ in the if statement.
let num = 0;
while(num < 10) {
if(++num === 4) {
console.log("skipping " + num);
continue;
}
console.log(num);
}
Inside your while loop when num gets incremented to 4, it enters the if block and you are not incrementing num inside if block.
Also you are using continue which skips the code in the current iteration and moves to the next iteration. This keeps on happening and num is never incremented which leads to an infinite loop.
The following code prints numbers from 0 to 9 skipping 4 as asked in the question.
let num = 0;
while(num < 10) {
if(num === 4) {
console.log("skipping " + num++);
continue;
}
console.log(num++);
}

Sum of Odd Fibonnaci failing with Higher values?

So im running into an odd error, where im summing all fibonnaci numbers that are odd and LESS than a number.
the odd thing is this works with low values, but when I get to upper values past 10 or so.....it'll crash codepen.io
here is what I have so far:
function f(n)
{
if(n <= 1)
return n;
return f(n-1)+f(n-2);
}
function sumFibs(num) {
var counter = 0;
var arr = [];
//Get all Fibbonaci Numbers up to num
for(let i = 1;i <= num;i++)
{
arr.push(f(i));
}
for(let j = 0;j < arr.length;j++)
{
if(arr[j] % 2 != 0 && arr[j] <=num)
{
counter+= arr[j];
}
}
console.log(counter);
return counter;
}
sumFibs(10);
Basically I calculate fib up to the num and then I go through each odd one thats less than or equal to num and add those up.
Im getting correct values (IE for 10 i get 10, for 4 i get 5....etc...)
but if I put in something like 1000 it seems to just crash? and I can't seem to figure out any reason why?
The recursive f() function is a logical way to express a Fibonacci number calculation, but it isn't very efficient compared to an iterative approach, especially because you are calling it repeatedly from inside a loop. I think this is bringing your browser to a halt. Within the loop each time you call f() it is calculating the specified Fibonacci number from scratch by recursively calling itself. So, say, to get f(10), it calls itself twice with f(9) + f(8) (and then they in turn call f(8)+f(7) and f(7)+f(6), etc., so even that is pretty inefficient), but in fact you already know what f(9) and f(8) are because you've stored those values in your array on previous loop iterations.
If you change your loop to calculate each subsequent number directly rather than calling another function you get much faster code:
var arr = [1, 1]; // start with the known first two numbers
//Get all Fibbonaci Numbers up to num
for(let i = 2; i < num; i++) // start the loop at index 2 for the third number
{
arr[i] = arr[i-2] + arr[i-1];
}
With that change in place, your sumFibs() function can give you results even for sumFibs(1000000) in a matter of milliseconds:
function sumFibs(num) {
var counter = 0;
var arr = [1, 1];
//Get all Fibbonaci Numbers up to num
for (let i = 2; i < num; i++) {
arr[i] = arr[i - 2] + arr[i - 1];
}
for (let j = 0; j < arr.length; j++) {
if (arr[j] % 2 != 0) {
counter += arr[j];
}
}
return counter;
}
console.log('10: ' + sumFibs(10));
console.log('100: ' + sumFibs(100));
console.log('1000: ' + sumFibs(1000));
console.log('10000: ' + sumFibs(10000));
console.time('High fib');
console.log('1000000: ' + sumFibs(1000000));
console.timeEnd('High fib');
Note that you also had a logic error in your second loop, the one that adds up the odd numbers: the && arr[j] <=num part needed to be removed. The values in arr are the actual Fibonacci numbers, but num is the sequence number, so it doesn't make sense to be comparing them. You just want every odd number in the whole array.
However, the return value from your function is still going to be incorrect if num is too large. That's because by the time you get to the 80-somethingth Fibonacci number it is larger than JavaScript can handle without losing precision, i.e., larger than Number.MAX_SAFE_INTEGER, 9,007,199,254,740,991 (which is 2^53 - 1). Numbers above that start getting rounded so your tests for odd numbers aren't reliable and thus the total sum doesn't include all of the numbers it should have, or if you add too many JS considers your result to be Infinity.

Sorting out numbers in for loops

So I am determining which is a prime number and which isn't, but I am just not understanding how it ends up with the correct output.
So the first starts at 2 and loops by 1 to 100. Easy.
But the second starts at 0, and loops by y + itself, this would make sense, but in determining the primes, it should mess up, atleast I thought
it's like: 1+3 = 4 or 2 + 4 = 6 or 3 + 5 = 8
and that works, but what happens to let's say the 15? that isn't a prime number.
How is numbers like that sorted in the loop?
var prim = [];
var notprim = [];
for(var x = 2; x <= 100; x++){
if(!notprim[x]){
prim.push(x);
for(var y = 0; y <= 100; y = y+x){
notprim[y] = true;
document.write(y);
}
}
}
You have an Array notprim that you can imagine as [undefined Ɨ 100], and !!undefined === false, i.e. undefined is falsy
If for some number n you have notprim[n] falsy, you assume it means n must be a prime number and add it to another Array, prim
Then you set all multiples of n to be truthy in notprim, i.e. if n is 3, you set notprim[n * x] = true;, i.e. 0, 3, 6, 9, 12, 15, etc
You then look for the next falsy index in notprim to start again
The reason the first loop starts at 2 is because 2 is the first prime number, starting from 1 or 0 would cause the assumption that "notprim[n] falsy means n is a prime number" to fail
Great, but what about the other loop? Well, one way of going through n * x is to add n to itself x times. When you're thinking of it this way, you can then limit how high you go without knowing a maximum multiplier in advance by looking at the running total, for example in a for loop
for (t = 0; t <= 100; t = t + n)
// t āˆˆ nā„¤, 0 <= t <= 100
but what happens to lets say the 15?
When you've found the prime number 3, you then flag all multiples of 3 to be excluded from your search for primes. 15 is a multiple of 3 so gets flagged as not a prime. Hence your if (!notprim[x]) does not pass
You can reduce the number of iterations this code needs by excluding 0 and x from the second for loop; i.e. begin from the index y = 2 * x

Categories

Resources