【发布时间】:2015-12-10 16:00:33
【问题描述】:
使用 Swift,我正在尝试锻炼一个复杂的数组过滤器或排序,但我被卡住了。我的整数数组可以包含 1 到 7 个元素。给定一个不在数组中的整数值(比如 X),我想找到它自己和 X 之间差异最小的数组元素。
【问题讨论】:
标签: arrays swift sorting filter
使用 Swift,我正在尝试锻炼一个复杂的数组过滤器或排序,但我被卡住了。我的整数数组可以包含 1 到 7 个元素。给定一个不在数组中的整数值(比如 X),我想找到它自己和 X 之间差异最小的数组元素。
【问题讨论】:
标签: arrays swift sorting filter
在 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 版本可以在编辑历史中找到。
【讨论】:
斯威夫特 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
【讨论】:
x = 4,那么result == 7不是最接近的数字
另一种选择是使用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]
}
【讨论】:
循环遍历数组中的值,计算 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)),那么您可以对数组中的 X 执行 O(log(n)) 二进制搜索并找到最接近它的值。在这种情况下根本不需要使用 abs() ......只是比较
但对于大小为 1 到 7 的数组,算法复杂度并不是真正的因素。
事实上,对于数组大小为 1,问题 甚至不是一个因素 (?!?)
更新>>刚刚意识到标题所说的最小积极差异..所以只需抛弃所有 abs() 东西并确保( array(i) - X) 的符号/方向正确。
【讨论】:
currentSmallestDifference 设为可选项比使用999999999 或其他常量要好。
Int.max 初始化。
Int.max,它不是伪代码,而是真正的 Swift 代码。它返回Int 类型的最大可能值。您的 int.max() 无法编译。
把它包装成一个扩展:
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
}
}
【讨论】:
这是一种计算最接近正值的单个循环的方法。不是单线。但仍然有效
let values = [1, 4, 9, 3, 8, 2, 11]
let x = 8
func closestPositiveValue(from array: [Int], value: Int) -> (Int, Int) { // -> (value, difference)
var (val, dif): (Int?, Int?) = (nil, nil)
for index in 0..<array.count {
if x <= array[index] {
var difference = x - array[index]
if difference < 0 {
difference = array[index] - x
}
if val == nil { // nil check for initial loop
(val, dif) = (array[index], difference)
} else {
if difference < dif! {
(val, dif) = (array[index], difference)
}
}
}
}
return (val ?? 0, dif ?? 0) // defaults to first item in array if there is no closest positive number
}
print(closestPositiveValue(from: values, value: x)) // prints (8, 0)
【讨论】:
以 Martin R 不久前的正确答案为基础,这是我一直在使用的一个方便的扩展。目前在 Swift 5 中。
extension Array where Element: (Comparable & SignedNumeric) {
func nearest(to value: Element) -> (offset: Int, element: Element)? {
self.enumerated().min(by: {
abs($0.element - value) < abs($1.element - value)
})
}
}
用法:
let numbers = [ 1, 3, 7, 11]
let x = 6
if let closest = numbers.nearest(to: x) {
print(closest)
}
// prints (offset: 2, element: 7)
【讨论】: