获取 CGFloat 最接近的值



我很好奇 swift 中是否有办法通过他们的现代 API 实现最接近的价值?

例如:

let x = [1.2, 3.4, 4.5, 6.7, 8.9]
print(x.getClosestValue(3.7) //3.4

我一直在玩地图和减少,但仍然无法解决这个问题。出现的问题是我还必须遍历整个数组以检测误报。在某些情况下,您可以有多个最接近的值,所以只是想知道如何快速完成此操作?

您可以使用min(by:)来实现此目的,并且不需要排序数组

let x = [1.2, 3.4, 4.5, 6.7, 8.9]
let target = 3.7
let closestTarget = x.min(by: {abs($0 - target) < abs($1 - target)})

我可以想象一些不同的场景,所以我将尝试解决其中的大部分。


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 作为答案(如果您对本主题感兴趣,可以查看此博客文章(。


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是一个函数,如果根据浮点算术将值ab视为相等,则返回true。有关更多信息,请参阅这篇文章 - 但请注意,您应该将 epsilon 调整到您认为合适的值。


这些解决方案的复杂性为 O(n(。

最后一点:如果你有一个已经排序的数组,如其他答案所述,你可以利用此属性使用二进制搜索找到你想要的东西。

这个使用reduce(_:_:)的解决方案应该可以工作:

let x = [1.2, 3.4, 4.5, 6.7, 8.9]
let target = 4.7
// assumes x is non-empty array
let closestTarget = x.reduce(x[0]) { closest,val in
abs(target - closest) > abs(target - val) ? val : closest
}

目前还没有来自Apple的直接API可以使用。 如果你不在乎时间,你可以对数组进行排序,并使用 Array 的first(where:)last(where:)方法来进行线性搜索。 但是,您可以通过排序和使用二进制搜索来使其更好,它可能只需要您额外的 20 行?

最新更新