Python 中的二分查找(二分查找)
是否有一个库函数可以对列表/元组执行二分搜索,如果找到则返回该项目的位置,如果没有则返回“False”(-1、None 等)?
我在 bisect 模块 中找到了 bisect_left/right 函数,但它们仍然返回一个位置如果该项目不在列表中。 这对于他们的预期用途来说非常好,但我只想知道列表中是否有某个项目(不想插入任何内容)。
我想过使用 bisect_left ,然后检查该位置的项目是否等于我正在搜索的内容,但这似乎很麻烦(而且我还需要进行边界检查,该数字是否可以大于我的列表中最大的数字)。 如果有更好的方法我想知道。
编辑为了澄清我需要这个的目的:我知道字典非常适合于此,但我试图将内存消耗保持在尽可能低的水平。 我的预期用途是一种双向查找表。 我在表中有一个值列表,我需要能够根据索引访问这些值。 而且我还希望能够找到特定值的索引,或者如果该值不在列表中则找不到索引。
为此使用字典将是最快的方法,但会(大约)使内存需求增加一倍。
我问这个问题时认为我可能忽略了 Python 库中的某些内容。 看来我必须按照 Moe 的建议编写自己的代码。
Is there a library function that performs binary search on a list/tuple and return the position of the item if found and 'False' (-1, None, etc.) if not?
I found the functions bisect_left/right in the bisect module, but they still return a position even if the item is not in the list. That's perfectly fine for their intended usage, but I just want to know if an item is in the list or not (don't want to insert anything).
I thought of using bisect_left
and then checking if the item at that position is equal to what I'm searching, but that seems cumbersome (and I also need to do bounds checking if the number can be larger than the largest number in my list). If there is a nicer method I'd like to know about it.
Edit To clarify what I need this for: I'm aware that a dictionary would be very well suited for this, but I'm trying to keep the memory consumption as low as possible. My intended usage would be a sort of double-way look-up table. I have in the table a list of values and I need to be able to access the values based on their index. And also I want to be able to find the index of a particular value or None if the value is not in the list.
Using a dictionary for this would be the fastest way, but would (approximately) double the memory requirements.
I was asking this question thinking that I may have overlooked something in the Python libraries. It seems I'll have to write my own code, as Moe suggested.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(22)
bisect_left
查找可以在给定排序范围内插入元素同时保持排序顺序的第一个位置p
。 如果x
存在于范围内,这将是x
的位置。 如果p
是最后位置,则未找到x
。 否则,我们可以测试一下x
是否存在,看看是否找到了x
。bisect_left
finds the first positionp
at which an element could be inserted in a given sorted range while maintaining the sorted order. That will be the position ofx
ifx
exists in the range. Ifp
is the past-the-end position,x
wasn't found. Otherwise, we can test to see ifx
is there to see ifx
was found.为什么不查看 bisect_left/right 的代码并对其进行调整以满足您的目的。
像这样:
Why not look at the code for bisect_left/right and adapt it to suit your purpose.
like this:
这有点偏离主题(因为莫伊的回答似乎完整地回答了OP的问题),但可能值得从头到尾看看整个过程的复杂性。 如果您将事物存储在排序列表中(二分搜索会有所帮助),然后仅检查是否存在,则会出现(最坏情况,除非指定):
排序列表
而使用
set()
,你需要花费排序列表真正让你得到的是“下一个”, “前一个”和“范围”(包括插入或删除范围),在给定起始索引的情况下,其时间复杂度为 O(1) 或 O(|range|)。 如果您不经常使用这些类型的操作,那么总体而言,存储为集合并排序显示可能是更好的选择。
set()
在 python 中产生的额外开销非常小。This is a little off-topic (since Moe's answer seems complete to the OP's question), but it might be worth looking at the complexity for your whole procedure from end to end. If you're storing thing in a sorted lists (which is where a binary search would help), and then just checking for existence, you're incurring (worst-case, unless specified):
Sorted Lists
Whereas with a
set()
, you're incurringThe thing a sorted list really gets you are "next", "previous", and "ranges" (including inserting or deleting ranges), which are O(1) or O(|range|), given a starting index. If you aren't using those sorts of operations often, then storing as sets, and sorting for display might be a better deal overall.
set()
incurs very little additional overhead in python.可能值得一提的是,bisect 文档现在提供搜索示例:
http://docs.python.org/library/bisect.html#searching -sorted-lists
(提高 ValueError 而不是返回 -1 或 None 更符合 Python 风格——例如 list.index() 就可以做到这一点。当然,您可以根据您的需要调整示例。)
It might be worth mentioning that the bisect docs now provide searching examples:
http://docs.python.org/library/bisect.html#searching-sorted-lists
(Raising ValueError instead of returning -1 or None is more pythonic – list.index() does it, for example. But of course you can adapt the examples to your needs.)
最简单的是使用 bisect 并检查一个位置以查看该项目是否在那里:
Simplest is to use bisect and check one position back to see if the item is there:
这是手册中的内容:
http://docs.python.org/2/library/bisect.html
8.5.1。 搜索排序列表
上述 bisect() 函数对于查找插入点很有用,但对于常见的搜索任务来说可能会很棘手或尴尬。 以下五个函数展示了如何将它们转换为排序列表的标准查找:
因此,稍微修改一下您的代码应该是:
This is right from the manual:
http://docs.python.org/2/library/bisect.html
8.5.1. Searching Sorted Lists
The above bisect() functions are useful for finding insertion points but can be tricky or awkward to use for common searching tasks. The following five functions show how to transform them into the standard lookups for sorted lists:
So with the slight modification your code should be:
这个基于数学断言,即(low + high)/2的下限始终小于high,其中low 是下限,high 是上限。
This one is based on a mathematical assertion that the floor of (low + high)/2 is always smaller than high where low is the lower limit and high is the upper limit.
我同意使用 bisect 模块的 @DaveAbrahams 的答案是正确的方法。 他在回答中没有提及任何重要细节。
来自 文档
bisect.bisect_left( a, x, lo=0, hi=len(a))
二分模块不需要提前预先计算搜索数组。 您可以仅将端点呈现给
bisect.bisect_left
,而不是使用默认值0
和len(a)
。对于我的使用来说更重要的是,寻找一个值 X 以使给定函数的误差最小化。 为此,我需要一种方法让 bisect_left 的算法调用我的计算。 这真的很简单。
只需提供一个将
__getitem__
定义为a
的对象即可。例如,我们可以使用二分算法来求任意精度的平方根!
I agree that @DaveAbrahams's answer using the bisect module is the correct approach. He did not mention one important detail in his answer.
From the docs
bisect.bisect_left(a, x, lo=0, hi=len(a))
The bisection module does not require the search array to be precomputed ahead of time. You can just present the endpoints to the
bisect.bisect_left
instead of it using the defaults of0
andlen(a)
.Even more important for my use, looking for a value X such that the error of a given function is minimized. To do that, I needed a way to have the bisect_left's algorithm call my computation instead. This is really simple.
Just provide an object that defines
__getitem__
asa
For example, we could use the bisect algorithm to find a square root with arbitrary precision!
如果您只想查看它是否存在,请尝试将列表转换为字典:
在我的机器上,“if n in l”花了 37 秒,而“if n in d”花了 0.4 秒。
If you just want to see if it's present, try turning the list into a dict:
On my machine, "if n in l" took 37 seconds, while "if n in d" took 0.4 seconds.
戴夫·亚伯拉罕的解决方案很好。 虽然我会做得很简约:
Dave Abrahams' solution is good. Although I have would have done it minimalistic:
查看 Wikipedia 上的示例 http://en.wikipedia.org/wiki/Binary_search_algorithm
Check out the examples on Wikipedia http://en.wikipedia.org/wiki/Binary_search_algorithm
虽然 Python 中没有明确的二分搜索算法,但有一个模块 - bisect ,旨在使用二分搜索查找排序列表中元素的插入点。 这可以被“欺骗”以执行二分搜索。 这样做的最大优点与大多数库代码具有相同的优点 - 它具有高性能、经过良好测试并且可以正常工作(特别是二进制搜索可以很难成功实施 - 特别是在不仔细考虑边缘情况的情况下)。
基本类型
对于像字符串或整数这样的基本类型,这非常简单 - 您所需要的只是 bisect 模块和排序列表:
您还可以使用它来查找重复项:
显然您可以只返回索引而不是如果需要的话,该索引处的值。
对象
对于自定义类型或对象,事情有点棘手:您必须确保实现丰富的比较方法才能正确平分比较。
这应该至少适用于 Python 2.7 -> 3.3
While there's no explicit binary search algorithm in Python, there is a module -
bisect
- designed to find the insertion point for an element in a sorted list using a binary search. This can be "tricked" into performing a binary search. The biggest advantage of this is the same advantage most library code has - it's high-performing, well-tested and just works (binary searches in particular can be quite difficult to implement successfully - particularly if edge cases aren't carefully considered).Basic Types
For basic types like Strings or ints it's pretty easy - all you need is the
bisect
module and a sorted list:You can also use this to find duplicates:
Obviously you could just return the index rather than the value at that index if desired.
Objects
For custom types or objects, things are a little bit trickier: you have to make sure to implement rich comparison methods to get bisect to compare correctly.
This should work in at least Python 2.7 -> 3.3
避免边界检查或检查项目是否相等的一种方法是同时运行 bisect_left() 和 bisect_right():
One way to avoid bounds checks or the checking the item for equality is to run both bisect_left() and bisect_right():
使用字典不会使内存使用量增加一倍,除非您存储的对象非常小,因为这些值只是指向实际对象的指针:
在该示例中,“foo”仅存储一次。 这对你有影响吗? 无论如何,我们到底在谈论多少项目?
Using a dict wouldn't like double your memory usage unless the objects you're storing are really tiny, since the values are only pointers to the actual objects:
In that example, 'foo' is only stored once. Does that make a difference for you? And exactly how many items are we talking about anyway?
此代码以递归方式处理整数列表。 寻找最简单的情况,即:列表长度小于2。这意味着答案已经存在,并执行测试以检查正确答案。
如果不是,则设置一个中间值并测试是否正确,如果不是,则通过再次调用该函数来执行二分,但将中间值设置为上限或下限,通过向左或向右移动它
This code works with integer lists in a recursive way. Looks for the simplest case scenario, which is: list length less than 2. It means the answer is already there and a test is performed to check for the correct answer.
If not, a middle value is set and tested to be the correct, if not bisection is performed by calling again the function, but setting middle value as the upper or lower limit, by shifting it to the left or right
s
是一个列表。binary(s, 0, len(s) - 1, find)
是初始调用。函数返回查询项的索引。 如果没有这样的项目,则返回
-1
。s
is a list.binary(s, 0, len(s) - 1, find)
is the initial call.Function returns an index of the queried item. If there is no such item it returns
-1
.我想这更好、更有效。 请纠正我:)。 谢谢
I guess this is much better and effective. please correct me :) . Thank you
二分查找:
// 要调用上述函数,请使用:
Binary Search :
// To call above function use :
我需要 python 中的二分搜索和 Django 模型的通用。 在 Django 模型中,一个模型可以具有另一个模型的外键,我想对检索到的模型对象执行一些搜索。 我写了下面的函数你可以使用它。
I needed binary search in python and generic for Django models. In Django models, one model can have foreign key to another model and I wanted to perform some search on the retrieved models objects. I wrote following function you can use this.
上面有很多好的解决方案,但我还没有看到一个简单的(KISS 保持简单(因为我)愚蠢地使用 Python 内置/通用二分函数来进行二分搜索。在二分函数周围有一些代码, 我已经测试了一个小字符串数组的所有情况,上面的一些解决方案暗示了这一点,但希望下面的简单代码能够帮助像我一样困惑的人。
我想我有一个例子, 指示将新值/搜索项插入到排序列表中的位置,下面的代码使用 bisect_left ,如果在列表/数组中找到搜索项,它将返回命中的索引(注意 bisect 和 bisect_right 将返回命中或匹配后的元素的索引作为插入点)如果未找到,bisect_left 将返回排序列表中下一项的索引,该索引不会 == 搜索值。唯一的其他情况是搜索项将。 go 位于列表末尾,其中返回的索引将超出列表/数组的末尾,并且在下面的代码中,Python 使用“and”逻辑句柄提前退出。 (第一个条件False Python不检查后续条件)
Many good solutions above but I haven't seen a simple (KISS keep it simple (cause I'm) stupid use of the Python built in/generic bisect function to do a binary search. With a bit of code around the bisect function, I think I have an example below where I have tested all cases for a small string array of names. Some of the above solutions allude to/say this, but hopefully the simple code below will help anyone confused like I was.
Python bisect is used to indicate where to insert an a new value/search item into a sorted list. The below code which uses bisect_left which will return the index of the hit if the search item in the list/array is found (Note bisect and bisect_right will return the index of the element after the hit or match as the insertion point) If not found, bisect_left will return an index to the next item in the sorted list which will not == the search value. The only other case is where the search item would go at the end of the list where the index returned would be beyond the end of the list/array, and which in the code below the early exit by Python with "and" logic handles. (first condition False Python does not check subsequent conditions)
你好,这是我的 python 实现,没有二分法。 让我知道是否可以改进。
Hello here is my python implementation without bisect. let me know if it can be improved.