提问人:topgun 提问时间:11/25/2019 更新时间:11/26/2019 访问量:573
获取最接近的 CGFloat 值
Get the most closest value of the CGFloat
问:
我很好奇 swift 中是否有办法通过他们的现代 api 实现最接近的价值?
例如:
let x = [1.2, 3.4, 4.5, 6.7, 8.9]
print(x.getClosestValue(3.7) //3.4
我一直在玩map和reduce,但仍然无法解决这个问题。问题是我必须遍历整个数组才能检测误报。在某些情况下,您可以拥有多个最接近的值,所以只是想知道如何快速完成这项工作?
答:
目前还没有来自 Apple 的直接 api 可供使用。
如果你不关心时间,你可以对数组进行排序,并使用数组的或方法进行线性搜索。
但是,您可以通过排序和使用二叉搜索来使其变得更好,它可能只需要您额外的 20 行?first(where:)
last(where:)
评论
sorted()
+ first(where:)
有效,但它是.只需使用 、 即可实现,甚至不需要实现二进制搜索。当然,二叉搜索是最好的,实现(假设输入已经排序)O(n*log(n))
O(n)
min(by:)
max(by:)
O(log(n))
min(by:)
/max(by:)
不需要排序。您建议使用或二进制搜索将需要排序。first(where:)
您可以使用它来实现此目的,并且它不需要排序数组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)})
此解决方案使用应该有效: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
}
评论
reduce
max(by:)
我可以想象一些不同的场景,所以我将尝试解决其中的大部分。
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]
这里的区别在于,我们用来查找与目标相等距离的所有值。可能会有重复的值,如果您愿意,可以使用 Set 消除这些值。filter()
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]
注意:是一个函数,如果根据浮点运算,值 和 被认为是相等的,则返回。有关更多信息,请参阅此帖子 - 但请注意,您应该将 epsilon 调整到您认为合适的值。isDoubleEqual(a: Double, b: Double) -> Bool
true
a
b
这些解决方案的复杂度为 O(n)。
最后一点:如果你有一个已经排序的数组,正如其他答案所提到的,你可以利用这个属性,使用二进制搜索来找到你想要的东西。
评论