Javascript loop with dynamic start and end variables - javascript

This seems pretty basic, but I can't find the best method to do this... I'm trying to set up a function that loops between a user selected start and end variables. This is what I ended up with but I'm sure there is a better way to do it (demo).
Note: the x & y variables are indexed to one, not zero.
getWidths1 = function(x, y) {
var start = (x < y) ? x : y,
end = (x < y) ? y : x,
total = 0;
for (; start < end; start++) {
total += values[start - 1] || 0;
}
return total;
};
I tried this function, but the results are one result off when y > x:
getWidths2 = function(x, y) {
var total = 0,
diff = (x < y) ? 1 : -1;
while (x !== y) {
total += values[x - 1] || 0;
x += diff;
}
return w;
};
So, is the first function the best, or does someone have a better method?

The first isn't bad. I think this is slightly more traditional:
for (var i = start; i < end; i++){
}
Only real difference is that it doesn't affect start and end.

I'd make a few changes:
Use Math.min and Math.max - much more readable.
Don't subtract one from start if the first value you want is values[start].
var getWidths1 = function(x, y) {
var start = Math.min(x,y), end = Math.max(x,y);
var total = 0;
for (; start < end; start++) {
total += values[start] || 0;
}
return(total);
}

I agree with #kingjiv with the added caveat that if you want to include the item at y then you need:
for (var i = start; i <= end; i++){
...
}
As it is your code (both versions) will total the values from x inclusive to y exclusive.

Related

How to retrieve the max value of a function output

I have a function f(x). I want to calculate x where f(x) = max. How can I do that in a script?
Given: x is a positive number, f(x) can be positive or negative.
I want to start with x = 1.
If f(1) is already negative I'm not interested anymore.
With increasing x, also f(x) returns increasing values, until a peak is reached, and then f(x) decreases. x at the peak is what I am interested in.
EDIT: Ok what I tried so far:
I tried starting with x = 1, then x *=2. If f(x) is smaller then the last result, I set back x to x/4.
Example: f(16) = 9, f(32) = 11, f(64) = 10. The peak can be between x=16 and x=64. So my new starting point is f(16). From there on, I want to continue in a similar way, but I cant find an algorithm.
Here is what I got so far:
let x = 1
let lasty = 0
let holder = 1
while (iteration++ < 20) {
let y = myFunction(x)
if(y < lasty ) {
x = x / 4
holder = 1
}
x += holder * 2
holder += 1
lasty = y
}
EDIT2: My improved version which works quite good but for sure not perfect:
let x = 1
let lasty = 0
let iteration = 0
let previousX = 1
let lastX = 1
let step = 2
let maxY = -Infinity
let bestX = 0
let randomInput = Math.random
while (iteration++ < 20) {
let y = myFunction(x, randomInput) //added randomInput to show the function does not rely on x alone
if (y < 0 && iteration == 1) break
if(y > maxY) {
maxY = y
bestX = x
}
if (y < lasty) {
x = previousX
step *= 0.8
lasty = 0
} else {
previousX = lastX
lastX = x
x = x * step
lasty = y
}
}
if(bestX > 0) {
console.log(`Got best x: ${bestX}`)
}
EDIT3: Added random additional parameter to emphasise the needed approach
EDIT4: I should also mention that the probability of f(x) = max is the highest when 0 < x < 100000
Assuming you want to maximise y, You can just store the x and y values where xy is highest:
let highestX = 1; // this is just the corresponding x value for the highest y
let highestY = -Infinity;
let x = 1
let lasty = 0
let holder = 1
while (iteration++ < 20) {
let y = myFunction(x);
// code for storing the highest y and corresponding x
if (y > highestY) {
highestY = y;
highestX = x;
}
if(y < lasty ) {
x = x / 4
holder = 1
}
x += holder * 2
holder += 1
lasty = y
}
console.log('f(x) is maximised when x =', highestX);
If you know the function has a single peak in a given range, and the closest value within a tolerance is acceptable, then a simple binary search should be all you need. Here we default the range to the entire safe integer range, but you can supply your own with findPeak (tolerance) (fn, rangeMin, rangeMax), perhaps something like findPeak (1e-8) (fn, 0, 100000).
const findPeak = (ε) => (
fn,
min = Number.MIN_SAFE_INTEGER,
max = Number.MAX_SAFE_INTEGER,
mid = min / 2 + max / 2
) =>
max - min < ε
? mid
: fn (min) < fn (max)
? findPeak (ε) (fn, mid, max)
: findPeak (ε) (fn, min, mid)
const fn = n => 600 + 50 * n - n ** 2
console .log (findPeak (1e-8) (fn)) //=> 24.99999991050572
This code is simple. It is less efficient than it might be if we took advantage of the fact that we will reuse fn (min) or fn (max) on each subsequent call, and probably would be faster with a while loop rather than the recursion. I'll leave such optimization to you. But I would first see if this is already fast enough, as simple code is much easier to work with.
It could be solved using recursive function calls.
// array of arguments
const args = [...Array(16).keys()];
// sample function
const sampleFunc = (x) => Math.sin(x);
// recursive function
function getElementWithFuncMaximum(arr, func, index = 0, currValue = 0) {
const currFuncValue = func(arr[index]);
return currFuncValue >= currValue
? getElementWithFuncMaximum(arr, func, index + 1, currFuncValue)
: arr[index - 1];
}
console.log(getElementWithFuncMaximum(args, sampleFunc));

