Dispersing numbers in a javascript array - javascript

I have an array of 10+ numbers. They represent coordinates on a circle - in degrees, i.e. each number is in between 0 and 359.999999...
The problem I am trying to solve is that when I draw my items on the circle (via html5 canvas api), sometimes they are clustered together and that results in items being drawn onto each other.
So I would like to create an algorithm which disperses items evenly around their initial cluster position. Let's say (and I'd like this to be a configurable option) the minimal distance between two items is 5 degrees.
So if the initial array is [5, 41, 97, 101, 103, 158, 201, 214, 216, 217, 320] then I would like the algorithm come up with something like [5, 41, 95, 100, 105, 158, 201, 211, 216, 221, 320]
(with bolded items being dispersed around their initial "gravity center" regardless whether those are 2 or more items).
Also what would be neccessary is that the algorithm recognizes 0 and 359 being just 1 unit (degree) apart and also spread such items evenly around.
Has anyone ever created such algorithm or have a good idea how it could be achieved? Even some general thoughts are welcome.
I'm sure I could achieve that with plenty of trial and error, but I'd like to hear some educated guesses, if you will, first.

var val = [5, 41, 96, 101, 103, 158, 201, 214, 216, 217, 320, 1201, 1213, 1214, 1216, 1217, 1320],
delta = Array.apply(null, { length: val.length }).map(function () { return 0 }),
result,
threshold = 5,
converged = false;
document.write('val: ' + val + '<br>');
while (!converged) {
converged = true;
delta = delta.map(function (d, i) {
if (i < delta.length - 1 && delta.length > 1) {
if (val[i + 1] + delta[i + 1] - val[i] - d < threshold) {
converged = false;
delta[i + 1] += 1;
return d - 1;
}
}
return d;
});
document.write('delta: ' + delta + '<br>');
}
result = val.map(function (v, i) {
return v + delta[i];
});
document.write('result: ' + result + '<br>');
// try to minimise difference
converged = false;
while (!converged) {
converged = true;
delta = delta.map(function (d, i) {
if (i < delta.length - 2) {
var space = val[i + 1] + delta[i + 1] - val[i] - d;
if (d < 0 && space > threshold) {
converged = false;
return d + space - threshold;
}
}
return d;
});
document.write('delta min: ' + delta + '<br>');
}
result = val.map(function (v, i) {
return v + delta[i];
});
document.write('result: ' + result + '<br>');
the code pushes two too close couples appart with one on each side. this is symetrically and results in sometimes to far pushed values, which can be corrected.
[not implemented!]
if the space of your values is not sufficient, [0..360[ or by more then 72 elements with a difference of 5 the the while loop may not come to an end.
edit: the minimise block should iterate until all values are correted.

To get that "evenly random" distribution, say you have N numbers - split the circle into N segments, then randomly place each number into its segment.
That way you won't even need to care about 0 and 359 being only 1 unit apart.
Here's an idea:
var numbers = 5;
var segment = 360/numbers;
var result = [];
for(var i = 0; i < numbers; i++) {
result.push(Math.round((Math.random() + i) * segment));
}
alert(result.join(','));
Here's a more graphical idea (imagine folding the rectangle into a cylinder):
var numbers = 5;
var segment = 360 / numbers;
var minimum = 15;
var div = document.getElementById('r');
function build() {
var result = [];
div.innerHTML = '';
for (var i = 0; i < numbers; i++) {
var current = 0;
while(current < minimum + (result[result.length - 1] || 0)) {
current = Math.round((Math.random() + i) * segment);
}
result.push(current);
var d = document.createElement('div');
d.className = 'segment';
d.style.left = (result[result.length - 2] || 0) + 'px';
d.style.width = (current - (result[result.length - 2] || 0) - 1) + 'px';
d.textContent = current;
div.appendChild(d);
}
console.log(result.join(','));
}
build();
.segment {
height: 100%;
position: absolute;
font-size: 12px;
text-align: right;
border-right: 1px solid black;
}
#r {
height: 100%;
width: 360px;
background: #eee;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
button {
position: absolute;
right: 4px;
margin: 0;
}
<button onclick="build()">Rebuild</button>
<div id="r"></div>

Algorithms that pretty-print graphs use a system of springs to move vertices away from each other when they overlap. Here, you are dealing with just one dimension and may get away with iteratively adjusting nearby angles until all nodes are at least 5 degrees apart.
You can deal with the cyclic values by creating an auxiliary working array, such that the elements are reordered after the largest gap. This allows you to treat the array as linear values without having to care about wrapping:
[2, 7, 320, 359] -> [-40, -1, 2, 7]
The code below does this. The nodes are moved in a rather crude fashion, though: The code only looks at pairs of nodes that are too close. The code can probably be improved by loking at clusters of two or more nodes that are too close to each other:
function adjust(arr, dist)
{
var offset = 0;
var max = 360.0 + arr[0] - arr[arr.length - 1];
var min = max;
var mix = 0; // index of first elment after largest gap
// exit early if array can't be adjusted
if (dist * arr.length > 240.0) return arr;
// find largest gap
for (var i = 1; i < arr.length; i++) {
var d = arr[i] - arr[i - 1];
if (d > max) {
max = d;
mix = i;
}
if (d < min) min = d;
}
var x = []; // working array
var adj = []; // final, adjusted array
// create working array on greatest gap
for (var i = 0; i < arr.length; i++) {
if (i + mix < arr.length) {
x.push(arr[i + mix] - 360);
} else {
x.push(arr[i + mix - arr.length]);
}
}
// iteratively adjust angles
while (min < dist) {
min = dist;
for (var i = 1; i < x.length; i++) {
var d = x[i] - x[i - 1];
if (d < dist) {
if (d < min) min = d;
x[i - 1] -= (dist - d) / 2;
x[i] += (dist - d) / 2;
}
}
}
// create final array
for (var i = 0; i < x.length; i++) {
if (i - mix < 0) {
adj.push(x[i - mix + x.length]);
} else {
adj.push(x[i - mix] + 360);
}
}
return adj;
}

Related

minimize of sum in javascript in two dimension array

I am trying to solve below problem ,my one test case fail why ?
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path.
Note: You can only move either down or right at any point in time.
Input:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.
/**
* #param {number[][]} grid
* #return {number}
*/
var minPathSum = function(grid) {
let firstRow = 0,
firstColumn = 0,
endColumn = 0,
endRow = 0;
if(grid.length === 1){
return grid[0][0]
}
let firstArray = grid[0];
let endArray = grid[grid.length - 1];
firstArray.forEach((i) => firstRow += i);
endArray.forEach((i) => endRow += i);
for (let i = 1; i < grid.length - 1; i++) {
firstColumn += grid[i].shift();
endColumn += grid[i].pop()
}
let cornEdge = grid[grid.length - 1].pop();
let firstElemt = grid[0].shift();
if (firstRow + endColumn + cornEdge> firstElemt + firstColumn + endRow) {
return firstElemt+ firstColumn + endRow
} else {
return firstRow + endColumn +cornEdge
}
};
failed test case
Input
[[1,2,5],[3,2,1]]
Output
7
Expected
6
from my point of view above expectation is wrong ? it should be 7 using 1->3->2->1 but how it is six
This is a brute force solution to the problem. It will recursively iterate every possible path through the array, and return the minimum found.
Unfortunately, whilst it works with the two test cases presented in the question, it exceeds the runtime execution limits on Leetcode for the more complex cases; although it does still yield an answer. For example:
minPathSum([
[3,8,6,0,5,9,9,6,3,4,0,5,7,3,9,3],
[0,9,2,5,5,4,9,1,4,6,9,5,6,7,3,2],
[8,2,2,3,3,3,1,6,9,1,1,6,6,2,1,9],
[1,3,6,9,9,5,0,3,4,9,1,0,9,6,2,7],
[8,6,2,2,1,3,0,0,7,2,7,5,4,8,4,8],
[4,1,9,5,8,9,9,2,0,2,5,1,8,7,0,9],
[6,2,1,7,8,1,8,5,5,7,0,2,5,7,2,1],
[8,1,7,6,2,8,1,2,2,6,4,0,5,4,1,3],
[9,2,1,7,6,1,4,3,8,6,5,5,3,9,7,3],
[0,6,0,2,4,3,7,6,1,3,8,6,9,0,0,8],
[4,3,7,2,4,3,6,4,0,3,9,5,3,6,9,3],
[2,1,8,8,4,5,6,5,8,7,3,7,7,5,8,3],
[0,7,6,6,1,2,0,3,5,0,8,0,8,7,4,3],
[0,4,3,4,9,0,1,9,7,7,8,6,4,6,9,5],
[6,5,1,9,9,2,2,7,4,2,7,2,2,3,7,2],
[7,1,9,6,1,2,7,0,9,6,6,4,4,5,1,0],
[3,4,9,2,8,3,1,2,6,9,7,0,2,4,2,0],
[5,1,8,8,4,6,8,5,2,4,1,6,2,2,9,7]
]);
// 83 = 3+0+8+2+2+3+3+3+1+0+0+0+2+0+2+5+0+2+0+5+4+1+3+3+8+3+3+3+5+2+0+0+7
const minPathSum = function(grid) {
let maxX = grid[0].length - 1;
let maxY = grid.length - 1;
return mapPath(grid, 0, 0, maxX, maxY, 0);
};
const mapPath = function(grid, x, y, maxX, maxY, length) {
const value = grid[y][x];
if (x === maxX && y === maxY) {
return length + value;
}
const minX = (x < maxX)
? mapPath(grid, x + 1, y, maxX, maxY, length + value)
: Infinity;
const minY = (y < maxY)
? mapPath(grid, x, y + 1, maxX, maxY, length + value)
: Infinity;
return Math.min(minX, minY);
};
console.log(minPathSum([[1,3,1],[1,5,1],[4,2,1]]));
console.log(minPathSum([[1,2,5],[3,2,1]]));
You should do like:
function min(array){
return Math.min(...array);
}
function sum(array){
return array.reduce((a, c)=>a+=c);
}
function sumsMin(arrayOfArrays){
const sums = [];
arrayOfArrays.forEach(a=>{
sums.push(sum(a));
});
return min(sums);
}
console.log(sumsMin([[1,2,5],[3,2,1]]));

JavaScript: Math.random: How to choose a random number between 190 and 255 "unevenly", not "evenly"?

If you want to choose a random number between 190 and 255, expecting evenly distributed results, the code you need is as simple as the following, correct?
190 + Math.floor(Math.random() * 66);
But what if you prefer unevenly distributed results between 190 and 255? I mean, for instance, the closer a number is to the lower end of the range (i.e. 190), the higher the possibility of the number being chosen.
Let us suppose that the code returns a random number:
between 190 and 210 with a 70% probability.
between 211 and 230 with a 20% probability.
between 231 and 255 with a 10% probability.
I think that the uneven distribution like this adds an interesting flavour to the act of choosing a random number.
I've written the code for this in two different ways, the second one taking a more complex form. I started to learn programming just several days ago, so I've written them, drawing on what little I know of JavaScript at the moment. I wonder if I can express them more efficiently.
Incidentally, I have one specific question in mind:
In the 1st Code, do I need to place var in front of attackPoint = midRange and attackPoint = highRange in the if/else statement?
1st Code:
var lowRange = 190 + Math.floor(Math.random() * 21);
var midRange = 211 + Math.floor(Math.random() * 20);
var highRange = 231 + Math.floor(Math.random() * 25);
var randomHundred = 1 + Math.floor(Math.random() * 100);
if (randomHundred <= 70) {
var attackPoint = lowRange;
}
else if (randomHundred <= 90) {
var attackPoint = midRange;
}
else {
var attackPoint = highRange;
}
console.log(attackPoint);
2nd Code:
var lowRange = 190 + Math.floor(Math.random() * 21);
var midRange = 211 + Math.floor(Math.random() * 20);
var highRange = 231 + Math.floor(Math.random() * 25);
var emptyArray = [];
for (var i = 0; i < 100; i++) {
if (i < 70) {
emptyArray.push(lowRange);
}
else if (i < 90) {
emptyArray.push(midRange);
}
else {
emptyArray.push(highRange);
}
};
var attackPoint = emptyArray[Math.floor(Math.random() * 100)];
console.log(attackPoint);
The 1st way improvement:
You don't need to calculate random values for all ranges, you only need the selected range. Also it's unnecessary to cast 0..1 range to 0..100. You can deal with 0..1 range directly.
var q = Math.random(), attackPoint;
if (q < 0.7) {
attackPoint = 190 + Math.floor(Math.random() * 21);
}
else if (q < 0.9) {
attackPoint = 211 + Math.floor(Math.random() * 20);
}
else {
attackPoint = 231 + Math.floor(Math.random() * 25);
}
console.log(attackPoint);
On your specific point, I am not a Javascript expert, but I think you need:
var attackPoint; // Declare, but do no initialize the variable.
if (randomHundred <= 70) {
attackPoint = lowRange;
}
else if (randomHundred <= 90) {
attackPoint = midRange;
}
else {
attackPoint = highRange;
}
A more efficient way is going to involve "stretching" the range you want to be more likely, and then choosing randomly. Something like:
var attackValues = []
for (var i = 190; i < 211; i++) {
for (var j = 0; j < 7; j++) {
attackValue.push(i); // Seven values here.
}
}
for (var i = 211; i < 231; i++) {
attackValue.push(i); attackValue.push(i); // Two values here
}
for (var i = 231; i < 256; i++) {
attackValue.push(i); // Just one value here.
}
// The attackValue array could be calculated once and stored for repeated
// use if your inflection points stay constant.
var attackPoint = attackValue[ Math.floor(Math.random() * attackValue.length) ]
The idea is to fill attackValue array with the possible results, putting in multiple copies of the values you want more often, and then choosing one at random.
Another approach is to do something like square the random value, and then convert to integer. Squaring makes smaller values more likely in a somoother way than adding specific inflexion points. Depending on your application, that may be better (or not).
Just from a programming style it is good form to separate the table of probabilities from the logic that generates the random numbers. The reason being that the exact details of the probabilities and ranges are likely to change over time, but the idea of a weighted random number will stay constant. So once you've got the weighted random number code changing, you don't want to have to change it time and again:
var table = [
{ probability:70, low:190, hi: 210},
{ probability:20, low:211, hi: 230},
{ probability:10, low:231, hi: 255}
]
function weightedRandom(table) {
var total = table.reduce(function(sum, line) {
return sum + line.probability;
}, 0);
var select = Math.floor(Math.random() * total);
for (var i = 0; i < table.length; i++) {
var line = table[i];
select -= table[i].probability;
if (select < 0) {
return (Math.floor(Math.random() * (line.hi - line.low)) +
line.low;)
}
}
}
for (var i = 0; i < 10; i++) {
console.log(weightedRandom(table));
}
NOTE: you could simplify the above code if you assume that the probabilities will always add to 100.
When I run this I get:
237
228
239
209
193
218
213
193
245
214
which seems correct.
I think this might be a possible solution too:
var number = Math.random();
var index = Math.floor(number * 66) + 190;
//answer:
console.log(Math.floor(number * dist(index) * 66) + 190);
function dist(index) {
var retValue;
if (index < 210) {
retValue = .7;
}
else if (number >= 210 && number < 230) {
retValue = .2;
}
else {
retValue = .1;
}
return retValue;
}

Find the closest index to given value [duplicate]

This question already has answers here:
Get the closest number out of an array
(21 answers)
Closed 7 years ago.
I have an ordered array:
btnDrag.pos = [0, 65, 131, 196, 259, 323, 388, 453, 517];
And a function that fires when drag stops:
btnDrag.draggable({
axis: 'x',
containment: 'parent',
stop: function() {
var index = (function(){
var new_x = btnDrag.position().left;
// now, how to find the closest index in btnDrag.pos relative to new_x ?
// return index;
})();
btnDrag.animate({
'left': (btnDrag.pos[index] + 'px')
});
}
});
The array values are points which btnDrag is allowed to stay (in axis 'x').
So, the function must return the closest index with the value to btnDrag go.
Thanks in advance.
Since your array is sorted, the fastest way is to use a modified version of the binary search algorithm:
function closest (arr, x) {
/* lb is the lower bound and ub the upper bound defining a subarray or arr. */
var lb = 0,
ub = arr.length - 1;
/* We loop as long as x is in inside our subarray and the length of our subarray is
greater than 0 (lb < ub). */
while (ub - lb > 1) {
var m = parseInt((ub - lb + 1) / 2); // The middle value
/* Depending on the middle value of our subarray, we update the bound. */
if (arr[lb + m] > x) {
ub = lb + m;
}
else if (arr[lb + m] < x) {
lb = lb + m;
}
else {
ub = lb + m;
lb = lb + m;
}
}
/* After the loop, we know that the closest value is either the one at the lower or
upper bound (may be the same if x is in arr). */
var clst = lb;
if (abs(arr[lb] - x) > abs(arr[ub] - x)) {
clst = ub;
}
return clst; // If you want the value instead of the index, return arr[clst]
}
Here is a fiddle where you can test it: http://jsfiddle.net/Lpzndcbm/4/
Unlike all the solution proposed here this solution runs in O(log(n)) and not in O(n). If you are not familiar with complexity, it means that this algorithm will find the closest value in an array of size N in at most O(log(N)) loop while the others will find it in at most N loop (with N = 10000, it makes a big difference since log(10000) ~ 14 (binary log)).
Note that if you have really small array, this may be slower than the naive algorithm.
There you go :
function closest(list, x) {
var min,
chosen = 0;
for (var i in list) {
min = Math.abs(list[chosen] - x);
if (Math.abs(list[i] - x) < min) {
chosen = i;
}
}
return chosen;
}
Each time, the minimum distance is computed and the chosen value is updated based on the minimum. (http://jsbin.com/dehifefuca/edit?js,console)
Something like this?
var closest = btnDrag.pos.reduce(function (prev, curr) {
return (Math.abs(curr - new_x) < Math.abs(prev - new_x) ? curr : prev);
});
Simple for loop will do it:
var btnDrag = {};
btnDrag['pos'] = [0, 65, 131, 196, 259, 323, 388, 453, 517];
new_x = 425;
var index = -1;
for (var i = 0; i < btnDrag.pos.length; i++)
{
if (i < btnDrag.pos.length-1) //loop till i is at 2 positions from the end.
{
//value has to be less then the selected value + 1
if (new_x < btnDrag.pos[i+1])
{
//calculate the half between the values and add it with the first value
// test if new_x is larger then that value.
if ((btnDrag.pos[i+1] - btnDrag.pos[i])/2 + btnDrag.pos[i] > new_x)
{
index = i;
break;
}
else
{
index = i+1;
break;
}
}
}
else
{
//edge cases.
if (new_x < 0)
{
index = 0;
}
else
{
index = btnDrag.pos.length-1;
}
}
}
document.body.innerHTML = btnDrag['pos'][index] + " (" + index + ")";

Skewed Number Generator

I have a simple implementation question.
Here is the random number function I have, and returns a random number from a given range, inclusive.
function randomNum(low, high){
return Math.floor(Math.random() * (high - low + 1)) + low;
}
However, I would like to have 50% chance of getting the high number, and 25% for everything else..
for example:
randomNum(1, 3)
'3' would have a 50% chance of getting a hit, while '1' and '2' will both have a hit percentage of 25%.
I'm not too sure as to what changes I need to make to my function...tips would be great, Thanks
function randomNum(low, high){
return Math.random() > 0.5 ?
high :
Math.floor(Math.random() * (high - low)) + low;
}
In a generic manner; I suppose you're after a weighted random number generator:
function weightedRandomNumber(weights) {
var sum = 0;
for (var w in weights) {
w = weights[w];
sum += w.weight;
}
var rand = Math.random() * sum;
for (var w in weights) {
w = weights[w];
if (rand < w.weight) {
return w.value;
}
rand -= w.weight;
}
return weights[weights.length - 1].value;
}
Test:
var config = [
{ weight: 25, value: 1 },
{ weight: 25, value: 2 },
{ weight: 50, value: 3 }
];
var test = { 1: 0, 2: 0, 3: 0 }, max = 10000;
for (var i = 1; i < max; i += 1) {
test[weightedRandomNumber(config).toString()] += 1;
}
alert('From ' + max + ' rounds; results: ' + JSON.stringify(test));
Make if else condition
If it is 3 its ok or else if it is not 3 then again make a random number between 1 and 2;
Hence the 3 will get 50% chances where as 1,2 will get 25% chances
There are two approaches that you can use. (1) You can have array of value and random the index of value to get. If you want certain number to have higher chance, just put it more. For example:
var arr = [1, 2, 3, 3];
return arr[Math.floor(Math.random() * arr.length)];
(2) Second approach is doing the array shuffling.
var arr[1, 2, 3, 3];
shuffle(arr);
return arr[0];
This should work:
function randomNum(low, high){
var mid = (low + high)/2;
var randomn = Math.floor(Math.random() * (high - low + 1)) + low;
if(randomn > mid)
return randomn ;
else
return Math.floor(Math.random() * (high - low + 1)) + low;
}
here you go. high will have 50% chance, the rest will split the other 50% equally
function randomNum(low, high)
{
var myarry = []
for(var i=0;i<(high-low);i++) { myarry.push(low+i) } ; //var myarry=[low, low+1, ....,high-1] excludes high
console.log(myarry)
var magic=Math.random();
var index=Math.round(magic*(high-low)); // Gaurantee the chance is split between the elements of the array
return Math.round(magic)==1?high:myarry[index] // Guaranteed 50% chance for high either 0 or 1, the rest will split the chance
}

In an array, how do I find the closest key given a float value?

I'm making an "acceleration" array like this:
acc["0100"] = 1;
acc["0300"] = 2;
acc["0600"] = 4;
acc["0900"] = 8;
acc["2000"] = 16;
acc["5000"] = 32;
And, when the user presses a key, I start a timer: this._startTick = (new Date()).getTime();
Now I have a timer that checks if the key is still pressed. If so, then I do something like:
this._delay = (new Date()).getTime() - this._startTick;
And now, based on this._delay, I'd like to find one of the previous values (1, 2, 4 or 8). How would you do that?
NB: if the value is greater than "5.0" then the result should always be 32.
NOTA: my goal is, given an elapsed time, find out which value is the best. I started the way I've just explained, but if you have another solution, I'll take it!
It's easier to operate on an array than on an object:
var accArr = [];
for (time in acc) {
accArr.push({time: time, value: acc[time]});
}
Assuming you have an array, you can do:
function getValue(delay) {
var diffs = accArr.map(function (e) { return Math.abs(e.time - delay); });
return accArr[diffs.indexOf(Math.min.apply(null, diffs))].value;
}
EDIT:
Well, you didn't mention that this is a performance-critical function. In that case, I would recommend picking a granularity (e.g. 0.05, so the multiplier for delay is 20) and pre-calculating all values from 0 to MAX_DELAY:
var multiplier = 20,
granularity = 1 / multiplier;
var delayValues = (function () {
var result = [];
for (var delay = 0; delay <= MAX_DELAY; delay += granularity) {
result.push(getValue(delay));
}
return result;
})();
During the animation, fetching the value will be a simple lookup in a relatively small table:
function getValueFast(delay) {
return (delayValues[Math.round(delay * multiplier)] ||
delayValues[delayValues.length - 1])
}
JSPerf comparison between this solution and simple if statements shows they perform equally fast for searching around a middle value.
Here is the jsfiddle test page.
var getAccForDelay = (function () {
var acc = {
0.1: 1,
0.3: 2,
0.6: 4,
0.9: 8,
2.0: 16,
5.0: 32
};
return function(delay) {
var key,
bestKey = undefined,
absDiff,
absDiffMin = Number.MAX_VALUE;
for (key in acc) {
if (acc.hasOwnProperty(key)) {
absDiff = Math.abs(delay - key);
if (absDiffMin > absDiff) {
absDiffMin = absDiff;
bestKey = key;
}
}
}
return bestKey === undefined ? undefined : acc[bestKey];
};
}());
Test:
console.clear();
console.log(getAccForDelay(0));
console.log(getAccForDelay(0.33));
console.log(getAccForDelay(3.14));
console.log(getAccForDelay(123456.789));
Output:
1
2
16
32
=== UPDATE ===
The above solution doesn't utilize of the fact that acc is sorted by key. I optimized the code by replacing linear search with binary search, which is much faster on long arrays. Here is the test page.
var getAccForDelay = (function () {
var accKey = [ 0.1, 0.3, 0.6, 0.9, 2.0, 5.0 ],
accValue = [ 1, 2, 4, 8, 16, 32 ],
accLength = accKey.length;
return function(delay) {
var iLeft, iMiddle, iRight;
iLeft = 0;
if (delay <= accKey[iLeft])
return accValue[iLeft];
iRight = accLength - 1;
if (accKey[iRight] <= delay)
return accValue[iRight];
while (true) {
if (iRight - iLeft === 1)
return delay - accKey[iLeft] < accKey[iRight] - delay ? accValue[iLeft] : accValue[iRight];
iMiddle = ~~((iLeft + iRight) / 2);
if (delay < accKey[iMiddle])
iRight = iMiddle;
else if (accKey[iMiddle] < delay)
iLeft = iMiddle;
else
return accValue[iMiddle];
}
};
}());
In my humble opinion I think the best solution to this problem is to write a function which picks the best acceleration based on the time using if statements as follows:
function getAcceleration(time) {
if (time < 0.20) return 1;
if (time < 0.45) return 2;
if (time < 0.75) return 4;
if (time < 1.45) return 8;
if (time < 3.50) return 16;
return 32;
}
However this is a static solution. If that's alright with you then I recommend you use this method. On the other hand if you need a dynamic solution then use this instead:
var getAcceleration = createAccelerationMap(0.1, 0.3, 0.6, 0.9, 2.0, 5.0);
function createAccelerationMap(previous) {
var length = arguments.length, limits = [];
for (var i = 1; i < length;) {
var current = arguments[i++];
limits.push((previous + current) / 2);
previous = current;
}
return function (time) {
var length = limits.length, acceleration = 1;
for (var i = 0; i < length;) {
if (time < limits[i++]) return acceleration;
acceleration *= 2;
}
return acceleration;
};
}
Either way you may then use getAcceleration as follows:
console.log(getAcceleration(0)); // 1
console.log(getAcceleration(0.33)); // 2
console.log(getAcceleration(0.64)); // 4
console.log(getAcceleration(1.42)); // 8
console.log(getAcceleration(3.14)); // 16
console.log(getAcceleration(123456.789)); // 32
See the demo: http://jsfiddle.net/QepT7/
If the 0.1 is the number of seconds, and you want to round to 1 decimal you can do something this:
// 0.42332 * 10 = 4.2332
// Math.round( ) will be 4
// 4 / 10 = 0.4
acc[ (Math.round(this._delay * 10) / 10).toString() ]
var seconds = this._delay.toString().substring(0,2)
console.log(acc[seconds]);
This is a straight-forward approach of your problem: First I convert the float to a string, second I cut off everything after the third character.
What units are you using?
this._startTick = (new Date()).getTime();
// ms = ms
this._delay = (new Date()).getTime() - this._startTick;
// ms = ms - ms
So to get to "0.1"/etc from milliseconds I'm assuming you are doing
(Math.floor(ms / 100) / 10).toString();
Why not just keep everything in ms/100 so you can use integers?
var acc = [];
acc[ 1] = 1;
acc[ 3] = 2;
acc[ 6] = 4;
acc[ 9] = 8;
acc[20] = 16;
acc[50] = 32;
Then you can create a "nearest" lookup function like this
function find(x) {
var i = 0;
x = x | 0; // The | 0 will cause a cast to int
if (x < 0) x = 0;
if (acc[x] !== undefined) return acc[x];
if (x > acc.length) return acc[acc.length - 1];
while (++i < acc.length) {
if (acc[x - i] !== undefined) return acc[x - i];
if (acc[x + i] !== undefined) return acc[x + i];
}
}
find(this._delay / 100);
Now examples are
find(30); // 16
find(100.5); // 32
find(0); // 1

Categories