Why does my JavaScript Prime Number-Generator not work? - javascript

The aim of this program is to list all the prime numbers from 1 up.
I am a complete novice. I began writing this in C++ (unsuccessfully) and translated to this (JS)
I am aware of some problems but don't know how to solve them:
handling global variables,
writing replaces,
no timeout before writing, etc.
is it important that i use an external JS file?
In summary, how do i make this work?
<!doctype html>
<html>
<head>
<script>
var number = 3; //to be run through the prime test
var prime = [2]; //numbers found to be prime
var found = 1; //counter for primes found
var runs = 0; //counter for times number has been tested
//numbers are tested against all smaller found primes
function test()
{
window.scroll(0,document.height); //automatically view the latest prime
var line = document.createElement("div");
while(runs < found && (prime[runs] < (number / 2))) //has number passed all tests
{
if(number % prime[runs] !== 0) //is number divisible by smaller prime
{
runs = runs + 1; //number passed a test
}
else //number is not prime
{
number = number + 1; //next number generated
runs = 0; //reset test counter for next number
setTimeout(test, 100); //start running next number tests
}
} //number passed all tests
line.innerHTML = number; //prime number displayed
document.body.appendChild(line);
prime[found] = number; //prime number saved for testing
found = found + 1; //counter for found is increased
number = number + 1; //next number generated
runs = 0; //reset test counter for next number
setTimeout(test, 100); //start running next number tests
}
</script>
</head>
<body>
<form>
<input type="button" onclick="test()">
</form>
</body>
</html>

On the mathematical side,
var prime = [1];
1 isn't a prime, and since 1 divides every number,
while (runs < found) //has number passed all tests
{
if (number % prime[runs] !== 0) //is number divisible by smaller prime
{
runs = runs + 1; //number passed a test
}
else //number failed a test
{
number = number + 1; //next number generated
runs = 0; //reset test counter for next number
setTimeout(test, 1000); //start running next number tests
}
} //number passed all tests
always enters the else branch for runs = 0.
You can start with var prime = [2]; or with var prime = []; and var found = 0;.

A few things to note:
for (runs < found)
You appear to want a while loop here. for loops cannot consist just of a condition.
document.writeln() cannot be used after the document has finished loading and has closed. Using them after this will re-open the document, resetting it to blank.
To modify the DOM after this, you'll need to use innerHTML or document.createElement() and element.appendChild().
var line = document.createElement('div');
line.innerHTML = number;
document.body.appendChild(line);
To avoid infinite recursion and give the UI thread a chance to update the display of the page, you can use a timer when calling test again:
setTimeout(test, 100);

Related

Largest prime factor function works too slow

I have written the function which finds largest prime factor of some number. This function works but the problem is that it is too slow. e.g when I enter 600851475143 as a parameter, the process of finding largest prime factor lasts too long. How can I modify it so that it works faster?
Here is my code:
class test {
static addArray(someArray, member) {
for (var i = 0; i <= someArray.length; i++) {
if (i == someArray.length) {
someArray[i] = member;
return someArray;
}
}
}
static someLength(someArray) {
var i = 0;
while (someArray[i] !== undefined) {
var lastItem = i;
i++;
}
return i;
}
static testPrime(i) {
for (var k=2; k < i; k++) {
if (i % k == 0) {
return false;
}
}
return true;
}
}
var primeArray = [];
function largestPrime(n) {
for (var i=2; i < n; i++) {
//var k = n / i;
if (n % i == 0 && test.testPrime(i) == true) {
test.addArray(primeArray, i);
n == n / i;
}
}
return primeArray[test.someLength(primeArray) - 1];
}
document.write(largestPrime(600851475143));
Alright, before we go into that, let's get a little bit of theory sorted. The way you measure the time a particular piece of code takes to run is, mathematically, denoted by the O(n) notation (big-o notation) where n is the size of the input.
Your test prime function is of something called linear complexity meaning that it'll become linearly slow as the size of n (in this case, your number input) gets large.
For the number 15, the execution context is as follows:
15 % 2 == 0 (FALSE)
15 % 3 == 0 (TRUE)
...
15 % 14 == 0 (FALSE)
This means that for the number 100, there will be 98 (2 - 99) steps. And this will grow with time. Let's take your number into consideration: 600851475143. The program will execute 600851475143; the for-loop will get triggered 600,851,475,141 times.
Now, let's consider a clock cycle. Say each instruction takes 1 clock cycle, and a dumbed down version of your loop takes 2, the number 600851475143 will execute 1,201,702,950,286 times. Consider each clock cycle takes 0.0000000625 seconds (for a 16-MHz platform such as the Arduino), the time taken by that code alone is:
0.0000000625 * 1201702950286 = ~75,106 seconds
Or around 20 hours.
You see where I am going with this.
Your best best to get this program to work faster is to use a probabilistic test and confirm your findings using this number (or a BigInteger variant thereof).
Your approach is more linear, in the sense that the number of iterations for the for-loop to check for primality increases with an increasing number. You can plot the CPU cycle time along with the number and you'll realize that this is a rather inefficient way to do this.
I have discrete mathematics at my Uni, so just a word of warning - primality tests and their variants get really messy as you get into the utopia of faster and faster tests. It's a path filled with thorns of mathematics and you should have a seat belt while riding through the jungle! ;)
If you need more information on this, I would be glad to assist! I hope this helped! :)

