【问题标题】:Find the number in an array that is closest to a given number查找数组中最接近给定数字的数字
【发布时间】:2011-01-26 23:53:37
【问题描述】:

我在 javascript 中有一个整数数组,[5,10,15,20,25,30,35] 当给定一个数字 x 时,如何在数组中找到最接近该数字的元素?

如果数字超过一个值,但小于下一个数字的一​​半,我会选择较小的值,如果它超过下一个数字的一​​半,我会选择更大的数字。

例如 7 会返回 5,但 8 会返回 10。我怎样才能做到这一点?任何帮助或提示将不胜感激。我已经搜索并找不到解决方案。我敢肯定这很常见。

【问题讨论】:

  • 像往常一样:到目前为止你尝试了什么?
  • 获取与您的点的绝对距离(作为 Y),然后将该 Y 反转为比例(这围绕 X === 0.5 进行缩放,其中 Y 为最大值)(0.5 - Math.abs(0.5 - value)) / 0.5;

标签: javascript arrays algorithm math


【解决方案1】:

可能最简单的做法是根据与参考值 x 的距离进行排序,然后取第一项。

内置的Array.prototype.sort() 可以采用一个比较函数,该函数将被调用以获取数组中的值对。然后关键是简单地传入一个比较函数,该函数根据它们与参考值 x 的距离来比较两个值。

let x = 8;
let array = [5, 10, 15, 20, 25, 30, 35];
let closest = array.sort( (a, b) => Math.abs(x - a) - Math.abs(x - b) )[0];

看这个简单的demo。

【讨论】:

  • 点赞。非常优雅的解决方案。
【解决方案2】:
function getClosest(array, target) {
    var tuples = _.map(array, function(val) {
        return [val, Math.abs(val - target)];
    });
    return _.reduce(tuples, function(memo, val) {
        return (memo[1] < val[1]) ? memo : val;
    }, [-1, 999])[0];
}

如果使用函数方法适用,那么您可以将集合映射到 (value, distance) 的元组,然后将该元组集合减少为距离最小的元组。我们返回该元组中的值。

解释_.map的用法。您将数组中的所有值映射到新值,该函数将返回新值数组。在这种情况下是一个元组数组。

解释_.reduce的用法。您将数组减少为单个值。你传入一个数组和一个备忘录。当您在阵列中移动时,备忘录是您的“运行计数器”。在这种情况下,我们检查当前元组是否比备忘录更接近,如果是,则将其作为备忘录。然后我们在最后返回备忘录。

上面的代码 sn-p 依赖于underscore.js 来去除函数式 javascript 的本质

【讨论】:

  • 附带说明,在 Lodash 中给出约 200 个项目的列表,Lodash 的性能比下划线版本 (~29K ops/sec) 好约 1.5 倍 (~45K ops/sec)。
  • 顺便说一句,_.reduce 可以换成“元组”数组上的数字排序;但是,这几乎没有表现得那么好。同样,使用对象/哈希而不是元组更具可读性,但性能也很差。
  • 最后说明,将下划线换成 ES5 Array.prototype.map 和 Array.prototype.reduce 会产生相似(几乎相同)的性能。
【解决方案3】:

您的示例列表已排序。如果总是这样,那么二进制搜索你的号码。如果您没有找到确切的数字,请通过检查数字 将 所在位置周围的两个数字来结束二分搜索并返回最接近的数字。小心所有数字都大于或小于目标数字的边缘情况

如果列表不总是排序,则遍历列表,跟踪最大数字 = 目标数字。返回最接近目标的那个。

在任一解决方案中,例如,如果您在 [1, 3] 中搜索 2,您都需要决定偏向哪一边。

【讨论】:

    【解决方案4】:

    创建一个与原始数组大小相同的临时数组,并使用 x 和数组元素之间的差异填充它。

    例如,让临时数组为 temp[],而您的原始数组为 a[]:

    temp[i]=Math.abs(x-a[i]);
    

    然后,将temp[]中最小值的index返回给用户。

    【讨论】:

      【解决方案5】:

      假设数组已排序,遍历数组中每对相邻的整数。对于每一对(比如“5 和 10”或“20 和 25”),测试 x 是否在它们之间,如果是,则返回更接近 x的那个> (偏向较低的那个)。

      当 x 小于第一个数字(返回第一个数字)或大于最后一个数字(返回最后一个数字)时,您还需要一个特殊情况。

      如果数组没有排序,先排序。

      【讨论】:

      • 如果数组已排序,您可以进行二分搜索(或几乎任何其他搜索)来找到最接近的两个数字...
      • @marcog 我在回答之前的评论,但已经消失了.. 将删除它
      • 没错,二分查找会更好。如果数组未排序,则无论如何都应该排序。
      • 绝妙的主意...现在执行。
      【解决方案6】:

      我创建了自己的函数,因为我找不到任何符合我要求的函数。

          function closest_number(quantities, number, closest_factor)
          {
              if (closest_factor == 'ceil')
              {
                  quantities.sort(function(a, b)
                      {
                          return a - b
                      }
                  );
      
                  for (var i = 0; i < quantities.length; i++)
                  {
                      if (quantities[i] >= number)
                      {
                          return quantities[i];
                      }
      
                      last_value = quantities[i];
                  }
      
                  return last_value;
              }
              else if (closest_factor == 'floor')
              {
                  quantities.sort(function(a, b)
                      {
                          return a - b
                      }
                  );
      
                  min_value = quantities[0];
      
                  for (var i = 0; i < quantities.length; i++)
                  {
                      if (number == quantities[i])
                      {
                          return number;
                      }
                      else if (quantities[i] < number)
                      {
                          min_value = quantities[i];
                      }
                      else if(quantities[i] > number)
                      {
                          return min_value;
                      }           
                  }
      
                  return min_value;
              }
              else
              {
                  return false;
              }
          };
      

      【讨论】:

        【解决方案7】:

        由于Array.reduce 已经存在很长时间了(even IE9 supports it),所以问题很容易解决。这样,不需要先对数组进行排序(根本没有数组突变):

        var numbers = [20, 25, 30, 35, 5, 10, 15], x = 7;
        
        var output = numbers.reduce(function (prev, curr) {
          return Math.abs(curr - x) < Math.abs(prev - x) ? curr : prev
        });
        
        console.log(output);

        您可以使用 箭头函数(但在本例中使用 no IE support),只用一行 ES6 (ECMAScript 2015) 语法就可以更进一步解决它:

        const numbers = [20, 25, 30, 35, 5, 10, 15], x = 7;
        
        const output = numbers.reduce((prev, curr) => Math.abs(curr - x) < Math.abs(prev - x) ? curr : prev);
        
        console.log(output);

        当然,为了灵活性和可重用性,很容易把它做成一个函数:

        const closest = (array, goal) => array.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
        
        console.log(closest([20, 25, 30, 35, 5, 10, 15], 7));
        console.log(closest([20, 25, 30, 35, 5, 10, 15], 8));
        console.log(closest([1, 5, 7], -5));
        console.log(closest([1, 5, 7], 4));
        console.log(closest([1, 5, 7], 20));

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-08-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多