我可以想象一些不同的场景,所以我会尝试解决其中的大部分。
1- 你只想找到一个数字:
1.1 - 查找实际数字:
你可以使用min(by:):
let x = [1.2, 4.0, 3.4, 6.7, 8.9]
let target = 3.7
let closestValue = x.min { abs($0 - target) < abs($1 - target) }
print(closestValue) // prints Optional(4.0)
这种方法是最直接的。您将得到返回数组元素和目标之间减法最小值的结果。
1.2 - 查找索引:
您也可以使用min(by:),但首先,您需要获取数组的枚举版本来获取索引。
let x = [1.2, 4.0, 3.4, 6.7, 8.9]
let target = 3.7
let closestIdx = x.enumerated().min { abs($0.1 - target) < abs($1.1 - target) }!.0
print(closestIdx) // prints 1
注意:尽管 3.4 与 3.7 和 4.0 的距离一样远,但由于浮点运算,这种方法将始终返回 4.0 作为答案(如果您有兴趣,可以查看 this blog post在本主题中)。
2- 你想找到所有最接近的数字:
既然您提到可以有多个数字,我认为这将是您选择的方法。
2.1 - 查找所有最接近的数字:
let x = [1.2, 3.4, 4.0, 6.7, 8.9]
let target = 3.7
let minDiff = x.map { return abs($0 - target) }.min()!
let closestValues = x.filter { isDoubleEqual(a: $0, b: target - minDiff) || isDoubleEqual(a: $0, b: target + minDiff) }
print(closestValues) // prints [3.4, 4.0]
这里的区别在于我们使用filter() 来查找与目标距离相等的所有值。可能存在重复值,如果您愿意,可以使用 Set 消除这些值。
2.2 - 查找所有最接近数字的索引:
再次使用enumerated() 获取索引的想法相同。
let x = [1.2, 3.4, 4.0, 6.7, 8.9]
let target = 3.7
let minDiff = x.map { return abs($0 - target) }.min()!
let tuples = x.enumerated().filter { isDoubleEqual(a: $0.1, b: target - minDiff) || isDoubleEqual(a: $0.1, b: target + minDiff) }
let closestIndices = tuples.map { return $0.0 }
print(closestIndices) // prints [1, 2]
注意: isDoubleEqual(a: Double, b: Double) -> Bool 是一个函数,如果根据浮点运算将 a 和 b 的值视为相等,则返回 true。有关更多信息,请参阅 this post - 但请注意,您应该将 epsilon 调整为您认为合适的值。
这些解决方案的复杂度为 O(n)。
最后一点:如果您有一个已经排序的数组,正如其他答案所提到的,您可以利用此属性使用二分搜索找到您想要的内容。