Related
I need to create a slider for a game that you can set skills to each player,
The rules are :
Each skill starts at 0.
The skills cannot total more than 100 points at any time.
It should always be possible to assign any 0-100 value to a given skill. Given rule (2), if this gets us over 100 total points, the excess automatically, immediately, removed from the other skills, according to their current values.
It's not required to use all 100 points (or any).
A skill's value is always an integer.
For example :
We start with:
Stamina: 0 | Speed: 0 | Armor: 0 | Strength: 0 | Remaining: 100
The player adds 50 Speed.
Stamina: 0 | Speed: 50 | Armor: 0 | Strength: 0 | Remaining: 50
The player adds 25 Armor.
Stamina: 0 | Speed: 50 | Armor: 25 | Strength: 0 | Remaining: 25 - 115
The player now adds 40 Stamina. The excess is automatically reduced from the other skills, weighted by their current values.
Stamina: 40 | Speed: 40 | Armor: 20 | Strength: 0 | Remaining: 0
The player then reduces Speed to 10.
Stamina: 40 | Speed: 30 | Armor: 20 | Strength: 0 | Remaining: 10
Finally, the player sets Strength to 100.
Stamina: 0 | Speed: 0 | Armor: 0 | Strength: 100 | Remaining: 0
To do so i've created a function the receives 3 arguments :
An array of values of the slider
let arrToCalc = [14,24,55,0]
The index number of the skill (0 for Stamina, 1 for Speed ...etc)
let newValueIndex = 2
New value for base the calculation on
let newVal = 64.
Im not sure my calculations are accurate so i'm getting partial good results.
when set to
let arrToCalc = [0,50,25,0]
let newValueIndex = 0
let newVal = 40
the results are fine - [40,40,20,0]
but when i'm testing it with with other values - it's not always correct.
function calcSkills(currentValues, newValue, newValueIndex) {
let outArr = [];
for (let i = 0; i < currentValues.length; i++) {
if (i == newValueIndex) {
currentValues[i] = newValue;
}
}
let calcValues = currentValues.reduce((a, b) => {
return a + b;
})
if (calcValues < 100) {
outArr = currentValues
console.log('less', outArr)
} else {
let accumulator = 0;
let isValidVal = false;
for (let i = 0; i < currentValues.length; i++) {
let val = currentValues[i];
if (val && i !== newValueIndex) {
let temp = 0;
if (accumulator == 0) {
isValidVal = (100 - (newValue + val) >= 0 ? true : false);
if (isValidVal) {
temp = val - (100 - (newValue + val));
} else {
temp = 0;
}
accumulator = newValue
} else {
accumulator = accumulator + accumulator;
isValidVal = (100 - accumulator) > 0 ? true : false;
if (isValidVal) {
temp = val - (val - (100 - accumulator));
} else {
temp = 0;
}
}
outArr.push(temp)
} else {
outArr.push(val)
}
}
console.log('greather', outArr)
}
}
arrToCalc = [44, 55, 25, 0]
newValueIndex = 2
newVal = 40
calcSkills(arrToCalc, newVal, newValueIndex)
This approach uses 2 reduce() cycles, one to set the new values, and the next to shave or add the tiny bit left over. I have put in a half-dozen tests below, they all add up.
function calcSkills(curV, nV, nvI) {
console.log("---------------\nrunning calcSkills")
console.log("curV", curV.join(","));
console.log('newValue', nV, 'at index', nvI)
curV[nvI] = nV
let cValues = curV.reduce((a, b) => a + b)
if (cValues < 100) { outArr = curV }
else {
console.log(typeof curV)
let newOrder = [...curV]
newOrder.sort((a, b) => a - b)
let orderMap = newOrder.reduce((b, a) => {
let tmp = curV.indexOf(a);
let ct = 0;
while (b.indexOf(tmp, ct) !== -1) {
tmp = curV.indexOf(a, tmp + 1)
ct++
}
b.push(tmp);
return b
}, [])
let uvalueIndex = newOrder.indexOf(curV[nvI])
curV = newOrder, nvI = uvalueIndex, outArr = [], diff = cValues - 100, ttl = 0, nonz = 0;
let nvals = curV.reduce((b, a, i) => {
if (nvI !== i) a = a - Math.floor(a / 100 * diff); // if not the newly added number, find out our relative percentage and subtract it from the original number
b.push(a);
ttl += a;
if (a != 0 && nvI !== i) nonz++;
return b
}, []);
let overage = (ttl % 100),
ldiff = Math.ceil(overage / nonz) * (ttl > 100 ? -1 : 1); // ldiff determines how to spread the overage/underage so we get to 100
let numspots = Math.ceil(Math.abs(ldiff) / nonz)
nvals = nvals.reduce((b, a, i) => {
if (a !== 0 && nvI !== i && numspots > 0) {
let fval = a + ldiff
if (fval < 0) ldiff += Math.abs(fval)
a += ldiff;
overage += ldiff
nonz--;
if (nonz < 1) a -= overage
}
b.push(a);
return b;
}, [])
// reassemble
//console.log(nvals.join(","), orderMap.join(","))
orderMap.forEach((o, i) => outArr[o] = nvals[i])
}
console.log('Result:', outArr.join(","));
return outArr
}
/*-------- Testing ----------*/
arrToCalc = [0, 0, 67, 33]
nvI = 1
newVal = 75
calcSkills(arrToCalc, newVal, nvI)
arrToCalc = [3, 13, 44, 1]
nvI = 1
newVal = 74
calcSkills(arrToCalc, newVal, nvI)
arrToCalc = [1, 20, 61, 18]
nvI = 2
newVal = 76
calcSkills(arrToCalc, newVal, nvI)
arrToCalc = [0, 50, 25, 0]
nvI = 0
newVal = 40
calcSkills(arrToCalc, newVal, nvI)
/*
arrToCalc = [0, 60, 25, 0]
nvI = 3
newVal = 40
calcSkills(arrToCalc, newVal, nvI)
arrToCalc = [50, 40, 10, 0]
nvI = 3
newVal = 40
calcSkills(arrToCalc, newVal, nvI)
arrToCalc = [80, 10, 10, 0]
nvI = 3
newVal = 60
calcSkills(arrToCalc, newVal, nvI)
*/
After calculating the total score, reduce that from 100, and store it in the variable (here extra), then run a while loop utill that value becomes 0.
In the below snippet, I am running a loop and in each iteration reducing the value by 10. You can change the reduction logic as per the requirement.
function calcSkills(currentValues, newValue, newValueIndex){
for(let i =0; i < currentValues.length; i++){
if(i == newValueIndex){
currentValues[i] = newValue;
}
}
let calcValues = currentValues.reduce((a,b)=>{
return a +b;
})
if(calcValues < 100){
console.log('less', currentValues)
}
else {
let isValidVal = false;
let extra = calcValues - 100;
while(!isValidVal) {
for(let i=0; i<currentValues.length; i++) {
if (i != newValueIndex && currentValues[i] > 0) {
if (extra >= 10) {
currentValues[i] -= 10;
extra -= 10;
} else {
currentValues[i] -= extra;
extra = 0;
}
}
}
if (extra == 0) {
isValidVal = true;
}
}
console.log('greather', currentValues)
}
}
arrToCalc = [44, 55, 25, 0]
newValueIndex = 2
newVal = 40
calcSkills(arrToCalc, newVal, newValueIndex)
This is my attempt at the problem, I'm not sure a possible remainder is always going to land in the most desirable place, maybe that's something that could be tweaked.
const distribute_remainder_for_indexes = (arr, indexes, remainder) => {
const sorted_indexes = [...indexes].sort((a, b) => arr[b] - arr[a]);
return arr.map((v, i) => {
const ii = sorted_indexes.indexOf(i);
return v - ((ii > -1 && ii < remainder) ? 1 : 0);
});
};
const distribute_reduction_for_indexes = (arr, indexes, reduction) => {
const total = indexes.reduce((acc, v) => acc + arr[v], 0);
const reduced = arr.map((v, i) => v - (indexes.includes(i) ? Math.floor(v * reduction / total) : 0));
return distribute_remainder_for_indexes(
reduced, indexes,
indexes.reduce((acc, v) => acc + reduced[v], 0) - (total - reduction)
);
};
function calcSkills(current, new_value, new_index) {
const max_value = 100;
let copy = [...current];
copy[new_index] = Math.min(new_value, max_value);
const excess = copy.reduce((a, b) => a + b) - max_value;
return excess > 0 ?
distribute_reduction_for_indexes(
copy,
copy.reduce((a, v, i) => i !== new_index && v > 0 ? a.concat(i) : a, []),
excess
) :
copy;
}
console.log("[44, 55, 25, 0], 40, 2 ::", ...calcSkills([44, 55, 25, 0], 40, 2));
console.log("[0, 50, 25, 0], 40, 0 ::", ...calcSkills([0, 50, 25, 0], 40, 0));
console.log("[44, 13, 3, 1], 74, 1 ::", ...calcSkills([44, 13, 3, 1], 74, 1));
console.log("[80, 10, 10, 0], 60, 3 ::", ...calcSkills([80, 10, 10, 0], 60, 3));
console.log("[0, 9, 90, 1], 60, 0 ::", ...calcSkills([0, 9, 90, 1], 60, 0));
console.log("[0, 10, 50, 0], 20, 0 ::", ...calcSkills([0, 10, 50, 0], 20, 0));
console.log("[0, 10, 10, 8], 73, 0 ::", ...calcSkills([0, 10, 10, 8], 73, 0));
console.log("[0, 8, 10, 10], 73, 0 ::", ...calcSkills([0, 8, 10, 10], 73, 0));
const temperature = [3, -2, -6, -1, 'error', 9, 13, 17, 15, 14, 9, 5];
const calcTempAmplitude = function (temp) {
let max = temp[0];
let min = temp[0];
for (let i = 0; i < temp.length; i++) {
const currtemp = temp[i];
if (typeof currtemp !== 'number') continue;
if (currtemp > max) max = currtemp;
if (currtemp < max) min = currtemp;
}
console.log(max, min);
};
calcTempAmplitude(temperature);
The output should be: 17 -6 but instead, I am getting 17 5. what's wrong with my code?
The second if statement is wrong it should compare to min
if (currtemp < min) min = currtemp;
const temperature = [3, -2, -6, -1, 'error', 9, 13, 17, 15, 14, 9, 5];
const calcTempAmplitude = function (temp) {
let max = temp[0];
let min = temp[0];
for (let i = 0; i < temp.length; i++) {
const currtemp = temp[i];
if (typeof currtemp !== 'number') continue;
if (currtemp > max) max = currtemp;
if (currtemp < min) min = currtemp;
}
console.log(max, min);
};
calcTempAmplitude(temperature);
I'm trying to write a function that will identify the longest period of variance in an array of numbers. Variance begins when the previous number is higher than the current, and ends when the next number is the same as the current; however, if variance doesn't end, then it is assumed the variance began with the last two numbers.
For example: [10, 5, 3, 11, 8, 9, 9, 2, 10] The longest period of variance in this array is [5, 3, 11, 8, 9], or just 5 (the length). A variance ends when the following number is the same as the current, in this case, 9.
The function I've written works on this case; however, it doesn't when the entire array has variance, such as [10, 5, 10, 5, 10, 5, 10, 5, 10], which returns 8, when it should be 9.
In the case where the previous number is number is always lower, or always higher than the variance would be 2, because it never ended. For example [2, 4, 6, 8] and [8, 6, 4, 2].
I know the issue with the entire array variance can be solved by starting the for loop at 0, but then the other cases become invalid. Any help is greatly appreciated.
Without further ado, here is my code:
function findVariance(numbers) {
if ([0,1].includes(numbers.length)) return numbers.length;
const variance = [[0]];
let greater = numbers[1] > numbers[0];
let lesser = numbers[1] < numbers[0];
for (let i = 1; i < numbers.length; i++) {
let previous = variance.length - 1;
let previousVarianceGroup = variance[previous];
let previousVarianceGroupValue = previousVarianceGroup[previousVarianceGroup.length - 1];
if (greater) {
if (numbers[i] < numbers[previousVarianceGroupValue]) {
previousVarianceGroup.push(i);
greater = false;
lesser = true;
} else {
greater = numbers[i] < numbers[previousVarianceGroupValue];
lesser = numbers[i] < numbers[previousVarianceGroupValue];
variance.push([previousVarianceGroupValue, i]);
}
} else if (lesser) {
if (numbers[i] > numbers[previousVarianceGroupValue]) {
previousVarianceGroup.push(i);
greater = true;
lesser = false;
} else {
greater = numbers[i] > numbers[previousVarianceGroupValue];
lesser = numbers[i] > numbers[previousVarianceGroupValue];
variance.push([previousVarianceGroupValue, i]);
}
} else {
greater = numbers[i] > numbers[previousVarianceGroupValue];
lesser = numbers[i] < numbers[previousVarianceGroupValue];
variance.push([previousVarianceGroupValue, i]);
}
}
const result = [];
for (let i = 0; i < variance.length; i++) {
result[i] = variance[i].length;
}
result.sort();
return result[result.length - 1];
}
console.log(findVariance([10, 5, 3, 11, 8, 9, 9, 2, 10]));
console.log(findVariance([10, 5, 10, 5, 10, 5, 10, 5, 10]));
console.log(findVariance([2, 4, 6, 8]));
Here's what I got (trying to understand the question as best i could)
function calculateVariance(arr) {
// trivial cases
if (arr.length <= 1) { return arr.length; }
// store the difference between each pair of adjacent numbers
let diffs = [];
for (let i = 1; i < arr.length; i++) {
diffs.push(arr[i] - arr[i - 1]);
}
let max = 0;
// if the difference between two numbers is 0, they're the same.
// the base max variance encountered is 1, otherwise it's 2.
// the boolean zen here is that diffs[0] is falsy when it's 0, and truthy otherwise
let count = diffs[0] ? 2 : 1;
// go through the array of differences,
// and count how many in a row are alternating above/below zero.
for (i = 1; i < diffs.length; i++) {
if ((diffs[i] < 0 !== diffs[i - 1] < 0) && diffs[i] && diffs[i - 1]) {
count++;
} else {
max = Math.max(count, max);
// see above
count = diffs[i] ? 2 : 1;
}
}
// account for the maximum variance happening at the end
return Math.max(count, max);
}
You are overcomplicating things a bit, just increase a counter as long as an element equals the next one, reset on equal:
const counts = [];
let count = 0;
for(let i = 0; i < numbers.length - 1; i++) {
if(numbers[i] === numbers[i + 1]) {
counts.push(count);
count = 0;
} else {
count++;
}
}
counts.push(count);
return counts.sort()[counts.length - 1];
I am looking for an implementation in JavaScript for the following problem.
Consider a sorted array:
[1,2,5,9,10,12,20,21,22,23,24,26,27]
I would like to calculate the length of the maximum range that increased by 1, duplicates are not allowed.
The given example has the following ranges:
1,2
9,10
20,21,22,23,24 // the maximum range
26,27
So the return value for the given example should be 5.
I know how to solve this problem with the obvious solution, but I believe it is possible to solve the problem with more efficient and short algorithm.
A short solution
I don't think this is any more efficient than what pretty much everybody else has suggested, but the code is reasonably short and only loops over the array once, except for the first element. Not sure if it's any help:
var arr = [1, 2, 5, 9, 10, 12, 20, 21, 22, 23, 24, 26, 27];
var streak = 0, best = 0, bestStart;
for (var i = 1; i < arr.length; i++) {
if(arr[i]-arr[i-1] === 1) streak++;
else streak = 0;
if (streak > best) [best, bestStart] = [streak, i - streak];
}
var bestArr = arr.slice(bestStart, bestStart + best + 1);
console.log('Best streak: '+bestArr);
Speeding it up
After looking at the code, I realized that there is a way to speed it up slightly, by not checking the last few elements of the array, based on the previous value of best:
var arr = [1, 2, 5, 9, 10, 12, 20, 21, 22, 23, 24, 26, 27];
var streak = 0, best = 0, bestStart;
for (var i = 1; i < arr.length; i++) {
if(best > arr.length - i + streak) break;
if(arr[i]-arr[i-1] === 1) streak++;
else streak = 0;
if (streak > best) [best, bestStart] = [streak, i - streak];
}
var bestArr = arr.slice(bestStart, bestStart + best + 1);
console.log('Best streak: '+bestArr);
One possible solution would be to iterate the array, keeping the the current range as long as the numbers are successors. If the next number is not a successor of the previous number, close the current range and store its length - by comparing it to the length of the last range.
In this approach, the array is iterated only once and the maximum found length of a range is updated in constant time, yielding an O(n) algorithm where n is the number of elements in the input.
An implementation in C#-like pseudocode could be as follows.
int MaximumLength = minus infinity
int CurrentValue = Input[0];
int CurrentLength = 1;
for(int i = 1; i < Input.Length; i++)
{
if ( CurrentValue + 1 == Input[i] )
{
// same range
CurrentLength = CurrentLength + 1;
}
else
{
// new range
MaximumLength = Math.Max(MaximumLength, CurrentLength);
CurrentLength = 1;
}
CurrentValue = Input[i];
}
// check current length again after loop termination
MaximumLength = Math.Max(MaximumLength, CurrentLength);
It is impossible to obtain better than O(n) because the input cannot be read in less than O(n) time. If that would be possible, it would imply that there are instances for which the result does not depend on every element of the input, which is not the case for the given problem. The algorithm Philipp Maurer has sketched below would also yield an O(n) runtime bound if the maximum range length is 1, i.e. no adjacent numbers in the input are successors.
Something like this should find the maximum length first and not last.
Let max = 0
Let n = array length
While n > 2
Let m = 0
While m <= (array length - n)
Let first = m
Let last = m + n - 1
Let diff = (value of element 'last' in array) - (value of element 'first' in array)
if diff = n - 1 then
max = n
stop
end if
Increase m
end while
Decrease n
end while
Edit (javascript implementation)
var a = [1,2,5,9,10,12,20,21,22,23,24,26,27];
var max = 1;
var n = a.length;
while(n > 2) {
var m = 0;
while(m <= a.length - n)
{
var first = m;
var last = m + n - 1;
var diff = a[last] - a[first];
if (diff == n - 1 && diff > max) {
max = n;
break;
}
m++;
}
n--;
}
console.log(max);
JSFiddle
I think looping and comparing with stored previous maximum length is optimal solution. Maybe like this:
function findLongestRange(input) {
let maxLength = 0
let currentLength = 0
for (let i = 0; i < input.length; i++) {
if (i !== input.length) {
if (input[i] === input[i + 1] - 1) {
currentLength++
} else {
if (maxLength <= currentLength && currentLength !== 0) {
maxLength = currentLength + 1
}
currentLength = 0
}
}
}
return maxLength
}
const data = [1, 2, 5, 9, 10, 12, 20, 21, 22, 23, 24, 26, 27]
console.log(findLongestRange(data))
Here is the version with tests to check how it works with different input.
const data = [1, 2, 5, 9, 10, 12, 20, 21, 22, 23, 24, 26, 27]
function findLongestRange(input) {
let maxLength = 0
let currentLength = 0
for (let i = 0; i < input.length; i++) {
if (i !== input.length) {
if (input[i] === input[i + 1] - 1) {
currentLength++
} else {
if (maxLength <= currentLength && currentLength !== 0) {
maxLength = currentLength + 1
}
currentLength = 0
}
}
}
return maxLength
}
console.clear()
;[
[[1,2,5,6,7,1,2], 3],
[[], 0],
[data, 5],
[[1,2,3], 3],
[[1,3,4,6,8,1], 2],
[[1,3,5], 0],
].forEach((test, index) => {
const result = findLongestRange(test[0])
console.assert(result === test[1], `Fail #${index}: Exp: ${test[1]}, got ${result}`)
})
A Python answer:
l = [1,2,5,9,10,12,20,21,22,23,24,26,27]
current_range = None
current_range_val = 0
max_range = 0
max_range_val = 0
for i, j in zip(l, l[1:]):
if j - i == 1:
current_range_val += 1
if current_range is None:
current_range = (i, j)
current_range = (current_range[0], j)
else:
if current_range_val > max_range_val:
max_range = current_range
max_range_val = current_range_val
current_range_val = 0
current_range = (j, None)
print(max_range)
gives
(20, 24)
Sorry for my english, really really tired...
Imagine two arrays:
var a = [-10, 5, 0, 5, 10, 15];
var b = [-20, 0, 20, 40, 60, 80];
Every value of each array increments by the same number (a by 5 and b by 20).
I would like to find a way to put the zero on the same position on both arrays.
Keep in mind that:
each serie has the same number of elements
you can change the min and max value of each array (the original min and max of a serie have to be a part of the serie, check the example below)
each value of the array increments by the same number, you can change this value
The expected result could be something like
var a = [-10, 5, **0**, 5, 10, 15];
var b = [-60, -30, **0**, 30, 60, 90];
b increments now by 30 and the original min (-20) and max (8) values are included in the interval.
Any idea on how to do that using javascript?
Why I'd like to do that? To solve something like that:
http://peltiertech.com/Excel/Charts/AlignXon2Ys.html
Thanks in advance
Rob
The following is a result of code iteration based on comments. Previous code has been removed for clarity, but remains available in edit history.
This one fixes the zero in the middle of a series, then adjusts values according to the initial requirements. Also rounding to the nearest 5 (previous code was inadequate in that regard, sorry). HTH.
function develop(data) {
if (data.length < 3) {
return data;
}
var lower = data[0];
var upper = data[data.length - 1];
var index = (data.length - 1) / 2;
var numLeft = Math.floor(index);
var numRight = Math.ceil(index);
var leftStep = findStep(lower, numLeft, false);
var rightStep = findStep(upper, numRight, true);
var step = roundStep(Math.max(leftStep, rightStep), 5);
var result = [];
for (var ii = 0; ii < data.length; ii++) {
result[ii] = step * (ii - numLeft);
}
return result;
// ---
function findStep(boundary, numEntries, positive) {
if (positive && boundary <= 0 || !positive && boundary >= 0) {
return 1;
}
return Math.abs(Math.ceil(boundary / numEntries));
}
function roundStep(step, roundTo) {
if (step < roundTo) {
return step;
}
return Math.ceil(step / roundTo) * roundTo;
}
}
function test() {
var testData = [
[-10, -5, 0, 5, 10, 15],
[-20, 0, 20, 40, 60, 80],
[0, 72, 144, 216, 288, 360],
[-30, -25, -20, -15, -10, 0]
];
var results = [];
for (var ii = 0; ii < testData.length; ii++) {
var data = testData[ii];
results.push(JSON.stringify(data) + " => " + develop(data));
}
document.getElementById("results").innerHTML = results.join("<br>");
}
<input type="button" value="test()" onclick="test()" />
<div id="results"></div>
This seems to work, but I'm probably doing couple thins not necessary
var setIndex = function (arr1, arr2) {
var arr1Min = arr1[0];
var arr2Min = arr2[0];
var arr1Max = arr1[arr1.length-1];
var arr2Max = arr2[arr2.length-1];
var length = arr1.length;
var newRatio;
var newMin;
var newMax;
var ratioArr1 = arr1Max/arr1Min;
var ratioArr2 = arr2Max/arr2Min;
if(ratioArr1 < ratioArr2){
newMin = calcNewMin(arr1Min, arr1Max, ratioArr2);
newMax = ratioArr2 * newMin;
newRatio = (newMax - newMin)/(length-1);
arr1 = [];
for(var i = 0; i < length; i++){
arr1.push(newMin + (i * newRatio));
}
return [arr1, arr2];
} else {
newMin = calcNewMin(arr2Min, arr2Max, ratioArr1);
newMax = ratioArr1 * newMin;
newRatio = (newMax - newMin)/(length-1);
arr2 = [];
for(var i = 0; i < length; i++){
arr2.push(newMin + (i * newRatio));
}
return [arr1, arr2];
}
};
var calcNewMin = function(min, max, ratio){
var count = 1;
var newMin = min;
var newMax = max;
while(newMax <= max){
count++;
newMin = min - count;
newMax = newMin * ratio;
}
return newMin;
};
Compare the array ranges first, if one array includes the range of the other one then the answer is a series with an increment higher than the array that increments where the start of the range is:
0 - increment * (position of 0)
In your example:
var a = [-10, 5, 0, 5, 10, 15];
var b = [-20, 0, 20, 40, 60, 80];
b includes the range of a, so any series that have an increment higher than b and obeys the rules at the beginning are valid solutions to the problem:
[-20, 0, 20, 40, 60, 80]
[-21, 0, 21, 42, 63, 84]
[-22, 0, 22, 44, 66, 88]
...
[-60, 0, 60, 120, 180, 240]
All of these series include the range of a.
It gets a little bit trickier when the ranges overlap:
[-10, 0, 10, 20, 30]
[ 0, 20, 40, 60, 80]
The idea behind is the same. We'll pick the series with the smallest value:
[-10, 0, 10, 20, 30]
From this, we'll need to find a higher increment so that it satisfies:
start + (inc * length) > max of other series.
Where start:
0 - (inc * pos of 0 in picked series)
Moving stuff around you get:
inc > (max value / (length - pos of 0 in picked series))
So in this example:
inc > 80 / (5 - 2)
inc > 80 / 3
inc > 26.666
Lets try it with an increment of 27 and a start of -27:
[-27, 0, 27, 54, 81]
Now, that you we know how to solve the problem, lets try it with code:
function getMinMax(a, b){
var last = a.length - 1,
min = a[0] < b[0] ? a : b,
max = a[last] > b[last] ? a : b;
return { min : min, max : max };
}
function closestInc(range){
if(range.min === range.max){
return range.min[1] - range.min[0];
} else {
var last = range.min.length - 1,
maxValue = range.max[last],
posOfCero = range.min.indexOf(0);
return (maxValue/(range.min.length - posOfCero));
}
}
So, all the possible answers would be any series with an increment value bigger than closestInc(a, b) and a start of -closestInc(a,b) * posOfCero.
Here's a function that prints out all possible values slowly:
function createSeries(inc, posOfCero, count) {
var series = [],
start = -(inc * posOfCero);
for (var i = 0; i < count; i++) {
series.push(start + (inc * i));
}
return series;
}
var a = [-10, 5, 0, 5, 10, 15],
b = [-20, 0, 20, 40, 60, 80],
ranges = getMinMax(a, b),
inc = closestInc(ranges),
posOfCero = ranges.min.indexOf(0);
setTimeout(function printSeries(i) {
console.log(createSeries(inc + 1, posOfCero, range.min.length));
setTimeout(printSeries, 1000, i + 1);
}, 1000, 1);
A snippet below:
function getMinMax(a, b) {
var last = a.length - 1,
min = a[0] < b[0] ? a : b,
max = a[last] > b[last] ? a : b;
return {
min: min,
max: max
};
}
function closestInc(range) {
if (range.min === range.max) {
return range.min[1] - range.min[0];
} else {
var last = range.min.length - 1,
maxValue = range.max[last],
posOfCero = range.min.indexOf(0) + 1;
return (maxValue / (range.min.length - posOfCero));
}
}
function createSeries(inc, posOfCero, count) {
var series = [],
start = -(inc * posOfCero);
for (var i = 0; i < count; i++) {
series.push(start + (inc * i));
}
return series;
}
//var a = [-10, 5, 0, 5, 10, 15],
// b = [-20, 0, 20, 40, 60, 80],
var a = [-10, 0, 10, 20, 30],
b = [ 0, 20, 40, 60, 80],
ranges = getMinMax(a, b),
inc = closestInc(ranges),
posOfCero = ranges.min.indexOf(0);
setTimeout(function printSeries(i) {
console.log(createSeries(Math.round(inc + i), posOfCero, ranges.min.length));
setTimeout(printSeries, 1000, i + 1);
}, 1000, 1);
A last note, this aren't all possible series to match your rules (there might still be some valid increments between the series).
You can use Array.push and Array.unshift like this jsFiddle