Câu trả lời:
Phiên bản 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);
goal
để giảm bớt, bạn phải tham chiếu nó từ phạm vi toàn cầu.
Đây là mã giả có thể chuyển đổi thành bất kỳ ngôn ngữ thủ tục nào:
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
Nó chỉ đơn giản là tìm ra sự khác biệt tuyệt đối giữa số đã cho và từng phần tử mảng và trả lại cho bạn một trong những phần tử có sự khác biệt tối thiểu.
Đối với các giá trị mẫu:
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.
Để chứng minh khái niệm, đây là mã Python tôi đã sử dụng để thể hiện điều này trong hành động:
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)
Và, nếu bạn thực sự cần nó trong Javascript, hãy xem bên dưới để biết tệp HTML hoàn chỉnh thể hiện chức năng đang hoạt động:
<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>
Bây giờ hãy nhớ rằng có thể có phạm vi để cải thiện hiệu quả nếu, ví dụ, các mục dữ liệu của bạn được sắp xếp (có thể được suy ra từ dữ liệu mẫu nhưng bạn không nói rõ). Bạn có thể, ví dụ, sử dụng tìm kiếm nhị phân để tìm mục gần nhất.
Bạn cũng nên nhớ rằng, trừ khi bạn cần thực hiện nhiều lần trong một giây, các cải tiến hiệu quả sẽ hầu như không được chú ý trừ khi bộ dữ liệu của bạn nhận được nhiều lớn hơn .
Nếu bạn làm muốn thử nó như vậy (và có thể đảm bảo mảng được sắp xếp theo thứ tự tăng dần), đây là một tốt điểm bắt đầu:
<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>
Về cơ bản, nó sử dụng dấu ngoặc và kiểm tra giá trị trung bình để giảm một nửa dung lượng giải pháp cho mỗi lần lặp, một O(log N)
thuật toán cổ điển trong khi tìm kiếm tuần tự ở trên là 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
Như đã nói, điều đó không tạo ra nhiều sự khác biệt cho các bộ dữ liệu nhỏ hoặc cho những thứ không cần phải nhanh chóng, nhưng đó là một lựa chọn bạn có thể muốn xem xét.
Phiên bản ES6 (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);
Để sử dụng lại, bạn có thể sử dụng chức năng cà ri hỗ trợ giữ chỗ ( http://ramdajs.com/0.19.1/docs/#curry hoặc https://lodash.com/docs#curry ). Điều này mang lại rất nhiều sự linh hoạt tùy thuộc vào những gì bạn cần:
const getClosest = curry((counts, goal) => {
return counts
.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
const closestTo5 = getClosest(_, 5);
const closestTo = getClosest([4, 9, 15, 6, 2]);
Mã làm việc như dưới đây:
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));
Mặc dù có một số giải pháp tốt được đăng ở đây, JavaScript là một ngôn ngữ linh hoạt cung cấp cho chúng tôi các công cụ để giải quyết vấn đề theo nhiều cách khác nhau. Tất cả đều đến với phong cách của bạn, tất nhiên. Nếu mã của bạn có nhiều chức năng hơn, bạn sẽ thấy biến thể giảm phù hợp, nghĩa là:
arr.reduce(function (prev, curr) {
return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);
});
Tuy nhiên, một số có thể thấy rằng khó đọc, tùy thuộc vào phong cách mã hóa của họ. Vì vậy, tôi đề xuất một cách giải quyết vấn đề mới:
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
Trái với các cách tiếp cận khác tìm giá trị tối thiểu bằng cách sử dụng Math.min.apply
, cách này không yêu cầu arr
sắp xếp mảng đầu vào . Chúng ta không cần quan tâm đến các chỉ mục hoặc sắp xếp nó trước.
Tôi sẽ giải thích từng dòng mã cho rõ ràng:
arr.map(function(k) { return Math.abs(k - x) })
Tạo một mảng mới, về cơ bản lưu trữ các giá trị tuyệt đối của các số đã cho (số trong arr
) trừ đi số đầu vào ( x
). Chúng tôi sẽ tìm số nhỏ nhất tiếp theo (cũng là số gần nhất với số đầu vào)Math.min.apply(Math, indexArr)
Đây là một cách hợp pháp để tìm số nhỏ nhất trong mảng mà chúng ta vừa tạo trước đó (không có gì khác với nó)arr[indexArr.indexOf(min)]
Đây có lẽ là phần thú vị nhất. Chúng tôi đã tìm thấy số nhỏ nhất của mình, nhưng chúng tôi không chắc mình nên cộng hay trừ số ban đầu ( x
). Đó là bởi vì chúng tôi thường Math.abs()
tìm thấy sự khác biệt. Tuy nhiên, array.map
tạo (một cách hợp lý) một bản đồ của mảng đầu vào, giữ các chỉ mục ở cùng một vị trí. Do đó, để tìm ra số gần nhất, chúng ta chỉ cần trả về chỉ số của mức tối thiểu được tìm thấy trong mảng đã cho indexArr.indexOf(min)
.Tôi đã tạo ra một thùng chứng minh nó.
3n
và đó là ES5 mặc dù bạn trả lời vào năm 2016 và các giải pháp khác vẫn ổn ngay cả khi người này hỏi câu hỏi này rõ ràng không phải là lập trình viên vào thời điểm đó.
O(n)
giải pháp của bạn thực hiện ít hơn khoảng 100 nghìn ops / giây sau đó @paxdiablo O(log n)
trên các số ngẫu nhiên. Khi thiết kế một thuật toán luôn luôn sắp xếp đầu tiên họ nói. (Ngoại trừ nếu bạn biết những gì bạn đang làm và bạn đã có điểm chuẩn để sao lưu.)
const findClosest = goal => (a,b) => Math.abs(a - goal) < Math.abs(b - goal) ? a : b;
[2, 42, 82, 122, 162, 202, 242, 282, 322, 362].reduce(findClosest(80))
Tất cả các câu trả lời cho đến nay tập trung vào việc tìm kiếm trong toàn bộ mảng. Xem xét mảng của bạn đã được sắp xếp và bạn thực sự chỉ muốn số gần nhất, đây có lẽ là giải pháp nhanh nhất:
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));
Lưu ý rằng thuật toán có thể được cải thiện rất nhiều, ví dụ như sử dụng cây nhị phân.
a[i]
hoặc i[0]
.
Tất cả các giải pháp được thiết kế quá mức.
Nó đơn giản như:
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);
});
// 5
Giải pháp này sử dụng bộ định lượng tồn tại ES5 Array#some
, cho phép dừng lặp, nếu một điều kiện được đáp ứng.
Đối lập với Array#reduce
, nó không cần lặp lại tất cả các yếu tố cho một kết quả.
Trong cuộc gọi lại, một delta
giá trị tuyệt đối giữa giá trị tìm kiếm và thực tếitem
được lấy và so sánh với delta cuối cùng. Nếu lớn hơn hoặc bằng nhau, việc lặp lại dừng lại, bởi vì tất cả các giá trị khác với đồng bằng của chúng đều lớn hơn giá trị thực.
Nếu delta
trong cuộc gọi lại nhỏ hơn, thì mục thực tế được gán cho kết quả và delta
được lưu vào lastDelta
.
Cuối cùng, các giá trị nhỏ hơn với đồng bằng bằng nhau được lấy, như trong ví dụ dưới đây 22
, kết quả là 2
.
Nếu có mức độ ưu tiên của các giá trị lớn hơn, kiểm tra delta phải được thay đổi từ:
if (delta >= lastDelta) {
đến:
if (delta > lastDelta) {
// ^^^ without equal sign
Điều này sẽ nhận được với 22
, kết quả 42
(Ưu tiên của các giá trị lớn hơn).
Hàm này cần các giá trị được sắp xếp trong mảng.
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
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
Ví dụ:
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
Tôi không biết liệu tôi có nên trả lời một câu hỏi cũ không, nhưng vì bài đăng này xuất hiện đầu tiên trên các tìm kiếm của Google, tôi hy vọng rằng bạn sẽ tha thứ cho tôi khi thêm giải pháp của tôi & 2c của tôi vào đây.
Vì lười biếng, tôi không thể tin rằng giải pháp cho câu hỏi này sẽ là LOOP, vì vậy tôi đã tìm kiếm thêm một chút và quay lại với chức năng lọc :
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);
Đó là tất cả !
goog.math.clamp
(đóng cửa google) chỉ với các mảng và không quan tâm đến giới hạn dưới.
Câu trả lời của tôi cho một câu hỏi tương tự là kế toán cho các mối quan hệ và nó là trong Javascript đơn giản, mặc dù nó không sử dụng tìm kiếm nhị phân nên nó là O (N) chứ không phải 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);
Tôi thích cách tiếp cận từ Fusion, nhưng có một lỗi nhỏ trong đó. Như thế là đúng:
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];
}
Nó cũng nhanh hơn một chút vì nó sử dụng for
vòng lặp cải tiến .
Cuối cùng, tôi đã viết chức năng của mình như thế này:
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;
};
Tôi đã thử nghiệm nó console.time()
và nó hơi nhanh hơn các chức năng khác.
improved for loop
? Vòng lặp đảo ngược không phải lúc nào cũng là một cải tiến hiệu suất.
.length
chỉ đánh giá một lần, khi bạn khai báo i
, trong khi đối với vòng lặp này. Nhưng tôi nghĩ var i = arr.length;while (i--) {}
sẽ còn nhanh hơn nữa
while
. Bây giờ nó thậm chí còn nhanh hơn.
Đối với phạm vi nhỏ, điều đơn giản nhất là có một mảng bản đồ, trong đó, ví dụ, mục thứ 80 sẽ có giá trị 82 trong đó, để sử dụng ví dụ của bạn. Đối với một phạm vi rộng hơn, thưa thớt hơn, có lẽ cách để đi là tìm kiếm nhị phân.
Với ngôn ngữ truy vấn, bạn có thể truy vấn các giá trị ở một số khoảng cách hai bên của số đầu vào của bạn và sau đó sắp xếp qua danh sách rút gọn. Nhưng SQL không có khái niệm tốt về "tiếp theo" hoặc "trước đó", để cung cấp cho bạn một giải pháp "sạch".
Một biến thể khác ở đây chúng ta có phạm vi vòng tròn nối từ đầu đến chân và chỉ chấp nhận giá trị tối thiểu cho đầu vào đã cho. Điều này đã giúp tôi có được các giá trị mã char cho một trong các thuật toán mã hóa.
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;
});
}
#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();
}
Hiệu quả nhất sẽ là một tìm kiếm nhị phân. Tuy nhiên, ngay cả các giải pháp đơn giản cũng có thể thoát khi số tiếp theo khớp với số hiện tại . Gần như tất cả các giải pháp ở đây không tính đến việc mảng được sắp xếp và lặp đi lặp lại mặc dù toàn bộ điều: /
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);
Điều này có thể được chạy trên phi nguyên thủy quá, vd closest(data, 21, item => item.age)
Thay đổi find
để findIndex
trả về chỉ mục trong mảng.
Để tìm hai số gần nhất trong mảng
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));
Đây là đoạn mã để tìm phần tử gần nhất với một số từ một mảng trong Độ phức tạp O (nlog (n)): -
Đầu vào: - {1,60,0, -10,100,87,56} Phần tử: - 56 Số gần nhất trong mảng: - 60
Mã nguồn (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;
}
}
x
, đi qua từng mảng một, so sánhi
với số hiện tại trong mảng, nếu chênh lệch giữa nó vài
nhỏ hơn giá trị hiện tạix
, được đặt thànhx
số mảng hiện tại. Khi kết thúc,x
có số gần nhấti
từ mảng.