Подтвердить что ты не робот

Как найти/фильтровать элемент массива с наименьшей положительной разницей

Используя Swift, я пытаюсь настроить фильтр сложного массива или сортировать, и я застрял. Мой массив целых чисел может содержать от 1 до 7 элементов. Учитывая некоторое целочисленное значение (скажем, X), которого нет в массиве, я хотел бы найти элемент массива, который дает наименьшую разницу между собой и X.

4b9b3361

Ответ 1

В Swift 2 вы можете сделать это как "однострочный" с функциональным стилем программирование:

let numbers = [ 1, 3, 7, 11]
let x = 6

let closest = numbers.enumerate().minElement( { abs($0.1 - x) < abs($1.1 - x)} )!

print(closest.element) // 7 = the closest element
print(closest.index)   // 2 = index of the closest element

enumerate() выполняет итерацию по всем элементам массива вместе с соответствующий индекс, а minElement() возвращает "самый маленький" (index, element) по отношению к замыканию. Замыкание сравнивает абсолютные значения разности два элемента на x.

(Здесь предполагается, что массив не пуст, поэтому minElement() не возвращает nil.)

Обратите внимание, что это, вероятно, не самое быстрое решение для больших массивов, потому что абсолютные различия вычисляются дважды для (почти) всех элементы массива. Но для небольших массивов это не имеет значения.

Swift 3:

let numbers = [ 1, 3, 7, 11]
let x = 6
let closest = numbers.enumerated().min( by: { abs($0.1 - x) < abs($1.1 - x) } )!
print(closest.element) // 7
print(closest.offset) // 2

Версия Swift 1.2 находится в истории изменений.

Ответ 2

Другой альтернативой может быть использование метода reduce

let numbers = [1, 3, 7, 11]
let x = 11

let result = numbers.reduce(numbers.first!) { abs($1 - x) < abs($0 - x) ? $1 : $0 }

Если вы хотите избежать повторного вычисления абсолютов, вы можете сначала сопоставить каждый элемент массива с абсолютной разницей с входным номером, а затем найти минимум.

let numbers = [1, 3, 7, 11]
let x = 11

if let (index, _) = numbers.map({ abs($0 - x) }).enumerate().minElement({ $0.1 < $1.1 }) {
    let result = numbers[index]
}

Ответ 3

Swift 4.2

Не совсем то, что запрашивает OP, но вы можете использовать методы массива first(where:) и firstIndex(where:) в отсортированном массиве, чтобы получить первое значение больше x:

let numbers = [1, 3, 7, 11]

let x = 6
let index = numbers.firstIndex(where: { $0 >= x })!
let result = numbers.first(where: { $0 >= x })!

print(index, result) // 2 7

Ответ 4

Прокрутите значения в вашем массиве, вычислите разницу abs(), сравните его с текущей переменной currentSmallestDifference, если текущая разница меньше, чем эта, затем перезапишите ее новым значением, в то же время сохраняйте запись индекса массива...

int currentSmallestDifference = int.max(); //largest value defined by the int type, this way the first difference in the loop will never be larger than this 
int index = -1;   
for(int i = 0, i < array.size(), i++){
    if( abs(array(i) - X) < currentSmallestDifference){
        currentSmallestDifference = abs(array(i) - X);
        index = i;
    }
}

Это решает его в O (n).

Но если массив уже был отсортирован (O (nlog (n)), вы могли бы выполнить двоичный поиск O (log (n)) для X в массиве и найти для него самое близкое значение. нужно использовать abs() вообще в этом случае... просто сравнения

Но для размеров массивов с 1 по 7 алгоритмическая сложность на самом деле не является фактором.

Фактически для размера массива 1 вопрос не является даже фактором (?!?)

Обновление → Только что поняли, что название имеет наименьшую положительную разницу. Так что просто отбросьте все вещи abs() и убедитесь, что (массив (i) - X) находится в правильном знаке/направлении.

Ответ 5

Обернуто это расширение:

extension Sequence where Iterator.Element: SignedNumeric & Comparable {

    /// Finds the nearest (offset, element) to the specified element.
    func nearestOffsetAndElement(to toElement: Iterator.Element) -> (offset: Int, element: Iterator.Element) {

        guard let nearest = enumerated().min( by: {
            let left = $0.1 - toElement
            let right = $1.1 - toElement
            return abs(left) <= abs(right)
        } ) else {
            return (offset: 0, element: toElement)
        }

        return nearest
    }

    func nearestElement(to element: Iterator.Element) -> Iterator.Element {
        return nearestOffsetAndElement(to: element).element
    }

    func indexOfNearestElement(to element: Iterator.Element) -> Int {
        return nearestOffsetAndElement(to: element).offset
    }

}