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