【问题标题】:Binary search algorithm in pythonpython中的二进制搜索算法
【发布时间】:2012-03-19 01:40:27
【问题描述】:

我正在尝试在 python 中实现二进制搜索,并将其编写如下。但是,只要 needle_element 大于数组中的最大元素,我就无法让它停止。

你能帮忙吗?谢谢。

def binary_search(array, needle_element):
    mid = (len(array)) / 2
    if not len(array):
        raise "Error"
    if needle_element == array[mid]:
        return mid
    elif needle_element > array[mid]:
        return mid + binary_search(array[mid:],needle_element)
    elif needle_element < array[mid]:
        return binary_search(array[:mid],needle_element)
    else:
        raise "Error"

【问题讨论】:

  • 我会尽量避免创建数组的大量部分副本,而是传入一个较低和较高的索引。然后,如果 lower>upper,您只需停止。
  • 如果这样做的目的是对 binsearch 的学术理解,我真的无能为力,但如果这段代码的目的是实际使用:永远不要滚动你自己的 binsearch。始终使用被广泛采用,最好是旧的库实现,即使这样也要非常小心,众所周知,binseach 在所有边缘情况下都很难正确处理。

标签: python algorithm binary-search


【解决方案1】:

如果您正在执行二进制搜索,我猜该数组已排序。如果这是真的,您应该能够将数组中的最后一个元素与needle_element 进行比较。正如章鱼所说,这可以在搜索开始之前完成。

