3

次のような「加速」配列を作成しています。

acc["0100"] = 1;
acc["0300"] = 2;
acc["0600"] = 4;
acc["0900"] = 8;
acc["2000"] = 16;
acc["5000"] = 32;

そして、ユーザーがキーを押すと、タイマーを開始します。this._startTick = (new Date()).getTime();

これで、キーがまだ押されているかどうかを確認するタイマーができました。もしそうなら、私は次のようなことをします:

this._delay = (new Date()).getTime() - this._startTick;

そして今、 に基づいてthis._delay、以前の値 (1、2、4、または 8) のいずれかを見つけたいと思います。どうやってそれをしますか?

注意: 値が " " より大きい場合5.0、結果は常に になります32

注: 私の目標は、経過時間が与えられたときに、どの値が最適かを調べることです。今説明した方法で始めましたが、別の解決策があれば、それを使用します。

4

6 に答える 6

2

jsfiddleのテスト ページは次のとおりです。

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];
    };
}());

テスト:

console.clear();
console.log(getAccForDelay(0));
console.log(getAccForDelay(0.33));
console.log(getAccForDelay(3.14));
console.log(getAccForDelay(123456.789));

出力:

1
2
16
32

===更新===

上記のソリューションはacc、キーでソートされているという事実を利用していません。線形検索をバイナリ検索に置き換えることでコードを最適化しました。これは、長い配列ではるかに高速です。テストページはこちら。

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];
        }
    };
}());
于 2013-08-28T14:59:51.580 に答える
2

オブジェクトよりも配列を操作する方が簡単です。

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のすべての値を事前に計算します。0MAX_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;
})();

アニメーション中の値のフェッチは、比較的小さなテーブルでの単純なルックアップになります。

function getValueFast(delay) {
    return (delayValues[Math.round(delay * multiplier)] || 
            delayValues[delayValues.length - 1])
}

このソリューションと単純なステートメントをJSPerf で比較するifと、中間値付近の検索で同等に高速に実行されることがわかります。

于 2013-08-28T15:50:23.953 に答える
1

私の謙虚な意見では、この問題に対する最善の解決策は、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;
}

ただし、これは静的なソリ​​ューションです。それでもよろしければ、この方法をお勧めします。一方、動的なソリューションが必要な場合は、代わりにこれを使用してください。

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;
    };
}

どちらの方法でも、getAcceleration次のように使用できます。

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

デモを参照してください: http://jsfiddle.net/QepT7/

于 2013-08-28T17:17:17.373 に答える
0
var seconds = this._delay.toString().substring(0,2)

console.log(acc[seconds]);

これはあなたの問題に対する簡単なアプローチです。最初に float を文字列に変換し、次に 3 番目の文字以降をすべて切り捨てます。

于 2013-08-28T14:53:56.707 に答える
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);

今例は

find(30);    // 16
find(100.5); // 32
find(0);     //  1
于 2013-08-28T15:01:21.103 に答える
0

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() ]
于 2013-08-28T14:50:39.050 に答える