【问题标题】:Get the closest number out of an array从数组中获取最接近的数字
【发布时间】:2012-01-24 23:23:28
【问题描述】:

我有一个从负 1000 到正 1000 的数字,并且我有一个包含数字的数组。像这样:

[2, 42, 82, 122, 162, 202, 242, 282, 322, 362]

我希望我得到的数字更改为数组中最接近的数字。

例如,我得到80 作为数字,我希望它得到82

【问题讨论】:

  • 对数组稍作修改的二进制搜索就可以了。
  • 不可能很简单:留一个变量x,逐个遍历数组,比较i和数组中当前的数字,如果和i相差更小比x 中的当前值,将x 设置为当前数组编号。完成后,x 的数字最接近数组中的i

标签: javascript arrays


【解决方案1】:

对于一个小范围,最简单的事情是有一个映射数组,例如,第 80 个条目的值为 82,以使用您的示例。对于更大、更稀疏的范围,可能要走的路是二分搜索。

使用查询语言,您可以查询输入数字两侧某个距离的值,然后对生成的简化列表进行排序。但是 SQL 并没有很好的“下一个”或“上一个”的概念,给你一个“干净”的解决方案。

【讨论】:

    【解决方案2】:

    这是可以转换成任何程序语言的伪代码:

    array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
    number = 112
    print closest (number, array)
    
    def closest (num, arr):
        curr = arr[0]
        foreach val in arr:
            if abs (num - val) < abs (num - curr):
                curr = val
        return curr
    

    它只是计算给定数字和每个数组元素之间的绝对差异,并返回一个差异最小的元素。

    对于示例值:

    number = 112  112  112  112  112  112  112  112  112  112
    array  =   2   42   82  122  162  202  242  282  322  362
    diff   = 110   70   30   10   50   90  130  170  210  250
                             |
                             +-- one with minimal absolute difference.
    

    作为概念证明,下面是我用来展示这一点的 Python 代码:

    def closest (num, arr):
        curr = arr[0]
        for index in range (len (arr)):
            if abs (num - arr[index]) < abs (num - curr):
                curr = arr[index]
        return curr
    
    array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
    number = 112
    print closest (number, array)
    

    而且,如果您真的在 Javascript 中需要它,请参阅下面的完整 HTML 文件,该文件演示了该功能的实际作用:

    <html>
        <head></head>
        <body>
            <script language="javascript">
                function closest (num, arr) {
                    var curr = arr[0];
                    var diff = Math.abs (num - curr);
                    for (var val = 0; val < arr.length; val++) {
                        var newdiff = Math.abs (num - arr[val]);
                        if (newdiff < diff) {
                            diff = newdiff;
                            curr = arr[val];
                        }
                    }
                    return curr;
                }
                array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
                number = 112;
                alert (closest (number, array));
            </script>
        </body>
    </html>
    

    现在请记住,例如,如果您的数据项已排序(可以从示例数据中推断出来,但您没有明确说明),则可能存在提高效率的空间。例如,您可以使用二进制搜索来查找最近的项目。

    您还应该记住,除非您需要每秒执行 很多次 次,否则效率提升几乎不会引起注意,除非您的数据集变得大大 更大.

    如果您确实想尝试这种方式(并且可以保证数组按升序排序),这是一个很好的起点:

    <html>
        <head></head>
        <body>
            <script language="javascript">
                function closest (num, arr) {
                    var mid;
                    var lo = 0;
                    var hi = arr.length - 1;
                    while (hi - lo > 1) {
                        mid = Math.floor ((lo + hi) / 2);
                        if (arr[mid] < num) {
                            lo = mid;
                        } else {
                            hi = mid;
                        }
                    }
                    if (num - arr[lo] <= arr[hi] - num) {
                        return arr[lo];
                    }
                    return arr[hi];
                }
                array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
                number = 112;
                alert (closest (number, array));
            </script>
        </body>
    </html>
    

    它基本上使用bracketing 并检查中间值以将每次迭代的解空间减少一半,这是一个经典的O(log N) 算法,而上面的顺序搜索是O(N)

    0  1  2   3   4   5   6   7   8   9  <- indexes
    2 42 82 122 162 202 242 282 322 362  <- values
    L             M                   H  L=0, H=9, M=4, 162 higher, H<-M
    L     M       H                      L=0, H=4, M=2, 82 lower/equal, L<-M
          L   M   H                      L=2, H=4, M=3, 122 higher, H<-M
          L   H                          L=2, H=3, difference of 1 so exit
              ^
              |
              H (122-112=10) is closer than L (112-82=30) so choose H
    

    如前所述,对于小型数据集或不需要 快得令人眼花缭乱的事物而言,这应该不会产生太大影响,但您可能需要考虑这种选择。 p>

    【讨论】:

    • @micha,我已将等效的 JS 代码添加到答案中,只需要一段时间就可以将语言从我更习惯的语言更改为 :-)
    • 如果您有大型数据集,此算法的运行时间会很差。
    • @ylun.ca,由于数据排序的问题中没有explicit语句(示例已排序但可能是巧合),你无法得到比 O(n) 更好的效率。无论如何,对于这么大的数据集,效率几乎是无关紧要的。但是您的观点是有效的,因此我会为此添加注释,以希望使答案更加完整。
    • 谢谢,我希望我能多次投票!关于堆栈溢出的更多答案应该投入这种努力。
    • 你为这个答案付出的努力太棒了,非常感谢你提供这些图表,它让你更容易理解
    【解决方案3】:

    工作代码如下:

    var array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
    
    function closest(array, num) {
      var i = 0;
      var minDiff = 1000;
      var ans;
      for (i in array) {
        var m = Math.abs(num - array[i]);
        if (m < minDiff) {
          minDiff = m;
          ans = array[i];
        }
      }
      return ans;
    }
    console.log(closest(array, 88));

    【讨论】:

    • 我认为这是一个更好的解决方案,因为它只使用 JavaScript。接受的答案使用 jQuery,原始问题中未提及,其他查看此问题的人可能未使用。
    • 如果您在数组[1, 2, 3] 中搜索与5000 最接近的数字,您会大吃一惊。
    【解决方案4】:

    ES5 版本:

    var counts = [4, 9, 15, 6, 2],
      goal = 5;
    
    var closest = counts.reduce(function(prev, curr) {
      return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
    });
    
    console.log(closest);

    【讨论】:

    • 缺点是它只有在 reduce 的回调被从与声明的变量相同的范围内调用时才有效。由于不能通过goal来reduce,所以必须从全局范围内引用它。
    • 也可以使用高阶函数来做到这一点。
    • @7yl4r 还是将其包装在一个函数中? ;)
    • @7yl4r 不是真的...你可以使用 bind 来实现这个... ---------- // reducer.js function reducer(goal, prev, curr) { return (Math.abs(curr - 目标)
    • 这会迭代每个项目,如果列表是有序的,则不是最优的,但对于小列表来说没问题。即使没有二进制搜索,如果下一个数字更远,循环也可能退出。
    【解决方案5】:

    我不知道我是否应该回答一个老问题,但由于这篇文章首先出现在 Google 搜索中,我希望你能原谅我在这里添加我的解决方案和我的 2c。

    因为懒惰,我不敢相信这个问题的解决方案会是一个循环,所以我搜索了更多并返回filter function

    var myArray = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
    var myValue = 80;
    
    function BiggerThan(inArray) {
      return inArray > myValue;
    }
    
    var arrBiggerElements = myArray.filter(BiggerThan);
    var nextElement = Math.min.apply(null, arrBiggerElements);
    alert(nextElement);
    

    就是这样!

    【讨论】:

    • 那么下限是什么?您总是使用下一个更高的值。但是你的方法让我想起了goog.math.clamp(谷歌闭包),只使用数组而不关心下限。
    • 您的解决方案从数组中返回下一个更大的数字。既没有找到最接近的值,也没有找到确切的值。
    【解决方案6】:

    对于排序数组(线性搜索)

    到目前为止,所有答案都集中在搜索整个数组上。 考虑到您的数组已经排序并且您真的只想要最接近的数字,这可能是最简单(但不是最快)的解决方案:

    var a = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
    var target = 90000;
    
    /**
     * Returns the closest number from a sorted array.
     **/
    function closest(arr, target) {
      if (!(arr) || arr.length == 0)
        return null;
      if (arr.length == 1)
        return arr[0];
    
      for (var i = 1; i < arr.length; i++) {
        // As soon as a number bigger than target is found, return the previous or current
        // number depending on which has smaller difference to the target.
        if (arr[i] > target) {
          var p = arr[i - 1];
          var c = arr[i]
          return Math.abs(p - target) < Math.abs(c - target) ? p : c;
        }
      }
      // No number in array is bigger so return the last.
      return arr[arr.length - 1];
    }
    
    // Trying it out
    console.log(closest(a, target));

    请注意,该算法可以大大改进,例如使用二叉树。

    【讨论】:

    • 虽然这里的策略很好,但这里有几个错别字。例如,a[i]i[0]
    • 谢谢,@WesleyWorkman。刚刚修好它们。我希望我得到了一切。顺便说一句,您还可以编辑其他人的答案。
    • 我需要在上面回答的代码中进行更正以获得更高的最接近值?示例:[110, 111, 120, 140, 148, 149, 155, 177, 188, 190] 如果我搜索 150,我应该得到 155 而不是 149。我试过但发现解决这个问题有困难。能否请你帮忙。谢谢
    • 你说这可能是最快的,然后说能不能大大提高。
    【解决方案7】:

    我对类似问题的回答也是考虑到关系,它是纯 Javascript,虽然它不使用二进制搜索,所以它是 O(N) 而不是 O(logN):

    var searchArray= [0, 30, 60, 90];
    var element= 33;
    
    function findClosest(array,elem){
        var minDelta = null;
        var minIndex = null;
        for (var i = 0 ; i<array.length; i++){
            var delta = Math.abs(array[i]-elem);
            if (minDelta == null || delta < minDelta){
                minDelta = delta;
                minIndex = i;
            }
            //if it is a tie return an array of both values
            else if (delta == minDelta) {
                return [array[minIndex],array[i]];
            }//if it has already found the closest value
            else {
                return array[i-1];
            }
    
        }
        return array[minIndex];
    }
    var closest = findClosest(searchArray,element);
    

    https://stackoverflow.com/a/26429528/986160

    【讨论】:

      【解决方案8】:

      ES6 (ECMAScript 2015) 版本:

      const counts = [4, 9, 15, 6, 2];
      const goal = 5;
      
      const output = counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
      
      console.log(output);

      为了可重用性,您可以包装一个支持占位符(http://ramdajs.com/0.19.1/docs/#curryhttps://lodash.com/docs#curry)的 curry 函数。根据您的需要,这提供了很大的灵活性:

      const getClosest = _.curry((counts, goal) => {
        return counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
      });
      
      const closestToFive = getClosest(_, 5);
      const output = closestToFive([4, 9, 15, 6, 2]);
      
      console.log(output);
      &lt;script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"&gt;&lt;/script&gt;

      【讨论】:

        【解决方案9】:

        我喜欢 Fusion 的方法,但其中有一个小错误。就像它是正确的:

            function closest(array, number) {
                var num = 0;
                for (var i = array.length - 1; i >= 0; i--) {
                    if(Math.abs(number - array[i]) < Math.abs(number - array[num])){
                        num = i;
                    }
                }
                return array[num];
            }
        

        它也快了一点,因为它使用了改进的for 循环。

        最后我这样写了我的函数:

            var getClosest = function(number, array) {
                var current = array[0];
                var difference = Math.abs(number - current);
                var index = array.length;
                while (index--) {
                    var newDifference = Math.abs(number - array[index]);
                    if (newDifference < difference) {
                        difference = newDifference;
                        current = array[index];
                    }
                }
                return current;
            };
        

        我用console.time()测试过,比其他功能稍快。

        【讨论】:

        • 嘿,你能解释一下为什么它是improved for loop吗?反向循环并不总是能提高性能。
        • 当您声明 i 时,您只评估一次 .length,而对于此循环。但我认为var i = arr.length;while (i--) {} 会更快
        • 我更新了我的答案。我将其更改为while。现在速度更快了。
        【解决方案10】:

        适用于未排序的数组

        虽然这里发布了一些很好的解决方案,但 JavaScript 是一种灵活的语言,它为我们提供了以多种不同方式解决问题的工具。 当然,这一切都取决于你的风格。如果您的代码更实用,您会发现 reduce variation 合适,即:

          arr.reduce(function (prev, curr) {
            return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
          });
        

        但是,根据他们的编码风格,有些人可能会觉得这很难阅读。因此我提出了一种解决问题的新方法:

          var findClosest = function (x, arr) {
            var indexArr = arr.map(function(k) { return Math.abs(k - x) })
            var min = Math.min.apply(Math, indexArr)
            return arr[indexArr.indexOf(min)]
          }
        
          findClosest(80, [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]) // Outputs 82
        

        other approaches 使用 Math.min.apply 查找最小值相反,这不需要对输入数组 arr 进行排序。我们不需要关心索引或预先对其进行排序。

        为了清楚起见,我将逐行解释代码:

        1. arr.map(function(k) { return Math.abs(k - x) }) 创建一个新数组,本质上存储给定数字(arr 中的数字)减去输入数字 (x) 的绝对值。接下来我们将寻找最小的数字(这也是最接近输入的数字)
        2. Math.min.apply(Math, indexArr) 这是在我们刚刚创建的数组中找到最小数字的合法方法(仅此而已)
        3. arr[indexArr.indexOf(min)] 这可能是最有趣的部分。我们找到了最小的数字,但我们不确定是否应该添加或减去初始数字 (x)。那是因为我们使用Math.abs() 来查找差异。但是,array.map 创建(逻辑上)输入数组的映射,将索引保持在同一位置。因此,要找出最接近的数字,我们只需返回给定数组 indexArr.indexOf(min) 中找到的最小值的索引。

        我创建了一个 bin 来演示它。

        【讨论】:

        • 我不知道,但我想有人可能会对性能产生疑问,因为这实际上是 3n 并且它是 ES5,即使您在 2016 年回答,其他解决方案也很好,即使这个菜鸟问这个问题question 当时显然不是程序员。
        • 是的,很高兴看到你学习!顺便说一句,我只谈到了性能问题,并不是说它实际上表现更差,但我为你运行了这些数字,你的 O(n) 解决方案在随机数上的运行速度比 @paxdiablo O(log n) 少约 100k ops/sec。他们说,在设计算法时,总是先排序。 (除非您知道自己在做什么,并且有基准支持。)
        • 简单解决方案的道具。非常适合我的用例(我没有像 noob 那样拥有预排序数组的奢侈。)
        • 您可以让 findClosest 返回一个 reduce 回调函数,使其可在所有数组上重用:const findClosest = goal =&gt; (a,b) =&gt; Math.abs(a - goal) &lt; Math.abs(b - goal) ? a : b;
        • 示例: [2, 42, 82, 122, 162, 202, 242, 282, 322, 362].reduce(findClosest(80))
        【解决方案11】:

        此解决方案使用 ES5 existential quantifier Array#some,如果满足条件,则允许停止迭代。

        Array#reduce相反,它不需要为一个结果迭代所有元素。

        在回调内部,搜索值和实际item 之间的绝对值delta 被获取并与最后一个增量进行比较。如果大于或等于,则迭代停止,因为所有其他值及其增量都大于实际值。

        如果回调中的delta较小,则将实际项分配给结果,并将delta保存在lastDelta中。

        最后,采用具有相同增量的较小值,例如下面的22 示例,结果为2

        如果存在较大值的优先级,则增量检查必须从以下位置更改:

        if (delta >= lastDelta) {
        

        到:

        if (delta > lastDelta) {
        //       ^^^ without equal sign
        

        这将得到22,结果42(较大值的优先级)。

        此函数需要数组中的排序值。


        优先级较小的代码:

        function closestValue(array, value) {
            var result,
                lastDelta;
        
            array.some(function (item) {
                var delta = Math.abs(value - item);
                if (delta >= lastDelta) {
                    return true;
                }
                result = item;
                lastDelta = delta;
            });
            return result;
        }
        
        var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
        
        console.log(21, closestValue(data, 21)); // 2
        console.log(22, closestValue(data, 22)); // 2  smaller value
        console.log(23, closestValue(data, 23)); // 42
        console.log(80, closestValue(data, 80)); // 82

        优先级较大的代码:

        function closestValue(array, value) {
            var result,
                lastDelta;
        
            array.some(function (item) {
                var delta = Math.abs(value - item);
                if (delta > lastDelta) {
                    return true;
                }
                result = item;
                lastDelta = delta;
            });
            return result;
        }
        
        var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
        
        console.log(21, closestValue(data, 21)); //  2
        console.log(22, closestValue(data, 22)); // 42 greater value
        console.log(23, closestValue(data, 23)); // 42
        console.log(80, closestValue(data, 80)); // 82

        【讨论】:

        • 所以你假设给定的数组是排序的......这样可以节省你很多时间。
        • 操作的数组看起来是排序的,所以是的:)
        • 第一个解决方案在两次包含相同数字的输入上中断。例如closestValue([ 2, 2, 42, 80 ], 50) === 2
        • @SébastienVercammen,op 的数据是唯一且已排序的。
        • @NinaScholz OP 仅确定 “我有一个包含数字的数组”“我希望将我得到的数字更改为最接近的数字数组”。示例数组只是一个示例。数组不保证唯一的条目。
        【解决方案12】:

        这里的另一个变体是圆形范围连接头到脚,并且只接受给定输入的最小值。这帮助我获得了其中一种加密算法的字符代码值。

        function closestNumberInCircularRange(codes, charCode) {
          return codes.reduce((p_code, c_code)=>{
            if(((Math.abs(p_code-charCode) > Math.abs(c_code-charCode)) || p_code > charCode) && c_code < charCode){
              return c_code;
            }else if(p_code < charCode){
              return p_code;
            }else if(p_code > charCode && c_code > charCode){
              return Math.max.apply(Math, [p_code, c_code]);
            }
            return p_code;
          });
        }
        

        【讨论】:

          【解决方案13】:

          这里是代码 sn-p 从复杂度 O(nlog(n)) 中的数组中找到最接近数字的元素:-

          输入:- {1,60,0,-10,100,87,56} 元素:- 56 数组中最接近的数字:- 60

          源代码(Java):

          package com.algo.closestnumberinarray;
          import java.util.TreeMap;
          
          
          public class Find_Closest_Number_In_Array {
          
              public static void main(String arsg[]) {
                  int array[] = { 1, 60, 0, -10, 100, 87, 69 };
                  int number = 56;
                  int num = getClosestNumber(array, number);
                  System.out.println("Number is=" + num);
              }
          
              public static int getClosestNumber(int[] array, int number) {
                  int diff[] = new int[array.length];
          
                  TreeMap<Integer, Integer> keyVal = new TreeMap<Integer, Integer>();
                  for (int i = 0; i < array.length; i++) {
          
                      if (array[i] > number) {
                          diff[i] = array[i] - number;
                          keyVal.put(diff[i], array[i]);
                      } else {
                          diff[i] = number - array[i];
                          keyVal.put(diff[i], array[i]);
                      }
          
                  }
          
                  int closestKey = keyVal.firstKey();
                  int closestVal = keyVal.get(closestKey);
          
                  return closestVal;
              }
          }
          

          【讨论】:

          • 帖子要求在标签中使用 javascript
          【解决方案14】:

          所有的解决方案都被过度设计了。

          就这么简单:

          const needle = 5;
          const haystack = [1, 2, 3, 4, 5, 6, 7, 8, 9];
          
          haystack.sort((a, b) => {
            return Math.abs(a - needle) - Math.abs(b - needle);
          })[0];
          
          // 5
          

          【讨论】:

          • 这根本没有效率
          • 我需要在 20k+ 数字列表中找到最接近的数字,而且我可能需要经常这样做(可能每次用户得分时)。我还需要在两个维度上做,所以在两个很长的列表中两个最接近。 “过度设计”是相对于需求而言的。我发现这里的一切都设计不足。
          【解决方案15】:
          #include <algorithm>
          #include <iostream>
          #include <cmath>
          
          using namespace std;
          
          class CompareFunctor
          {
          
          public:
              CompareFunctor(int n) { _n = n; }
              bool operator()(int & val1, int & val2)
              {
                  int diff1 = abs(val1 - _n);
                  int diff2 = abs(val2 - _n);
                  return (diff1 < diff2);
              }
          
          private:
              int _n;
          };
          
          int Find_Closest_Value(int nums[], int size, int n)
          {
              CompareFunctor cf(n);
              int cn = *min_element(nums, nums + size, cf);
              return cn;
          }
          
          int main()
          {
              int nums[] = { 2, 42, 82, 122, 162, 202, 242, 282, 322, 362 };
              int size = sizeof(nums) / sizeof(int);
              int n = 80;
              int cn = Find_Closest_Value(nums, size, n);
              cout << "\nClosest value = " << cn << endl;
              cin.get();
          }
          

          【讨论】:

            【解决方案16】:

            ES6

            适用于已排序和未排序的数组

            数字整数和浮点数,欢迎字符串

            /**
             * Finds the nearest value in an array of numbers.
             * Example: nearestValue(array, 42)
             * 
             * @param {Array<number>} arr
             * @param {number} val the ideal value for which the nearest or equal should be found
             */
            const nearestValue = (arr, val) => arr.reduce((p, n) => (Math.abs(p) > Math.abs(n - val) ? n - val : p), Infinity) + val
            
            

            例子:

            let values = [1,2,3,4,5]
            console.log(nearestValue(values, 10)) // --> 5
            console.log(nearestValue(values, 0)) // --> 1
            console.log(nearestValue(values, 2.5)) // --> 2
            
            values = [100,5,90,56]
            console.log(nearestValue(values, 42)) // --> 56
            
            values = ['100','5','90','56']
            console.log(nearestValue(values, 42)) // --> 56
            
            

            【讨论】:

              【解决方案17】:

              最有效的方法是二分查找。然而,当下一个数字与当前数字进一步匹配时,即使是简单的解决方案也可能退出。这里几乎所有的解决方案都没有考虑到数组是有序的并且遍历整个事情:/

              const closest = (orderedArray, value, valueGetter = item => item) =>
                orderedArray.find((item, i) =>
                  i === orderedArray.length - 1 ||
                  Math.abs(value - valueGetter(item)) < Math.abs(value - valueGetter(orderedArray[i + 1])));
              
              var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
              
              console.log('21 -> 2', closest(data, 21) === 2);
              console.log('22 -> 42', closest(data, 22) === 42); // equidistant between 2 and 42, select highest
              console.log('23 -> 42', closest(data, 23) === 42);
              console.log('80 -> 82', closest(data, 80) === 82);

              这也可以在非基元上运行,例如closest(data, 21, item =&gt; item.age)

              find 更改为findIndex 以返回数组中的索引。

              【讨论】:

              • 无序数组呢?
              • 对于无序,采用不退出的解决方案之一,但是如果您多次执行该工作,那么对数组进行一次排序可能会更理想。如前所述,如果数组真的很大,那么二进制搜索将比线性搜索更有效。
              • 如果您有一个包含 1.000.000 个对象的数组,则二进制搜索可以在 19 次比较中找到任何数字。但是,这里介绍的解决方案平均需要 500.000,最多需要 1.000.000。是的,这是一个性能调整,但我不认为代码的复杂性证明它是合理的。 - 话虽如此,我怀疑Math.abs 在您的解决方案中是否真的必要。如果有的话,我认为它会破坏您对同时具有正数和负数的数组的功能。
              【解决方案18】:

              在数组中查找两个最接近的数

              function findTwoClosest(givenList, goal) {
                var first;
                var second;
                var finalCollection = [givenList[0], givenList[1]];
                givenList.forEach((item, firtIndex) => {
                  first = item;
              
                  for (let i = firtIndex + 1; i < givenList.length; i++) {
                    second = givenList[i];
              
                    if (first + second < goal) {
                      if (first + second > finalCollection[0] + finalCollection[1]) {
                        finalCollection = [first, second];
                      }
                    }
                  }
                });
              
                return finalCollection;
              }
              
              var counts = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
              var goal = 80;
              console.log(findTwoClosest(counts, goal));
              

              【讨论】:

              • 一句 cmets 就好了。例如,您对“2 个最接近的数字”的定义是什么。这可能意味着不同的东西。您指的是围绕goal 的两个最接近的数字。还是您真的指的是偏差最小的 2 个数字?假设你的意思是后者,我想知道你为什么会想要其中的两个。我的意思是,在像[0, 1, 2, 33, 35] 这样的数组中,当搜索30 时,你想要[33, 35] 吗?您是否打算稍后对它们进行平均,得到34
              【解决方案19】:

              具有 O(n) 时间复杂度的更简单方法是在数组的一次迭代中执行此操作。此方法适用于未排序的数组。

              以下是一个javascript示例,这里从数组中找到最接近“58”的数字。

              var inputArr = [150, 5, 200, 50, 30];
              var search = 58;
              var min = Math.min();
              var result = 0;
              for(i=0;i<inputArr.length;i++) {
                let absVal = Math.abs(search - inputArr[i])
                if(min > absVal) {
                  min=absVal;
                  result = inputArr[i];
                }
              }
              console.log(result); //expected output 50 if input is 58

              这也适用于十进制数字。

              Math.min() 将返回 Infinity

              result 将存储最接近搜索元素的值。

              【讨论】:

                【解决方案20】:

                其他答案表明您需要遍历整个数组

                • 计算每个元素的偏差
                • 跟踪最小偏差及其元素
                • 最后,在遍历整个数组后,返回具有最小偏差的元素。

                如果数组已经排序,那没有意义。无需计算所有偏差。例如在 100 万个元素的有序集合中,您只需要计算大约 19 个偏差(最多)即可找到您的匹配项。您可以使用 二分搜索 方法完成此操作:

                function findClosestIndex(arr, element) {
                    let from = 0, until = arr.length - 1
                    while (true) {
                        const cursor = Math.floor((from + until) / 2);
                        if (cursor === from) {
                            const diff1 = element - arr[from];
                            const diff2 = arr[until] - element;
                            return diff1 <= diff2 ? from : until;
                        }
                
                        const found = arr[cursor];
                        if (found === element) return cursor;
                
                        if (found > element) {
                            until = cursor;
                        } else if (found < element) {
                            from = cursor;
                        }
                    }
                }
                

                结果:

                console.log(findClosestIndex([0, 1, 2, 3.5, 4.5, 5], 4));
                // output: 3
                
                console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 4));
                // output: 4
                
                console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 90));
                // output: 5
                
                console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], -1));
                // output: 0
                

                【讨论】:

                • 很想知道为什么这被否决了; - 我试图改进答案,解释为什么这是一个更好的答案。
                【解决方案21】:

                如果数组像您的示例一样排序,您可以使用 二分搜索 以获得更好的 O(log n) 时间复杂度。

                const myArray = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
                
                const binaryClosestIdx = (arr, target) => {
                    let start = 0;
                    let end = arr.length - 1;
                    let mid = Math.floor((start + end) / 2);
                
                    while (1) {
                        if (arr[mid] === target) {
                            return mid;
                        }
                        else if (start >= end) {
                            break;
                        }
                        else if (arr[mid] > target) {
                            end = mid - 1;
                        } else {
                            start = mid + 1;
                        }
                        
                        mid = Math.floor((start + end) / 2);
                    }
                
                    // Return the closest between the last value checked and it's surrounding neighbors
                    const first = Math.max(mid - 1, 0);
                    const neighbors = arr.slice(first, mid + 2);
                    const best = neighbors.reduce((b, el) => Math.abs(el - target) < Math.abs(b - target) ? el : b);
                
                    return first + neighbors.indexOf(best);
                }
                
                const closestValue = myArray[binaryClosestIdx(myArray, 80)];
                console.log(closestValue);

                工作原理:

                1. 它将目标值与数组的中间元素进行比较。如果中间元素更大,我们可以忽略它之后的每个元素,因为它们会更大。如果中间元素更小也是如此,我们可以忽略它之前的每个元素。

                2. 如果找到目标值,我们将其返回,否则我们将最后测试的值与其周围的邻居进行比较,因为最接近的值只能在这 3 个值之间。

                【讨论】:

                  【解决方案22】:

                  您可以使用以下逻辑来查找最接近的数字,而无需使用 reduce 函数

                  let arr = [0, 80, 10, 60, 20, 50, 0, 100, 80, 70, 1];
                  const n = 2;
                  let closest = -1;
                  let closeDiff = -1;
                  
                  for (let i = 0; i < arr.length; i++) {
                    if (Math.abs(arr[i] - n) < closeDiff || closest === -1) {
                      closeDiff = Math.abs(arr[i] - n);
                      closest = arr[i];
                    }
                  }
                  console.log(closest);
                  

                  【讨论】:

                  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
                  猜你喜欢
                  • 1970-01-01
                  • 2012-10-30
                  • 1970-01-01
                  • 2019-06-30
                  • 1970-01-01
                  • 1970-01-01
                  • 2023-03-20
                  • 2022-01-16
                  相关资源
                  最近更新 更多