Javascript 在数组中,如何找到给定浮点值的最近键?
我正在制作一个“加速”数组,如下所示:Javascript 在数组中,如何找到给定浮点值的最近键?,javascript,arrays,sorting,Javascript,Arrays,Sorting,我正在制作一个“加速”数组,如下所示: acc["0100"] = 1; acc["0300"] = 2; acc["0600"] = 4; acc["0900"] = 8; acc["2000"] = 16; acc["5000"] = 32; 当用户按下一个键时,我启动一个计时器:this.\u startTick=(new Date()).getTime() 现在我有一个计时器,可以检查按键是否仍然按下。如果是这样,那么我会做如下事情: this._delay = (new Date()
acc["0100"] = 1;
acc["0300"] = 2;
acc["0600"] = 4;
acc["0900"] = 8;
acc["2000"] = 16;
acc["5000"] = 32;
当用户按下一个键时,我启动一个计时器:this.\u startTick=(new Date()).getTime()代码>
现在我有一个计时器,可以检查按键是否仍然按下。如果是这样,那么我会做如下事情:
this._delay = (new Date()).getTime() - this._startTick;
现在,基于此.\u delay
,我想找到前面的一个值(1、2、4或8)。你会怎么做
注意:如果该值大于“5.0
”,则结果应始终为32
注:我的目标是,给定经过的时间,找出哪个值是最好的。我是按照刚才解释的方式开始的,但如果你有其他解决方案,我就接受 如果
0.1
是秒数,并且您希望将小数点四舍五入为1,则可以执行以下操作:
// 0.42332 * 10 = 4.2332
// Math.round( ) will be 4
// 4 / 10 = 0.4
acc[ (Math.round(this._delay * 10) / 10).toString() ]
这是解决问题的直接方法:首先,我将浮点转换为字符串,其次,我将第三个字符后面的所有字符都切掉。是JSFIDLE测试页面
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];
};
}());
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];
}
};
}());
测试:
输出:
1
2
16
32
==更新===
上述解决方案没有利用acc
按键排序这一事实。我通过使用替换线性搜索来优化代码,这在长数组上要快得多。是测试页面
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];
};
}());
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];
}
};
}());
var getAccForDelay=(函数(){
var accKey=[0.1,0.3,0.6,0.9,2.0,5.0],
accValue=[1,2,4,8,16,32],
accLength=accKey.length;
返回功能(延迟){
var iLeft、Imddle、iRight;
iLeft=0;
如果(延迟您使用什么单位
this._startTick = (new Date()).getTime();
// ms = ms
this._delay = (new Date()).getTime() - this._startTick;
// ms = ms - ms
因此,要从毫秒开始进入“0.1”
/etc,我假设您正在这样做
(Math.floor(ms / 100) / 10).toString();
为什么不把所有东西都保存在ms/100
中,这样就可以使用整数了
var acc = [];
acc[ 1] = 1;
acc[ 3] = 2;
acc[ 6] = 4;
acc[ 9] = 8;
acc[20] = 16;
acc[50] = 32;
然后您可以像这样创建一个“最近的”查找函数
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);
在阵列上操作比在对象上操作更容易:
var accArr = [];
for (time in acc) {
accArr.push({time: time, value: acc[time]});
}
假设您有一个数组,您可以执行以下操作:
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;
}
编辑:
嗯,您没有提到这是一个性能关键函数。在这种情况下,我建议选择一个粒度(例如0.05
,因此延迟的乘数是20
),并预先计算0
到最大延迟的所有值:
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;
})();
在这个解决方案和简单的if
语句之间,它们在搜索中间值时的速度是一样快的。在我看来,这个问题的最佳解决方案是编写一个函数,使用if
语句根据时间选择最佳加速度,如下所示:
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;
}
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
请参阅演示:但几乎所有的指示都在0-1之间。您如何处理这种情况?如果没有相应的键?我的问题不够精确,我将进行相应修改。您不是将acc
用作数组,而是用作对象(在JavaScript中工作,因为数组继承自对象).@PaulS.好吧,我的错。那么也许你有其他方法来实现我想要做的事情?这是直截了当的,但是如果延迟超过100怎么办?如果延迟==12345.456
,值应该是8。你会怎么做?@OlivierPons你更新的acc对象让问题更难解决,所以我不得不完全重写我的答案。我不理解这部分“if(acc.hasOwnProperty(key))
”因为它位于()
和acc
将始终具有属性键
…除非我遗漏了什么?@OlivierPons我迭代了acc的属性,此hasOwnProperty检查确保我不会意外使用继承的属性。在这种特殊情况下不需要这样做,但这是“标准方式”"迭代对象本身的属性,所以我这样写代码。这是一个非常好的答案,我想我会检查它是否正确,尽管我遇到了另一个大问题:速度变慢。我想我会实现如下内容:或者从这里开始:你做得很好,谢谢你的建议,我会用这个想法修改我的代码,b但不幸的是,这并不能回答我的问题(我的钥匙问题)。还有一个问题:如果(this.\u delay/100)|0
给出的索引超出范围,我希望它给出可能的最高值(acc[4]
)“你会怎么做?”奥利维波彭斯还说,如果你期待大量的未定义的VS定义,那么你可以考虑一个更接近于这里描述的查找函数,你也可以用适当的值来填充<代码> ACC < /代码>的所有元素(例如,代码> ACC〔2〕。=1
并摆脱while循环中的笨拙检查。在执行时间方面更容易还是更快?更容易,如“更方便”,因为数组有对象没有的实用函数:map
,indexOf
,等等。在一个列表/对象中有5个项目,不必担心执行时间优化。如果我有100个精灵,我必须每1/60秒重新计算加速度,这意味着60*100*5=30000次比较/秒,而不是“Aadit M Shah”#1建议(平均15000次比较/秒)。我认为这是一个我应该注意的“基本”优化。我不知道我是否错了……一场游戏中100个精灵是很少的(计算得分、气泡、提示、爆炸的数字,更不用说每个“真实”精灵之间的碰撞检测了(与之前的版本没有冲突)。好的,添加了一个针对速度而非优雅优化的版本:)
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;
}
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;
};
}
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