I try since few days to create an javascript patern.
I parse an multidimentionnal array with "an for loop" that contain x and y positions as this :
array[1][x]
array[1][y]
array[2][x]
array[2][y]
ect...
i don't know the length the user push x and y numbers in an array and at the end an function is activated then length of the array is fixed. every seconds i want to increase or decrease x and y for one number at the end of the array.length i want to rebegin to parse the array, the parsing function stop when array[1][x] == stopX && array[1][y] == stopY.
i tested different approachs I have bug and infinite loops.
// i know stopX and stopY they are numbers and never change.
var stopX = 25;
var stopy = 49;
//i wish an condition to stop the script as this :
while( array[i][x] != stopX && array[i][y] != stopY ){
for( i = 1 ; i < array.length; i++ ){
(function(){
var j = i;
setTimeout( function(){
//here differents "if( ){" to increase or decrease array[i][x] and array[i][y];
array[i][x] += 1;
array[i][y] += 1;
}, j*1000);
}
}
}
Do i need "while number" as this "w*j*1000" i don't grasp this concept , i don't understand completely the utility of the closure i but that worked better, i think i need an second closure but where and how i can set up this?
In final i replacef "while loop" by setinterval+setTimeout+clearInterval.
Related
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).
As a practice project I made a Tic-Tac-Toe game on JSFiddle (because there aren't enough already, right?) and I progressed into adding an unbeatable AI. For the most part it works, but there are some combinations (e.g. setting X into fields 5, 9, 3 or into fields 3, 7, 9) that lead to the computer not calculating the optimal move properly.
The project on JSFiddle: https://jsfiddle.net/jd8x0vjz/
And the relevant function starting at line 63:
function evaluateMove(move, player, depth) {
var gameStatus = evaluateGameStatus(move); //get status of current board
if (gameStatus < 2 && player)
return -1; //if human won, return -1
if (gameStatus < 2 && !player)
return 1; //if human lost, return 1
var returnValue = 0 //value to be returned later
for (var z = 0; z < 3; z++) { //loop for row
for (var s = 0; s < 3; s++) { //loop for column
if (move[z][s]) //if current slot has an x or o,
continue; //skip it
var nextMove = cloneGameStatus(move); //create temporary array with base of current grid
nextMove[z][s] = !player ? "x" : "o"; //assign first free field the appropriate symbol
var value = evaluateMove(nextMove, !player, depth+1); //recursion but with switched player, to add the correct icon afterwards
if ((value > returnValue) && player)
returnValue = value;
if ((value < returnValue) && !player)
returnValue = value;
}
}
return returnValue; //return value of current simulation
}
I think the last two if-clauses are causing these problems, since the computer does calculate the proper values (as observable in the debugger), but they're sometimes overwritten, but I am not sure if this really is the root of the problem. Any help or tips would be appreciated!
EDIT: Problem solved! Look for my answer below in case it is not the first one.
I can't say for sure this is the source of the problem but there is most definitely a bug in your code that will produce strange results. The line:
var returnValue = 0 //value to be returned later
is incorrect. Aside from the fact that you are missing a semi-colon, the proper code should be:
var returnValue = -1;
if(!player){
returnValue = 1;
}
You want the default value for the maximum player to be negative so that he takes the best move, and for the minimizing player positive so he takes the worst move. The way you did it, if the maximizing player was faced only with options that valued -1, since -1 is less than 0, and returnValue was initialized to 0, 0 would be returned although the correct value to be returned is -1.
The idea of the default value of returnValue being wrong definitely sent me down the right path; it didn't make everything magically work (would've been too nice if it did), but it did give me the right nudge. Since we don't want to return any value if nothing is calculated, I adjusted the evaluateMove function as following:
function evaluateMove(move, player, depth) {
var gameStatus = evaluateGameStatus(move); //get status of current board
if (gameStatus != 2)
return gameStatus; //if the game is not running anymore, return result
var returnValue; //value to be returned later
for (var z = 0; z < 3; z++) { //loop for row
for (var s = 0; s < 3; s++) { //loop for column
if (move[z][s]) //if current slot has an x or o,
continue; //skip it
var nextMove = cloneGameStatus(move); //create temporary array with base of current grid
nextMove[z][s] = !player ? "x" : "o"; //assign first free field the appropriate symbol
var value = evaluateMove(nextMove, !player, depth+1); //recursion but with switched player, to add the correct icon afterwards
if ((value > returnValue || returnValue == null) && player)
returnValue = value;
if ((value < returnValue || returnValue == null) && !player)
returnValue = value;
}
}
return returnValue; //return value of current simulation
}
Now the default is null and as such should not throw the calculations off. What it did throw off however was the first block of checks, so I adjusted it to simply return the current status should the game have ended, instead of doing any elaborate checks. However that threw off the results because I'm using inversed default values in the two methods, so I had to adjust evaluateGameStatus too. Now if the human won it returns -1 instead of 1, and if the computer won it returns 1 instead of -1:
function evaluateGameStatus(gameStatus) { //a clusterfuck of winning combinations
if(
X Checks
)
return -1; //there's a successful combination of x's
else if(
O Checks
)
return 1; //there's a successful combination of o's
else {
for (var z = 0; z < 3; z++) {
for (var s = 0; s < 3; s++) {
if (!gameStatus[z][s])
return 2; //if there is an empty field neither has won, continue playing
}
}
return 0; //there's no successful combination and max moves have been reached. it's a draw
}
}
I had to do the same adjustmends for the checkGameEnd function, obviously.
You'll notice that I changed the check for draws too. That is because, for some reason, the old check for count == maxMoves did not work anymore, so I changed to a loop which simply checks whether there is any empty field at all, and to return 2 if there is, and 0 if there is not (it returns 0 here because at this point it has run through all the checks: X hasn't won, O hasn't won, and there are no open slots left, so the game must be a draw).
Working project can now be found here:
https://jsfiddle.net/h5zwzkm7/
I'm trying to increment a number until it reaches a value (say 100), then make it decrement until it reaches 0, and have all this run in a while loop.
The code seems correct, but the browser is freezing, so I can't check in the console if there's a flaw in the algorithm.
var ceiling = 100;
var floor = 1;
var x = 1;
var step = 1;
setInterval(function(){
while(1) {
while(x + step <= ceiling) {
x += step;
document.write(x + ' ');
}
while(x - step >= floor) {
x -= step;
document.write(x + ' ');
}
}
}, 1500);
I've used setInterval here to slow down execution and avoid some buffering issue with the browser. It shouldn't be a problem since I have 16GB of RAM, but it might depend on how the browser can use/access that too in one tab.
I've also tried wrapping the setInterval function inside another function and execute it, with the same result.
You have a while(1) loop which basically is a infinite loop, even if no code is executed, it will freeze the browser.
The other answers have already pointed out the problems with your endless loop and with using document.write.
I think this does what you want:
var ceiling = 100;
var floor = 1;
var x = 1;
var step = 1;
setInterval(function() {
console.log(x);
x += step;
if (x === ceiling || x === floor) {
step = -step;
}
}, 1500);
setInterval is essentially your while(1) loop.
document.write is only intended to be used on the first rendering of the document. since you are doing an interval, then the doc is already rendered, and you can't use document.write. instead, you need to append your text to the body or a div
I'm writing a program in JS and im feeling i'm repeating code, which is not good. I'm trying to avoid an if then else block that has two similar for loop and re-write it without an if then else using just one for loop.
Consider this: minimum has value 0. maximum has value 10. if new_value is less than old_value i wanna execute a for loop from minimum to new_value, else i wanna execute it from maximum DOWNto new_value
Lets see it in action, lets say javascript (language-agnostic answers are welcome and upvoted -but will not grant you an extra cookie)
var minimum = 0;
var maximum = 10;
var old_value = 5;
/* var new_value = taken from user input whatever ... */
if(new_value<old_value)
{
for(i=minimum;i<new_value;i++)
{
// whatever
}
}
else
{
for(i=maximum;i>new_value;i--)
{
// whatever
}
}
I have a feeling these two for loops are similar enough to be written as one in a mathematical approach maybe. Have tried a bit using absolute values Math.abs() Math.max.apply() but had no luck.
I don't want to set other helping variables using if then else to give appropriate values.
So, whats the question: I'm wondering if this can be rewritten in one for ... loop without being nested in an if then else.
A complete solution using built-in js functions will grant you an extra cookie.
Edit: Didn't see your original thing about not using the if/else with variables. Why not do something like this then? Just go from 0 to 10, using that value or 10 minus that value depending on the conditional.
for(var j = 0; j <= 10; j++) {
var i = new_value < old_value ? j : 10 - j;
// whatever
}
Assign your min, max and increment as variables, define them based on your if condition and then use them in the for loop:
var old_value = 5, start, end, inc;
if(new_value<old_value) {
start = 0;
end = 10;
inc = 1;
} else {
start = 10;
end = 0;
inc = -1;
}
for( i = start;i >= start && i <= end; i += inc) {
// whatever
}
You could abuse of the ternary operator just for fun:
var minimum = 0;
var maximum = 10;
var old_value = 5;
var new_value = 7;
/* var new_value = taken from user input whatever ... */
var check =(new_value<old_value);
var foo1 = function () { console.log("foo1") }
var foo2 = function () { console.log("foo2") }
for(i=check?minimum:maximum;
check?(i<new_value):(i>new_value);
check?i++:i--)
{
check?foo1():foo2();
}
Does the second loop have to iterate in reverse? If not you can simply use
var i0 = new_value<old_value ? minimum : new_value+1;
var i1 = new_value<old_value ? new_value : maximum+1;
for(i=i0;i<i1;++i)
{
//whatever
}
Edit: In the light of your comment, if you can be sure that you're dealing with integers you can use
var i0 = new_value<old_value ? minimum : maximum;
var d = new_value<old_value ? 1 : -1;
for(i=i0;i!=new_value;i+=d)
{
//whatever
}
If not
var i0 = new_value<old_value ? minimum : maximum;
var d = new_value<old_value ? 1 : -1;
for(i=i0;d*i<d*new_value;i+=d)
{
//whatever
}
I did it!
With this +(old_value>new_value) instead of getting true/false i am getting 1/0. I am using the 1 and 0 as multipliers to emulate the if then else functionality.
lets assume
var minimum = 0;
var maximum = 10;
var old_value = 5;
for(expression1;expression2;expression3)
For expression1:
if new value is bigger than old value then we need minimum
if new value is smaller than old value then we need maximum
I am multiplying be zero the maximum depending the above conditions with (maximum*(+(old_value>new_value)))
I am multiplying by zero the minimum depending the above conditions with (minimum*(+(old_value<new_value))
by adding these two the sum is what i am supposed to get! (maximum*(+(old_value>new_value)))+(minimum*(+(old_value<new_value)))
This will give minimum if new_value > old value and maximum if new_value < old_value
For expression2:
while i!=new_value; simple. (we just have to be sure the maximum is bigger than new_value and minimum is smaller than new_value or we have an endless loop.)
For expression3:
if new value is bigger than old value then we need i=i +1
if new value is smaller than old value then we need i=i -1
this
(+(old_value<new_value)+1)+(-1*(+(old_value>new_value)+1))
will give either 2+-1=1 or 1+(-2)=-1 so we simply use it in expression3 as
i=i+(+(old_value<new_value)+1)+(-1*(+(old_value>new_value)+1))
complete code:
http://jsfiddle.net/eBLat/
var minimum = 0;
var maximum = 10;
var old_value = 5;
var new_value = 3; // change this value
// if new value is bigger than old value then for loop from maximum downto new_value (dont include new value)
// if new value is smaller than old value then for loop from minimum upto new_value (dont include new value)
for(i=(maximum*(+(old_value>new_value)))+(minimum*(+(old_value<new_value)));i!=new_value;i=i+(+(old_value<new_value)+1)+(-1*(+(old_value>new_value)+1)) )
{
alert("Iteration:"+i);
}
Another question would be if this is actually better than just write two for in a if then else ... anyway i had fun. And i got the cookie :D :D
Hope someone will find useful in some way the fact that +true gives 1 and +false gives 0 in javascript
I've got the following code inside a <script> tag on a webpage with nothing else on it. I'm afraid I do not presently have it online. As you can see, it adds up all primes under two million, in two different ways, and calculates how long it took on average. The variable howOften is used to do this a number of times so you can average it out. What puzzles me is, for howOften == 1, method 2 is faster, but for howOften == 10, method 1 is. The difference is significant and holds even if you hit F5 a couple of times.
My question is simply: how come?
(This post has been edited to incorporate alf's suggestion. But that made no difference! I'm very much puzzled now.)
(Edited again: with howOften at or over 1000, the times seem stable. Alf's answer seems correct.)
function methodOne(maxN) {
var sum, primes_inv, i, j;
sum = 0;
primes_inv = [];
for ( var i = 2; i < maxN; ++i ) {
if ( primes_inv[i] == undefined ) {
sum += i;
for ( var j = i; j < maxN; j += i ) {
primes_inv[j] = true;
}
}
}
return sum;
}
function methodTwo(maxN) {
var i, j, p, sum, ps, n;
n = ((maxN - 2) / 2);
sum = n * (n + 2);
ps = [];
for(i = 1; i <= n; i++) {
for(j = i; j <= n; j++) {
p = i + j + 2 * i * j;
if(p <= n) {
if(ps[p] == undefined) {
sum -= p * 2 + 1;
ps[p] = true;
}
}
else {
break;
}
}
}
return sum + 2;
}
// ---------- parameters
var howOften = 10;
var maxN = 10000;
console.log('iterations: ', howOften);
console.log('maxN: ', maxN);
// ---------- dry runs for warm-up
for( i = 0; i < 1000; i++ ) {
sum = methodOne(maxN);
sum = methodTwo(maxN);
}
// ---------- method one
var start = (new Date).getTime();
for( i = 0; i < howOften; i++ )
sum = methodOne(maxN);
var stop = (new Date).getTime();
console.log('methodOne: ', (stop - start) / howOften);
// ---------- method two
for( i = 0; i < howOften; i++ )
sum = methodTwo(maxN);
var stop2 = (new Date).getTime();
console.log('methodTwo: ', (stop2 - stop) / howOften);
Well, JS runtime is an optimized JIT compiler. Which means that for a while, your code is interpreted (tint), after that, it gets compiled (tjit), and finally you run a compiled code (trun).
Now what you calculate is most probably (tint+tjit+trun)/N. Given that the only part depending almost-linearly on N is trun, this comparison soes not make much sense, unfortunately.
So the answer is, I don't know. To have a proper answer,
Extract the code you are trying to profile into functions
Run warm-up cycles on these functions, and do not use timing from the warm-up cycles
Run much more than 1..10 times, both for warm-up and measurement
Try swapping the order in which you measure time for algorithms
Get into JS interpretator internals if you can and make sure you understand what happens: do you really measure what you think you do? Is JIT run during the warm-up cycles and not while you measure? Etc., etc.
Update: note also that for 1 cycle, you get run time less than the resolution of the system timer, which means the mistake is probably bigger than the actual values you compare.
methodTwo simply requires that the processor execute fewer calculations. In methodOne your initial for loop is executing maxN times. In methodTwo your initial for loop is executing (maxN -2)/2 times. So in the second method the processor is doing less than half the number of calculations that the first method is doing. This is compounded by the fact that each method contains a nested for loop. So big-O of methodOne is maxN^2. Whereas big-O of methodTwo is ((maxN -2)/2)^2.