【讨论】:

    【解决方案2】:

    您可以在开始之前检查needle_element 是否在数组的边界内。这也将提高效率,因为您无需执行多个步骤即可到达终点。

    if needle_element < array[0] or needle_element > array[-1]:
        # do something, raise error perhaps?
    

    【讨论】:

      【解决方案3】:

      needle_element &gt; array[mid] 的情况下,您当前将array[mid:] 传递给递归调用。但是你知道array[mid]太小了,所以你可以通过array[mid+1:]来代替(并相应地调整返回的索引)。

      如果指针比数组中的所有元素都大,这样做最终会给你一个空数组,并且会按预期引发错误。

      注意:每次创建子数组都会导致大型数组的性能不佳。最好是传入数组的边界。

      【讨论】:

      • 良好的性能说明; OP 考虑这一点很重要
      【解决方案4】:

      array[mid:] 每次调用它时都会创建一个新的子副本 = 慢。您还使用递归,这在 Python 中也很慢。

      试试这个:

      def binarysearch(sequence, value):
          lo, hi = 0, len(sequence) - 1
          while lo <= hi:
              mid = (lo + hi) // 2
              if sequence[mid] < value:
                  lo = mid + 1
              elif value < sequence[mid]:
                  hi = mid - 1
              else:
                  return mid
          return None
      

      【讨论】:

      • 不仅递归很慢 - 如果数组足够长,它实际上会在你的脸上爆炸,因为 Python 没有尾递归优化,并且在输入数组足够大的情况下会耗尽堆栈帧。
      • @Shnatsel 广告“足够大的输入数组” - 鉴于我们正在讨论“二进制”搜索,并且 CPython 默认递归限制设置为 1000(可以通过sys.setrecursionlimit 设置为更多),我们是谈论大小不超过 2**1000 的数组,也称为 ~10^300...
      【解决方案5】:

      Lasse V. Karlsen 在对该问题的评论中建议使用 lowerupper 索引会更好。

      这将是代码:

      def binary_search(array, target):
          lower = 0
          upper = len(array)
          while lower < upper:   # use < instead of <=
              x = lower + (upper - lower) // 2
              val = array[x]
              if target == val:
                  return x
              elif target > val:
                  if lower == x:   # these two are the actual lines
                      break        # you're looking for
                  lower = x
              elif target < val:
                  upper = x
      
      • lower &lt; upper 将在您到达较小的数字(从左侧)后停止
      • if lower == x: break 将在您达到更高的数字后停止(从右侧)

      例子:

      >>> binary_search([1,5,8,10], 5)   # return 1
      1
      >>> binary_search([1,5,8,10], 0)   # return None
      >>> binary_search([1,5,8,10], 15)  # return None
      

      【讨论】:

      • 这不起作用。尝试一个列表:[7,9,2,4,8,6,5,1,8,5,3]。
      • 你可以做x = (lower + upper) // 2
      • @user1342336 二进制搜索仅适用于已按排序顺序排列的列表。
      【解决方案6】:

      为什么不使用 bisect 模块?它应该可以完成您需要的工作——您需要维护和测试的代码更少。

      【讨论】:

      • bisect.bisect_right(a, b) -> 很难在速度方面做得更好
      【解决方案7】:

      您可以按照其他人的建议改进您的算法,但让我们先看看为什么它不起作用:

      您陷入了循环,因为如果 needle_element &gt; array[mid],您将在接下来搜索的二等分数组中包含元素 mid。因此,如果 needle 不在数组中,您最终将永远搜索长度为 1 的数组。改为传递array[mid+1:](即使mid+1 不是有效索引也是合法的),您最终将使用长度为零的数组调用您的函数。所以len(array) == 0 表示“未找到”,而不是错误。妥善处理。

      【讨论】:

        【解决方案8】:

        以上所有答案都是正确的,但我认为分享我的代码会有所帮助

        def binary_search(number):
            numbers_list = range(20, 100)
            i = 0
            j = len(numbers_list)
            while i < j:
                middle = int((i + j) / 2)
                if number > numbers_list[middle]:
                    i = middle + 1
                else:
                    j = middle
            return 'the index is '+str(i)
        

        【讨论】:

          【解决方案9】:

          它使用递归返回数组中键的索引。

          round() 是一个将浮点数转换为整数的函数,可以使代码快速运行并达到预期的情况[O(logn)]。

          A=[1,2,3,4,5,6,7,8,9,10]
          low = 0
          hi = len(A)
          v=3
          def BS(A,low,hi,v):
              mid = round((hi+low)/2.0)
              if v == mid:
                  print ("You have found dude!" + " " + "Index of v is ", A.index(v))
              elif v < mid:
                  print ("Item is smaller than mid")
                  hi = mid-1
                  BS(A,low,hi,v)
              else :
                  print ("Item is greater than mid")
                  low = mid + 1
                  BS(A,low,hi,v)
          BS(A,low,hi,v)
          

          【讨论】:

            【解决方案10】:
            def binary_search(array, target):
                low = 0
                mid = len(array) / 2
                upper = len(array)
            
                if len(array) == 1:
                    if array[0] == target:
                        print target
                        return array[0]
                    else:
                        return False
                if target == array[mid]:
                    print array[mid]
                    return mid
                else:
                    if mid > low:
                        arrayl = array[0:mid]
                        binary_search(arrayl, target)
            
                    if upper > mid:
                        arrayu = array[mid:len(array)]
                        binary_search(arrayu, target)
            
            if __name__ == "__main__":
                a = [3,2,9,8,4,1,9,6,5,9,7]
                binary_search(a,9)
            

            【讨论】:

              【解决方案11】:

              如果没有下/上索引,这也应该这样做:

              def exists_element(element, array):
                  if not array:
                      yield False
              
                  mid = len(array) // 2
                  if element == array[mid]:
                      yield True
                  elif element < array[mid]:
                      yield from exists_element(element, array[:mid])
                  else:
                      yield from exists_element(element, array[mid + 1:])
              

              【讨论】:

                【解决方案12】:

                这是一个尾递归解决方案,我认为这比复制部分数组然后跟踪返回的索引更干净:

                def binarySearch(elem, arr):
                    # return the index at which elem lies, or return false
                    # if elem is not found
                    # pre: array must be sorted
                    return binarySearchHelper(elem, arr, 0, len(arr) - 1)
                
                def binarySearchHelper(elem, arr, start, end):
                    if start > end:
                        return False
                    mid = (start + end)//2
                    if arr[mid] == elem:
                        return mid
                    elif arr[mid] > elem:
                        # recurse to the left of mid
                        return binarySearchHelper(elem, arr, start, mid - 1)
                    else:
                        # recurse to the right of mid
                        return binarySearchHelper(elem, arr, mid + 1, end)
                

                【讨论】:

                  【解决方案13】:

                  使用递归:

                  def binarySearch(arr,item):
                      c = len(arr)//2
                      if item > arr[c]:
                         ans = binarySearch(arr[c+1:],item)
                         if ans:
                            return binarySearch(arr[c+1],item)+c+1
                      elif item < arr[c]:
                         return binarySearch(arr[:c],item)
                      else:
                         return c
                  
                  binarySearch([1,5,8,10,20,50,60],10)
                  

                  【讨论】:

                  • 如果列表中不存在该项目,则不处理。它只是抛出异常。
                  【解决方案14】:

                  如果值在列表中,则返回布尔值。

                  捕获列表的第一个和最后一个索引,循环和划分捕获中间值的列表。 在每个循环中都会做同样的事情,然后比较输入的值是否等于中间值。

                  def binarySearch(array, value):
                    array = sorted(array)
                    first = 0
                    last = len(array) - 1
                  
                    while first <= last:
                      midIndex = (first + last) // 2
                      midValue = array[midIndex]
                  
                      if value == midValue:
                        return True
                      if value < midValue:
                        last = midIndex - 1
                      if value > midValue:
                        first = midIndex + 1
                    return False
                  

                  【讨论】:

                    猜你喜欢
                    • 2022-11-25
                    • 2015-05-18
                    • 2012-05-09
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2022-06-19
                    • 1970-01-01
                    相关资源
                    最近更新 更多