JS - Prevent long loop javascript from "crash" browser implementation

I have a long loop that takes maybe 10 mins or more, and I want to set always a new time to avoid it to continue. But it dosen't works.
function problem3(){
var img = document.getElementById('p_3');
img.style.display = img.style.display === 'block' ? 'none' : 'block';
var number=600851475143;
var t = new Date();
for(var i=3;i*i<=number;i+=2){
if(isPrime(i) && number%i==0){
var maxPrime = i;
}
setInterval(function(){time(t)},5000);
}
document.getElementById("p3").innerHTML = 'Il più grande divisiore primo di <span>'+number+"</span> è <span>" + maxPrime+"</span>";
}
function time(t){
return console.log(Date() - t);
}
If I put console.log(Date() - t);in the problem3() function it works, but I can't do Date()-t every 5 seconds, something like setInterval(Date()-t,5000)
This is a case where you might consider using the workers API. Instead of freezing the browser, let the job be done in the background and call back to the main thread when it's done.
https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API
JavaScript is not multithreaded. So we think of setInterval() as running a piece of code every n ms (5000 in your example). But that's not quite true. If there's already script running when the interval elapses, the best that can happen is the bit of code gets added to a queue to be executed - but nothing from that queue is going to run until the already-running script finishes.
So in rough terms that's why it's not working, but what to do? Well, if you want anything to happen before problem3() returns, then problem3() is going to have to make it happen in a synchronous way.
For example, you could create a lastOutputTime variable, initialize it to the current time, and on each iteration through the for loop compare the current time to the stored value. If 5 seconds have passed, output to console and update lastOutputTime.
Your algorithm should be improved to something like this:
function maxPrimeFactor(number) {
if (number == 0 || !Number.isInteger(number) ||
number > Number.MAX_SAFE_INTEGER) return NaN;
number = Math.abs(number);
while(number % 2 == 0) number /= 2;
for (var i = 3; i * i <= number; i += 2) {
while(number % i == 0) number /= i;
}
return number;
}
var number = 600851475143;
console.log('maxPrimeFactor(' + number + ') == ' + maxPrimeFactor(number));
If for some numbers you need too much time, then break the loop into smaller chunks and asynchronize. But never use setInterval for this, and especially never use setInterval inside a long loop. setInterval schedules some task to run every n milliseconds, so if you use it in a loop, after i iterations, the task will run i every n milliseconds! And setInterval is so problematic because it can freeze the browser if the task takes more than n milliseconds. You should use setTimeout instead.
However, this would be useless in this case. The algorithm above can detect that 304250263527209 (15 digits) is a prime almost instantly. Given that the maximum safe integer is 9007199254740991 (16 digits), I don't think you will have problems for any number.
If you say the algorithm takes so long, it may be because you are trying it with bigger numbers. But be aware JS numbers are 64-bit floating point numbers, and thus integers can't be represented accurately above Number.MAX_SAFE_INTEGER. You will get a wrong result anyways, so do not even try to calculate that.
In the case of the Project Euler #551, a brute-force approach would be
function sumOfDigits(n) {
var sum = 0;
while(n != 0) {
sum += n % 10;
n = Math.floor(n/10);
}
return sum;
}
function sumDigitsSeq(n) {
return new Promise(function(resolve) {
var i = 1;
var chunkSize = 1e5;
var sum = 1;
(function chunk() {
chunkSize = Math.min(chunkSize, n-i);
for (var j=0; j<chunkSize; ++j, ++i) {
sum += sumOfDigits(sum);
}
if (i >= n) return resolve(sum);
console.log('Please wait. sumDigitsSeq(' + i + ') == ' + sum);
setTimeout(chunk, 60);
})();
});
}
var number = 1e6;
sumDigitsSeq(number).then(function(result) {
console.log('Done! sumDigitsSeq(' + number + ') == ' + result);
});
Of course brute-force is not the appropriate way to solve the problem.

