【问题标题】:how to find/filter the array element with the smallest positive difference如何找到/过滤具有最小正差的数组元素
【发布时间】:2015-12-10 16:00:33
【问题描述】:

使用 Swift,我正在尝试锻炼一个复杂的数组过滤器或排序,但我被卡住了。我的整数数组可以包含 1 到 7 个元素。给定一个不在数组中的整数值(比如 X),我想找到它自己和 X 之间差异最小的数组元素。

【问题讨论】:

    标签: arrays swift sorting filter


    【解决方案1】:

    Swift 2 中,您可以将其作为具有功能样式的“单线” 编程:

    let numbers = [ 1, 3, 7, 11]
    let x = 6
    
    let closest = numbers.enumerate().minElement( { abs($0.1 - x) < abs($1.1 - x)} )!
    
    print(closest.element) // 7 = the closest element
    print(closest.index)   // 2 = index of the closest element
    

    enumerate() 遍历所有数组元素以及 对应的索引,minElement() 返回“最小的” (index, element) 对关闭。 闭包比较差异的绝对值 x 的两个元素。

    (这里假设数组不为空,所以minElement() 不返回nil。)

    请注意,这可能不是大型数组的最快解决方案, 因为对于(几乎)所有的绝对差异计算两次 数组元素。但对于小型数组,这应该无关紧要。

    Swift 3

    let numbers = [ 1, 3, 7, 11]
    let x = 6
    let closest = numbers.enumerated().min( by: { abs($0.1 - x) < abs($1.1 - x) } )!
    print(closest.element) // 7
    print(closest.offset) // 2
    

    Swift 1.2 版本可以在编辑历史中找到。

    【讨论】:

    • 抱歉没有早点回复。实际上我的元素不超过 7,但我仍在使用 Swift 1.2。如果您能包含一个 Swift 1.2 函数式解决方案,我将不胜感激。
    • @chronos:您只需要最接近的值还是还需要索引?
    • 索引是我需要的主要东西。
    • @chronos:查看更新。请注意,Xcode 7 现在是最终版本,因此在 Swift 1.2 代码上花费太多时间可能不值得。
    • 谢谢,刚刚更新到 Xcode 7。这可能与问题不完全相关,但我已经观看了 WWDC 2015 上关于 Swift 的新功能的视频,它基本上触及了表面。您是否知道任何主要提供有关 Swift 2 中的新功能的深入信息的资源?
    【解决方案2】:

    斯威夫特 4.2

    不完全是 OP 的要求,但您可以在排序数组上使用 first(where:)firstIndex(where:) 数组方法来获取大于 x 的第一个值:

    let numbers = [1, 3, 7, 11]
    
    let x = 6
    let index = numbers.firstIndex(where: { $0 >= x })!
    let result = numbers.first(where: { $0 >= x })!
    
    print(index, result) // 2 7
    

    【讨论】:

    • 这个答案对我很有用(谢谢!),但投票反对,因为它实际上不是问题的答案。他们要求最接近的数字,而不是系列中的下一个。证明是如果x = 4,那么result == 7不是最接近的数字
    【解决方案3】:

    另一种选择是使用reduce 方法

    let numbers = [1, 3, 7, 11]
    let x = 11
    
    let result = numbers.reduce(numbers.first!) { abs($1 - x) < abs($0 - x) ? $1 : $0 }
    

    如果你想避免重复计算绝对值,你可以先将每个数组元素映射到它与输入数字的绝对差值,然后找到最小值。

    let numbers = [1, 3, 7, 11]
    let x = 11
    
    if let (index, _) = numbers.map({ abs($0 - x) }).enumerate().minElement({ $0.1 < $1.1 }) {
        let result = numbers[index]
    }
    

    【讨论】:

      【解决方案4】:

      循环遍历数组中的值,计算 abs() 差异,将其与正在运行的“currentSmallestDifference”变量进行比较,如果当前差异小于该值,则用新值覆盖它,同时保持数组索引的记录...

      int currentSmallestDifference = int.max(); //largest value defined by the int type, this way the first difference in the loop will never be larger than this 
      int index = -1;   
      for(int i = 0, i < array.size(), i++){
          if( abs(array(i) - X) < currentSmallestDifference){
              currentSmallestDifference = abs(array(i) - X);
              index = i;
          }
      }
      

      这在 O(n) 中解决。

      但是如果数组已经排序了 (O(nlog(n)),那么您可以对数组中的 X 执行 O(log(n)) 二进制搜索并找到最接近它的值。在这种情况下根本不需要使用 abs() ......只是比较

      但对于大小为 1 到 7 的数组,算法复杂度并不是真正的因素。

      事实上,对于数组大小为 1,问题 甚至不是一个因素 (?!?)

      更新>>刚刚意识到标题所说的最小积极差异..所以只需抛弃所有 abs() 东西并确保( array(i) - X) 的符号/方向正确。

      【讨论】:

      • 我认为将currentSmallestDifference 设为可选项比使用999999999 或其他常量要好。
      • ... 或使用Int.max 初始化。
      • Int.max 是我的想法,我只是不知道用伪代码的说法会接受这样的事情......
      • @LamarLatrell:我的意思是Int.max,它不是伪代码,而是真正的 Swift 代码。它返回Int 类型的最大可能值。您的 int.max() 无法编译。
      • oooh,好的 - 所以 hrrrrm,我在原始问题 now 中看到了标签 :)
      【解决方案5】:

      把它包装成一个扩展:

      extension Sequence where Iterator.Element: SignedNumeric & Comparable {
      
          /// Finds the nearest (offset, element) to the specified element.
          func nearestOffsetAndElement(to toElement: Iterator.Element) -> (offset: Int, element: Iterator.Element) {
      
              guard let nearest = enumerated().min( by: {
                  let left = $0.1 - toElement
                  let right = $1.1 - toElement
                  return abs(left) <= abs(right)
              } ) else {
                  return (offset: 0, element: toElement)
              }
      
              return nearest
          }
      
          func nearestElement(to element: Iterator.Element) -> Iterator.Element {
              return nearestOffsetAndElement(to: element).element
          }
      
          func indexOfNearestElement(to element: Iterator.Element) -> Int {
              return nearestOffsetAndElement(to: element).offset
          }
      
      }
      

      【讨论】:

        【解决方案6】:

        这是一种计算最接近正值的单个循环的方法。不是单线。但仍然有效

        let values = [1, 4, 9, 3, 8, 2, 11]
        let x = 8
        
        func closestPositiveValue(from array: [Int], value: Int) -> (Int, Int) { // -> (value, difference)
          var (val, dif): (Int?, Int?) = (nil, nil)
          for index in 0..<array.count {
            if x <= array[index] {
              var difference = x - array[index]
              if difference < 0 {
                difference = array[index] - x
              }
              if val == nil { // nil check for initial loop
                (val, dif) = (array[index], difference)
              } else {
                if difference < dif! {
                  (val, dif) = (array[index], difference)
                }
              }
            }
          }
          return (val ?? 0, dif ?? 0) // defaults to first item in array if there is no closest positive number
        }
        print(closestPositiveValue(from: values, value: x)) // prints (8, 0)
        

        【讨论】:

          【解决方案7】:

          以 Martin R 不久前的正确答案为基础,这是我一直在使用的一个方便的扩展。目前在 Swift 5 中。

          extension Array where Element: (Comparable & SignedNumeric) {
          
              func nearest(to value: Element) -> (offset: Int, element: Element)? {
                  self.enumerated().min(by: {
                      abs($0.element - value) < abs($1.element - value)
                  })
              }
          }
          

          用法:

          let numbers = [ 1, 3, 7, 11]
          let x = 6
          
          if let closest = numbers.nearest(to: x) {
              print(closest)
          }
          // prints (offset: 2, element: 7)
          
          

          【讨论】:

          • 如果我们想与数组元素的属性进行比较,我们将如何实现?
          猜你喜欢
          • 2017-01-17
          • 1970-01-01
          • 1970-01-01
          • 2011-02-22
          • 1970-01-01
          • 2011-11-27
          • 2013-11-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多