【问题标题】:Get the most closest value of the CGFloat获取最接近的 CGFloat 值
【发布时间】:2020-03-20 22:44:40
【问题描述】:

我很好奇是否有办法通过他们的现代 api 快速实现最接近的价值?

例如:

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

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

【问题讨论】:

  • 列表总是排序的吗?
  • 是的,这个列表总是排序的。
  • 如果列表已排序,您可以使用我在回答中提到的方法,但您也可以使用分而治之的策略更有效地做到这一点,例如二分搜索。您想知道所有最接近的值还是一个最接近的值的范围?我可以提供更多关于如何实现这种分而治之的方法的细节——不过这需要几行代码。

标签: ios arrays swift swift3 swift2


【解决方案1】:

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

【讨论】:

  • sorted()+ first(where:) 有效,但它是 O(n*log(n))。只需使用min(by:)max(by:)就可以实现O(n),甚至不需要实现二分查找。当然,二分查找是最好的,实现O(log(n))(假设输入已经排序)
  • @Alexander-ReinstateMonica 为什么您可以假设您的输入已排序?我当然首先对其进行了排序,因为我不认为它已排序。大声笑。
  • min(by:)/max(by:) 不需要排序。您建议使用first(where:) 或二分搜索 需要排序。
  • @Alexander-ReinstateMonica 感谢您的关注。我不得不承认我的解决方案很糟糕。大声笑
【解决方案2】:

您可以使用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)})

【讨论】:

    【解决方案3】:

    这个使用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
    }
    

    【讨论】:

    【解决方案4】:

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


    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) -&gt; Bool 是一个函数,如果根据浮点运算将 ab 的值视为相等,则返回 true。有关更多信息,请参阅 this post - 但请注意,您应该将 epsilon 调整为您认为合适的值。


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

    最后一点:如果您有一个已经排序的数组,正如其他答案所提到的,您可以利用此属性使用二分搜索找到您想要的内容。

    【讨论】:

      猜你喜欢
      • 2022-01-25
      • 1970-01-01
      • 2020-11-18
      • 2016-07-07
      • 1970-01-01
      • 2012-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多