網站搜尋

javascript - 從陣列中獲取最接近的數字


我有一個從負1000到正1000的數字,並且我有一個裡面有數字的陣列。像這樣:

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

我希望我得到的數字能變成陣列中最接近的數字。

例如,我得到 80 作為數字,我希望它得到 82。

答案#1

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

答案#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>

它基本上使用括號法和檢查中間值的方法,在每次迭代中將解空間減少一半,是一種經典的 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

如前所述,對於小型數據集或對速度要求不極高的情況,這不應該有太大影響,但這是一個你可能想要考慮的選項。

答案#3

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/#curry 或 https://lodash.com/docs#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);
<script src="https://cdn.jsdelivr.net/npm/[email /lodash.min.js"></script>

答案#4

工作程式碼如下:

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

答案#5

適用於未排序的陣列

雖然這裡有一些不錯的解決方案,但 JavaScript 是一種靈活的語言,提供了多種工具來用不同方式解決問題。當然,這一切最終取決於你的程式風格。如果你的程式碼偏向函數式,你會發現 reduce 變化是適合的,例如:

  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

與其他使用 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)。

我已經創建了一個示範它的程式箱。

答案#6

所有的解決方案都設計得過於複雜。

它就像這樣簡單:

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

答案#7

針對已排序的陣列(線性搜尋)

到目前為止,所有的答案都集中在遍歷整個陣列。考慮到你的陣列已經排序,且你真正只想要最接近的數字,這可能是最簡單的(但不是最快的)解決方案:

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

請注意,這個演算法可以大幅改進,例如使用二元樹。

答案#8

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