averagePair problem using multiple pointers as a solution

I'm trying to solve the following problem :
What I've come up with so far:
function averagePair(arr,tar){
if (arr.length < 2){
return false
}
let x = 0
for (var y = 1; y < arr.length; y++){
if ((arr[x] + arr[y]) / 2 == tar){
return true
}
else {
x++;
}
}
return false
}
I know this solution isn't correct, can someone explain why? It works for some cases but not all
There's a solution with O(1) additional space complexity and O(n) time complexity.
Since an array is sorted, it makes sense to have two indices: one going from begin to end (say y), another from end to begin of an array (say x).
Here's the code:
function averagePair(arr,tar){
// That's now included in for-loop condition
// if (arr.length < 2) {
// return false;
// }
let x = arr.length - 1;
for (var y = 0; y < x; y++) {
// Division may lose precision, so it's better to compare
// arr[x] + arr[y] > 2*tar
// than
// (arr[x] + arr[y]) / 2 > tar
while (y < x && arr[x] + arr[y] > 2*tar) {
x--;
}
if (x != y && arr[x] + arr[y] == 2*tar) {
return true;
}
}
return false;
}
It's kinda two-pointers technique: we'll decrease x until a[x] + a[y] > 2*tar for current loop iteration because we need to find the closest match. At the next for-loop iteration a[y] is greater or equal than the previous one, so it makes no sense to check if a[z] + a[y] == 2*tar for any z > x. We'll do this until indices aren't equal, which means there's no match.
You're only comparing adjacent elements, eg [0] vs [1], and [1] vs [2]. You also need to compare [0] vs [2] and so on. The simplest tweak would be to use a nested loop:
for (let x = 0; x < arr.length; x++) {
for (let y = 0; y < arr.length; y++) {
if (x !== y) {
// test arr[x] against arr[y]
But it'd be more elegant and less computationally complex (O(n) instead of O(n ^ 2)) to use a Set to keep track of what's been found so far:
const nums = new Set();
for (const num of arr) {
if (nums.has(tar - num)) {
return true;
} else {
nums.add(num);
}
}
function averagePair(arr,tar){
const nums = new Set();
for (const num of arr) {
if (nums.has(tar - num)) {
return true;
} else {
nums.add(num);
}
}
return false;
}
console.log(averagePair([-2, 3, 2], 0));
console.log(averagePair([-2, 3, 3], 0));

When using x as index no. to show array location in a funtion, the array returns undefined

Hi guys so my function is returning undefined when I am using x to represent the index number of an array in the below code, why is this happening?
It would be very much appreciated if somebody could answer my question
Code:
var a = 0;
var b = 0;
var z = 0;
var x = 0;
function findOutlier(integers){
//your code here
while(x < integers.length){
if(integers[x]%2 === 0){
a = x;
z = z + 1;
}
else{
b = x;
}
x = x + 1
}
if(z === 1){return integers[a]}
else{return integers[b]}
}
findOutlier([2,6,8,10,3]);
x <= integers.length The array goes from 0 to length-1, so looping from 0 to length means you are going one off the end of the array.
Replace with while(x < integers.length).

Why is my subfactorial function off by one?

I'm working on implementing a subfactorial function in JavaScript to calculate the total number of derangements possible for n elements, and I seem to have screwed something up. My calculation always seems to be one high, or one low. What did I screw up? Is it a rounding error?
function subfactorial (x) {
x = parseInt(x);
var i;
var sub = 0;
var sum = 0;
sum += factorial(x);
for (i = 0; i < x; i++) {
sub += (Math.pow(-1, i)/factorial(i));
}
return sum * sub;
}
function factorial (y) {
var negative = y < 0;
y = parseInt(Math.abs(y)); // Ints only
var acc = 1;
for (y; y > 0; y--) {
acc *= y;
}
return negative ? -acc : acc;
}
function getSubfactorial () {
var val = document.getElementById('subfac').value;
document.getElementById('result').innerHTML = subfactorial(val);
}
<label for="subfac">Subfactorial input:</label>
<input type="number" id="subfac">
<button type="button" onClick="getSubfactorial()">Get Subfactorial</button>
<div id="result"></div>
For example, subfactorial(3) returns 3, when the answer should be 2. subfactorial(4) returns 8, when the answer should be 9. subfactorial(5) returns 45 (with a floating point rounding error) when the answer should be 44, and so on, and so forth. It seems to alternate being too low and too high between even and odd numbers respectively.
The formula I'm using comes out to be this:
In TeX:
!x = x! \sum_{k=0}^{x}\frac {(-1)^k}{k!}
Rendered TeX:
You're going to laugh:
for (i = 0; i < x; i++) {
That not what the Sum symbol means. It should be
for (i = 0; i <= x; i++) {
Also, that is the most literal-minded way to implement subfactorial imaginable. The exponentiation is just a way to represent "oscillate between positive and negative one" -- but in Javascript, there are about 10 better ways to do that. And there is no reason to use (or worry about) floating-point. Instead of calculating 1/k! and then multiplying by x!, calculate x!/k!, which can be done as
var factDiv = function(x, k) {
return (k >= x) ? 1 : (x * factDiv(x-1,k));
}
And then subfactorial() can be defined as
var subfactorial = x => {
var p = 1;
var sum = 0;
for (var k=0; k <= x; k++) {
sum += p * factDiv(x, k);
p *= -1;
}
return sum;
}
The sum goes from x=0 to x included.
Change the exit condition of the for loop
function subfactorial (x) {
x = parseInt(x);
var i;
var sub = 0;
var sum = 0;
sum += factorial(x);
for (i = 0; i <= x; i++) {
sub += (Math.pow(-1, i)/factorial(i));
}
return sum * sub;
}
function factorial (y) {
var negative = y < 0;
y = parseInt(Math.abs(y)); // Ints only
var acc = 1;
for (y; y > 0; y--) {
acc *= y;
}
return negative ? -acc : acc;
}
function getSubfactorial () {
var val = document.getElementById('subfac').value;
document.getElementById('result').innerHTML = subfactorial(val);
}
<label for="subfac">Subfactorial input:</label>
<input type="number" id="subfac">
<button type="button" onClick="getSubfactorial()">Get Subfactorial</button>
<div id="result"></div>
This seems to fix it.
for (i = 0; i <= x; i++) {
sub += (Math.pow(-1, i)/factorial(i));
}
Change the loop to i <= x.
Still some rounding issues though. Probably a javascript thing. Looks like it gets the right numbers now though.

Can a for loop increment/decrement by more than one?

Are there other ways to increment a for loop in Javascript besides i++ and ++i? For example, I want to increment by 3 instead of one.
for (var i = 0; i < myVar.length; i+3) {
//every three
}
Use the += assignment operator:
for (var i = 0; i < myVar.length; i += 3) {
Technically, you can place any expression you'd like in the final expression of the for loop, but it is typically used to update the counter variable.
For more information about each step of the for loop, check out the MDN article.
A for loop:
for(INIT; TEST; ADVANCE) {
BODY
}
Means the following:
INIT;
while (true) {
if (!TEST)
break;
BODY;
ADVANCE;
}
You can write almost any expression for INIT, TEST, ADVANCE, and BODY.
Do note that the ++ operators and variants are operators with side-effects (one should try to avoid them if you are not using them like i+=1 and the like):
++i means i+=1; return i
i++ means oldI=i; i+=1; return oldI
Example:
> i=0
> [i++, i, ++i, i, i--, i, --i, i]
[0, 1, 2, 2, 2, 1, 0, 0]
for (var i = 0; i < 10; i = i + 2) {
// code here
}​
Andrew Whitaker's answer is true, but you can use any expression for any part.
Just remember the second (middle) expression should evaluate so it can be compared to a boolean true or false.
When I use a for loop, I think of it as
for (var i = 0; i < 10; ++i) {
/* expression */
}
as being
var i = 0;
while( i < 10 ) {
/* expression */
++i;
}
for (var i = 0; i < myVar.length; i+=3) {
//every three
}
additional
Operator Example Same As
++ X ++ x = x + 1
-- X -- x = x - 1
+= x += y x = x + y
-= x -= y x = x - y
*= x *= y x = x * y
/= x /= y x = x / y
%= x %= y x = x % y
You certainly can. Others have pointed out correctly that you need to do i += 3. You can't do what you have posted because all you are doing here is adding i + 3 but never assigning the result back to i. i++ is just a shorthand for i = i + 1, similarly i +=3 is a shorthand for i = i + 3.
For those who are looking to increment pair of numbers (like 1-2 to 3-4):
Solution one:
//initial values
var n_left = 1;
var n_right = 2;
for (i = 1; i <= 5; i++) {
console.log(n_left + "-" + n_right);
n_left =+ n_left+2;
n_right =+ n_right+2;
}
//result: 1-2 3-4 5-6 7-8 9-10
Solution two:
for (x = 0; x <= 9; x+=2) {
console.log((x+1) + "-" + (x+2));
}
//result: 1-2 3-4 5-6 7-8 9-10
The last part of the ternary operator allows you to specify the increment step size. For instance, i++ means increment by 1. i+=2 is same as i=i+2,... etc.
Example:
let val= [];
for (let i = 0; i < 9; i+=2) {
val = val + i+",";
}
console.log(val);
Expected results: "2,4,6,8"
'i' can be any floating point or whole number depending on the desired step size.
There is an operator just for this. For example, if I wanted to change a variable i by 3 then:
var someValue = 9;
var Increment = 3;
for(var i=0;i<someValue;i+=Increment){
//do whatever
}
to decrease, you use -=
var someValue = 3;
var Increment = 3;
for(var i=9;i>someValue;i+=Increment){
//do whatever
}

Categories

Resources