【问题标题】:Map and match ranges in javascriptjavascript中的映射和匹配范围
【发布时间】:2012-09-05 19:40:26
【问题描述】:

在 JavaScript 中使用数组映射范围最有效的是什么?

我有这个:

var def = [70,200,1000];

还有一组可能的数字,比如说:

var n = [23,45,74,120,240,800,1204,2000];

现在,如何从 n 中提取与 def 中的值匹配的最接近的值?

在上面的例子中,我会得到[74,240,800]。我希望我说清楚...

【问题讨论】:

  • 如果n 已排序,您需要对def 中的每个条目进行二进制搜索,并在最后一次访问时停止,如果不是实际目标。如果没有重复的选择,则从 n 中删除条目。这将是o(#def . log #n),如果两者都通过n 进行排序,则可以将其设为o(#n),但如果def 较小且n 较高,则情况会更糟。
  • 如果我们可以假设n 将是一个排序数组,那么逻辑将类似于:iterate through n until a value greater than this is found. compare that value with the value from the previous index to determine which is closer to this
  • 想一想:如果def被排序了,我们真的需要每次都循环遍历n吗?
  • 如果我们假设两个数组都已排序,则不会。否则,我相信您需要为 def 中的每个值遍历 n。
  • 这是 Python 中的解决方案:[min(n, key=lambda p: abs(d-p)) for d in def_]。抱歉,我无法抗拒。

标签: javascript arrays math map match


【解决方案1】:

这是一个尝试。只迭代一次 defn

var nIndex = 0,
    nlen = n.length,
    prev,
    next;

for(var i = 0, ilen = def.length; i < ilen && nIndex < nlen; i++) {
    var current = def[i];

    while (n[nIndex] < current && nIndex < nlen - 1) nIndex++;

    next = n[nIndex];
    prev = nIndex == 0 ? next : n[nIndex - 1];

    result[i] = current - prev < next - current ? prev : next;
}
// values in def are larger than the greatest value in n
while (result.length < def.length) result.push(n[nlen-1]);

fiddle

【讨论】:

  • 很好,但是是否可以使用可读的变量来获取它,以便我可以遵循?它现在看起来有点像汇编程序......另外,小提琴链接是空的。
【解决方案2】:

http://jsfiddle.net/8bY2M/

var def = [70,200,1000];
var n = [23,45,74,120,240,800,1204,2000];

var result = [];

for (var i = 0; i < def.length; i++){
    var val = def[i];
    for (var j = 0; j < n.length; j++){
        var nVal = n[j];                
        if (nVal > val){
            var closest = n[j-1] == undefined ? nVal : 
                nVal - val > val - n[j-1] ? n[j-1] : nVal;

            result.push(closest);
            break;
        }    
    }
}

console.log(result); // outputs: [74, 240, 800] 

请注意,如果有两个匹配项,这将使用较大的值。 (即如果你试图匹配 70,并且 n 包含 66 和 74,这将与 74 匹配)

【讨论】:

    【解决方案3】:

    其他解决方案非常简单,但如果您正在寻找效率,最好的办法是对初始列表进行预排序(如果尚未排序),然后执行二分搜索。这是我的解决方案:

    var def = [70,200,1000];
    var n = [23,45,74,120,240,800,1204,2000];
    
    var findClosest = function(n, def) {
        var i, result = [];
    
        n = n.sort(function(a, b) { return a - b; }); //numeric sort
    
    
        var closestN = (function(val) {
            return (function search(r) {
                var mid = r.low + (0 | ((r.high - r.low) / 2));
    
                if(n[mid] === val) { 
                    return n[mid];
                }
    
                if((r.high - r.low) === 1) {
                    return n[r[(Math.abs(n[r.low] - val) > Math.abs(n[r.high] - val)) ? "high" : "low"]];
                }
    
                r[(val < n[mid]) ? "high" : "low"] = mid;
    
                return search(r);
            }({low : 0, high: (n.length - 1)})); 
        });
    
        for (i=0; i<def.length; i++) {
            result.push(closestN(def[i]));    
        }
    
        return result;
    };
    
    //The result can be obtained with findClosest(n, def)
    

    请注意,它的可读性不是很高(如果您不熟悉二进制搜索算法),如果您正在寻找性能不重要的简单解决方案,线性搜索会产生更易于维护的代码。但是这个解决方案在 O(n log(n)) 时间内运行,而不是像线性搜索那样在 O(n^2) 时间内运行。

    您可以在这里看到它的实际效果:

    http://jsfiddle.net/naYu6/

    请注意,在“平局”的情况下(列表中的两个值与所讨论的值等距,将选择两者中较低的一个)。

    【讨论】:

      猜你喜欢
      • 2020-09-12
      • 2023-01-09
      • 1970-01-01
      • 1970-01-01
      • 2020-11-19
      • 2021-06-12
      • 1970-01-01
      • 2014-08-19
      • 1970-01-01
      相关资源
      最近更新 更多