【问题标题】:Get index of closest value with binary search使用二分搜索获取最接近值的索引
【发布时间】:2014-07-04 02:52:23
【问题描述】:

我想在 python 中进行二分搜索:

def binarySearch(data, val):

其中data 是排序数组,value 是要搜索的值。如果找到该值,我想返回index(例如data[index] = val)。如果找不到该值,我想返回最接近该值的项目的index

这是我得到的:

def binarySearch(data, val):
    high = len(data)-1
    low = 0
    while True:
        index = (high + low) / 2
        if data[index] == val:
            return index
        if data[index] < val:
            low = index
        if data[index] > val:
            high = index

【问题讨论】:

  • 使用 Python 的 bisect 模块:“该模块被称为 bisect,因为它使用基本的二分算法来完成它的工作。源代码作为算法的工作示例可能是最有用的(边界条件已经对了!)。”
  • 确实在标准库中已经解决了这个问题,并且那里有源代码。
  • 在添加代码后撤回关闭投票和否决票。

标签: python binary-search


【解决方案1】:

这是如果找到值将返回索引的代码,否则返回最接近该值的项目的索引,希望对您有所帮助。

def binarySearch(data, val):
    lo, hi = 0, len(data) - 1
    best_ind = lo
    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if data[mid] < val:
            lo = mid + 1
        elif data[mid] > val:
            hi = mid - 1
        else:
            best_ind = mid
            break
        # check if data[mid] is closer to val than data[best_ind] 
        if abs(data[mid] - val) < abs(data[best_ind] - val):
            best_ind = mid
    return best_ind

def main():
    data = [1, 2, 3, 4, 5, 6, 7]
    val = 6.1
    ind = binarySearch(data, val)
    print 'data[%d]=%d' % (ind, data[ind])

if __name__ == '__main__':
    main()

【讨论】:

  • 此解决方案仅适用于唯一值列表。为了将其调整为非唯一值列表,应更改:if abs(data[mid] - val) &lt; abs(data[best_ind] - val): on if abs(data[mid] - val) &lt;= abs(data[best_ind] - val):
  • 你应该mid = lo + (hi - lo) // 2 除了那个优秀的解决方案
【解决方案2】:

这样的事情应该可以工作。它返回一个有两个索引的数组。如果找到 val,则返回数组中的两个值相同。否则,它返回最接近 val 的两项的索引。

def binarySearch(data, val):
    highIndex = len(data)-1
    lowIndex = 0
    while highIndex > lowIndex:
            index = (highIndex + lowIndex) / 2
            sub = data[index]
            if data[lowIndex] == val:
                    return [lowIndex, lowIndex]
            elif sub == val:
                    return [index, index]
            elif data[highIndex] == val:
                    return [highIndex, highIndex]
            elif sub > val:
                    if highIndex == index:
                            return sorted([highIndex, lowIndex])
                    highIndex = index
            else:
                    if lowIndex == index:
                            return sorted([highIndex, lowIndex])
                    lowIndex = index
    return sorted([highIndex, lowIndex])

【讨论】:

  • 假设列表中有偶数个值。如果它有 7 个项目 data[index]data[3.5] 这是一个 TypeError。
  • @nackjicholson 我使用的是 Python 2.x,其中 / 将返回一个截断的整数。如果您使用的语言 / 不会返回截断的整数,那么您必须添加一行代码来截断它。
【解决方案3】:

我知道这是一个老问题,但它在 Google 的搜索结果中占了上风,我也遇到了同样的问题。有一个内置函数可以做到这一点,它使用二进制搜索并允许您输入参考数组和比较数组。

numpy.searchsorted(a, v, side='left', sorter=None)

a 是参考数组(data 在原始问题中),v 是要比较的数组(val 来自问题)。这将返回一个大小为varray,其索引的int 值需要将v 的第n 个元素插入到a 中以保留a 中的排序顺序'side 关键字确定是否您希望将v 的元素放置在a 中的适当值的“左”(之前)或“右”(之后)。

[截至 2017 年 7 月的文档链接] https://docs.scipy.org/doc/numpy/reference/generated/numpy.searchsorted.html#numpy.searchsorted

【讨论】:

    【解决方案4】:

    这是一个二分搜索的示例实现。我不会为您完成所有(家庭?)工作,我相信您可以自己弄清楚如何存储和返回最接近值的索引。

    # BINARY SEARCH: O(log n), search space halfed each step
    def biSearch(lst, find): # expects sorted lst 
        lowIndex = 0
        highIndex = len(lst) - 1
        midIndex = (lowIndex + highIndex)//2
        lastMid = None
        steps = 0
        while midIndex != lastMid:
            steps += 1
            if lst[midIndex] == find:
                return (midIndex, steps)
            if lst[midIndex] < find:
                lowIndex = midIndex + 1
            else:
                highIndex = midIndex - 1
            lastMid = midIndex    
            midIndex = (lowIndex + highIndex)//2
        return (-1, steps)
    

    【讨论】:

      【解决方案5】:

      不是这个问题的答案。但我来到这里试图弄清楚如何在排序列表中获取给定目标项的两个周围值。

      如果其他人正在寻找,这就是我根据此处的其他一些答案得出的结论。

      import random
      
      
      def get_nearest(items, target):
          print(f'looking for {target}')
          high_index = len(items) - 1
          low_index = 0
      
          if not items[low_index] <= target <= items[high_index]:
              raise ValueError(f'The target {target} is not in the range of'
                               f' provided items {items[low_index]}:{items[high_index]}')
      
          if target in items:
              return target, target
      
          while high_index > low_index:
              index = int((high_index + low_index) / 2)
              sub = items[index]
      
              if sub > target:
                  if high_index == index:
                      return tuple(sorted([items[high_index], items[low_index]]))
                  high_index = index
              else:
                  if low_index == index:
                      return tuple(sorted([items[high_index], items[low_index]]))
                  low_index = index
          return tuple(sorted([items[high_index], items[low_index]]))
      
      
      if __name__ == '__main__':
          my_randoms = sorted(random.sample(range(10000000), 100000))
          x = 340000
          print(get_nearest(my_randoms, x))
      
          x = 0
          my_randoms = [x] + my_randoms
          print(get_nearest(my_randoms, x))
      
          x = 10000000
          my_randoms.append(x)
          print(get_nearest(my_randoms, x))
      
          idx = random.randint(0, 100000)
          x = my_randoms[idx]
          print(get_nearest(my_randoms, x))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-12-06
        • 1970-01-01
        • 1970-01-01
        • 2016-06-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多