Massive for loop taking too long to load

I'm working on a program designed to generate prime numbers, and I'm trying to find the 10001st prime number. Each time the loop runs, it tests variable i to see if it's divisible by variable j, which is part of another loop that goes up to half of i. If i is divisible by j then it adds one to variable prime, and when the loop's done it checks to see if variable prime is larger than two; if it is, then i is not prime. If it is prime, it gets push()ed to the variable array, which stores all the primes. Sorry if that's a little confusing.
My problem is that although this code works, whenever I set the first for loop too high (here I have it end at 100000), my browser freezes:
var prime = 0;
var array = [];
for(var i = 2; i <= 100000; i+=1) {
var prime = 0;
for(var j = 0; j <= i/2; j+=1) {
if(i % j === 0) {
prime += 1;
}
}
if(prime < 2) {
array.push(i);
}
}
console.log(array.length)
I know I could just copy and paste all the prime numbers but I want to make the program work. Any ideas to make the program stop freezing?
This is because the time complexity of your algorithm is pretty much O(n^2). Sieve of Eratosthenes created a better algorithm that will prevent your browser from freezing.
Here is one way of doing it:
var exceptions = {
2: 2,
3: 3,
5: 5,
7: 7
}
var primeSieve = function (start, end) {
var result = [];
for (start; start < end; start++) {
if (start in exceptions) result.push(start);
if (start > 1 && start % 2 !== 0 && start % 3 !== 0 && start % 5 !== 0 && start % 7 !== 0) {
result.push(start);
}
}
return result;
};
You can obviously make this look prettier, but I just did it quickly so you can get the point. I hope this helps! Let me know if you have further questions.

Checking for a Prime Number with JavaScript

I'm doing the 3rd project euler question right now. So far I've figured out how to solve the problem which is finding the largest prime factor of 600851475143.
I've wrote a small bit of code that can place all the prime factors of a number into an array. The problem I'm having is the number may be too large to compute. I've used other large numbers (not as large as this one) and they've worked fine but this one just freezes up the page like it's in an endless loop. Here's the code:
var primes = [];
function factor (largestNumber) {
var a = largestNumber;
var b = 2;
while (b < largestNumber) {
if (a % b == 0) {
a /= b;
primes.push(b);
b = 2;
} else {
b++;
}
}
}
factor(600851475143);
console.log(primes);
Your algorithm is not optimal.
function factor(largestNumber) {
var primes = []; // using local value
var a = largestNumber;
var b = 2;
while (b <= Math.floor(Math.sqrt(a))) { // we do not need to check whole number
// over and over again.
// We could check to a only
// or even to sqrt(a) only
if (a % b == 0) {
a /= b;
primes.push(b);
// there is no reason to reset value
} else {
b++;
}
}
primes.push(a); // rest number would be always prime
return primes;
}
console.log(factor(600851475143));
This may be a useful way, split the largest primary factor into 3 part.
Cut the largest Num into 2 close num eg 40 => 5 * 8
Get the bigger num(8) and find out all the prime num small than it. Storage into Array.
Use a loop to get the largest prime factor.
That's all, I will try it tonight. If I make it I will add the jsfiddle addr. : )

control random number with javascript

This is my intent,
Generate random number
Store in variable
Clear variable
Generate new number greater than previous
Store in variable
I understand
(Math.floor(Math.random()*100)+1)
For 1-100 but not sure how to accomplish what I want exactly.
The following will generate a random number and then find the next random number it finds that is greater than it (or equal to it if it is greater than or equal to 99):
var num = Math.floor(Math.random()*100)+1;
alert(num); //current number
var newNum;
while((newNum = Math.floor(Math.random()*100)+1) < num && newNum < 100);
alert(newNum); //new number > num (or == num if num >= 99)
use
var ran_val = 1;
// ... some code goes here
ran_val = (Math.floor(Math.random()*100) + ran_val)
if you have no upper limit on the random numbers,
ran_val = (Math.floor(Math.random()*(100-ran_val)) + ran_val)
otherwise.
fwiw, the random numbers you emulate this way are no longer uniformly distributed.
var numb1 = Math.floor(Math.random()*100)+1, //Generate random number
numb2 = 0;
while (numb2<numb1) {
numb2 = Math.floor(Math.random()*100)+2; // Generate new number greater than previous
}
FIDDLE
You need to have a global variable and a function that handles the random number generation.
You can do something like this:
var num = 1;
function generaterandom(){
num = Math.floor(Math.random()*100)+num;
}

Categories