简体   繁体   English

获取最接近的 CGFloat 值

[英]Get the most closest value of the CGFloat

I am curious if there is a way in swift to achieve the most closest value via their modern api's?我很好奇是否有一种方法可以通过他们的现代 api 快速实现最接近的价值?

For example:例如:

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

I have been playing around with map and reduce but still not be able to crack this problem.我一直在玩地图和减少但仍然无法解决这个问题。 The problem occur's that I have to iterate through entire array to detect false positives as well.出现的问题是我必须遍历整个数组以检测误报。 There can be a scenario where you can have multiple closest values, so was just wondering how this can be done in swiftly way?在某些情况下,您可以拥有多个最接近的值,所以只是想知道如何快速完成?

You can use min(by:) to achieve this and it doesn't require a sorted array您可以使用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)})

I can imagine some different scenarios, so I will try to address most of them.我可以想象一些不同的场景,所以我将尝试解决其中的大部分问题。


1- You want to find only one number: 1- 您只想找到一个数字:

1.1 - Finding the actual number: 1.1 - 查找实际数字:

You can use min(by:) :您可以使用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)

This approach is the most straightforward.这种方法是最直接的。 You will get the result that returns the minimum value of the subtraction between the array element and the target.您将得到返回数组元素与目标之间相减的最小值的结果。

1.2 - Finding the index: 1.2 - 查找索引:

You can also use min(by:) , but first, you get the enumerated version of the array to get the indices.您也可以使用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

Note: Even though 3.4 is equally distant to 3.7 as 4.0, this approach will always return 4.0 as the answer because of floating-point arithmetic (you can check this blog post if you're interested in this topic).注意:尽管 3.4 与 3.7 的距离与 4.0 相同,但由于浮点运算,此方法将始终返回 4.0 作为答案(如果您对此主题感兴趣,可以查看此博客文章)。


2- You want to find all the closest numbers: 2- 你想找到所有最接近的数字:

Since you've mentioned that there can be multiple numbers, I think this would be the method of your choice.既然您提到可以有多个数字,我认为这将是您选择的方法。

2.1 - Finding all closest numbers: 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]

The difference here is that we use filter() to find all the values that are equally distant to a target.这里的区别在于我们使用filter()来查找与目标等距的所有值。 There may be repeated values, which can be eliminated using a Set if you wish.可能存在重复值,如果您愿意,可以使用 Set 消除这些值。

2.2 - Finding the indices of all closest numbers: 2.2 - 找到所有最接近数字的索引:

The same idea of using enumerated() again to take the indices.再次使用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]

Note: isDoubleEqual(a: Double, b: Double) -> Bool is a function that returns true if the values a and b are considered equal according to floating-point arithmetic.注意: isDoubleEqual(a: Double, b: Double) -> Bool是一个函数,如果根据浮点算法认为值ab相等,则返回true See this post for more information - but note that you should adjust the epsilon to a value that you find suitable.有关更多信息,请参阅此帖子- 但请注意,您应该将 epsilon 调整为您认为合适的值。


The complexity of these solutions is O(n).这些解决方案的复杂度是 O(n)。

A final note: if you have an array that is already sorted, as mentioned by other answers, you can take advantage of this property to find what you want using a binary search.最后一点:如果您有一个已经排序的数组,如其他答案所述,您可以利用此属性使用二分搜索找到您想要的内容。

This solution using reduce(_:_:) should work:这个使用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
}

There is no straight api from Apple to use yet.目前还没有来自 Apple 的直接 API 可供使用。 If you don't care about the time, you can sort the array and use Array's first(where:) or last(where:) methods to do linear search.如果你不关心时间,你可以对数组进行排序,并使用 Array 的first(where:)last(where:)方法进行线性搜索。 However, you can make it better by sort and using binary search, it will probably just take you additional 20 lines?但是,您可以通过排序和使用二分搜索使其更好,它可能只需要额外的 20 行?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM