การค้นหาแบบไบนารี (bisection) ใน Python


177

มีฟังก์ชั่นห้องสมุดที่ดำเนินการค้นหาแบบไบนารีในรายการ / ทูเปิลและส่งคืนตำแหน่งของรายการหากพบและ 'เท็จ' (-1, ไม่มี, ฯลฯ ) ถ้าไม่?

ฉันพบฟังก์ชัน bisect_left / ขวาในโมดูล bisectแต่พวกเขายังคงส่งคืนตำแหน่งแม้ว่ารายการไม่ได้อยู่ในรายการ มันดีมากสำหรับการใช้งานที่ต้องการ แต่ฉันแค่อยากรู้ว่ารายการนั้นอยู่ในรายการหรือไม่ (ไม่ต้องการแทรกอะไร)

ฉันคิดว่าจะใช้bisect_leftแล้วตรวจสอบว่ารายการในตำแหน่งนั้นเท่ากับสิ่งที่ฉันกำลังค้นหาหรือไม่ แต่ดูเหมือนว่าจะยุ่งยาก (และฉันต้องทำการตรวจสอบอย่าง จำกัด หากจำนวนอาจมากกว่าจำนวนที่มากที่สุดในรายการของฉัน) หากมีวิธีที่ดีกว่าฉันต้องการทราบเกี่ยวกับมัน

แก้ไขเพื่อชี้แจงสิ่งที่ฉันต้องการสิ่งนี้สำหรับ: ฉันทราบว่าพจนานุกรมจะเหมาะมากสำหรับสิ่งนี้ แต่ฉันพยายามรักษาปริมาณการใช้หน่วยความจำให้ต่ำที่สุดเท่าที่จะทำได้ การใช้งานที่ฉันตั้งใจจะเป็นตารางค้นหาสองทาง ฉันมีรายการของค่าในตารางและฉันต้องสามารถเข้าถึงค่าตามดัชนีของพวกเขา และฉันต้องการหาดัชนีของค่าใดค่าหนึ่งหรือไม่มีถ้าค่านั้นไม่อยู่ในรายการ

การใช้พจนานุกรมสำหรับวิธีนี้จะเป็นวิธีที่เร็วที่สุด แต่จะ (ประมาณ) ความต้องการของหน่วยความจำเพิ่มขึ้นเป็นสองเท่า

ฉันถามคำถามนี้โดยคิดว่าฉันอาจมองข้ามบางอย่างในห้องสมุด Python ดูเหมือนว่าฉันจะต้องเขียนโค้ดของตัวเองตามที่ Moe แนะนำ


1
คุณพยายามทำอะไรให้สำเร็จ หากค่าไม่ซ้ำกันให้พิจารณาใช้ชุดและ "if value in set: something"
Kirk Strauser

สำหรับสิ่งที่คุ้มค่า "-1" ถือว่าเป็นจริง "0" จะเป็นเท็จ
Glyph

3
ฉันพูดถึง -1 เพราะฟังก์ชั่นที่ส่งคืนดัชนีของรายการที่ค้นหาในอาร์เรย์สามารถส่งคืน 0 แล้วดังนั้น -1 จะถูกส่งกลับหากไม่พบรายการ (คล้ายกับการค้นหาสตริงย่อย)
rslite

3
หากคุณใช้ numpy np.searchsortedมีประโยชน์ docs.scipy.org/doc/numpy/reference/generated/…
Roman Shapovalov

คำตอบ:


238
from bisect import bisect_left

def binary_search(a, x, lo=0, hi=None):  # can't use a to specify default for hi
    hi = hi if hi is not None else len(a)  # hi defaults to len(a)   
    pos = bisect_left(a, x, lo, hi)  # find insertion position
    return pos if pos != hi and a[pos] == x else -1  # don't walk off the end

10
@ volcano ดังนั้น binsearch โดยทั่วไปแล้ว
cubuspl42

4
@TomSwirly ไม่ง่ายเหมือนคุณ แต่ที่ถูกต้องและยังคงปรับปรุง:if hi is None: hi = len(a)
มาร์คไถ่

สิ่งที่เกี่ยวกับการสั่งซื้อมากไปน้อย?
Parikshit Chalke

2
คุณสามารถเพิ่มคำอธิบายนอกรหัสได้ไหม มาตรฐานที่นี่มีการเปลี่ยนแปลง
SS Anne

54

ทำไมไม่ลองดูรหัสสำหรับ bisect_left / right และปรับให้เหมาะกับวัตถุประสงค์ของคุณ

แบบนี้:

def binary_search(a, x, lo=0, hi=None):
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        midval = a[mid]
        if midval < x:
            lo = mid+1
        elif midval > x: 
            hi = mid
        else:
            return mid
    return -1

29
ฉัน +1 สิ่งนี้มา แต่ตอนนี้ฉันได้ข้อสรุปว่านี่ไม่ใช่เรื่องดี หากปฏิบัติตามคำตอบนี้จะทำให้เกิดการซ้ำซ้อนของรหัสจำนวนมากและอย่างที่เราทุกคนทราบกันดีว่าเป็นเรื่องง่ายมากที่จะทำการค้นหาแบบไบนารี
abyx

1
มันไม่ควรจะhi = mid - 1อยู่ในelif?
PawełPrażak

7
@ Paweł: มันมีสองรูปแบบที่เทียบเท่ากันขึ้นอยู่กับว่าขอบเขตบนนั้นรวมหรือไม่รวม คุณสามารถเปลี่ยนhi = midเป็นhi = mid-1และhi = len(a)ไปhi = len(a)-1และwhile lo < hi:ไปwhile lo <= hiและก็จะถูกต้องเท่ากัน
user102008

2
ทำไมไม่ทำเช่น: def binary_search (a, x, lo = 0, hi = None): i = bisect (a, x, lo, hi) คืนค่า i ถ้า a [i] == x else -1 ขออภัยสำหรับ การจัดรูปแบบ - ไม่แน่ใจว่าวิธีการทำเช่นนี้อย่างถูกต้องในความคิดเห็น arrea
Vitali

1
คุณควรใช้bisect.bisect_left()มากกว่านี้
alastair

37

นี่เป็นหัวข้อเล็กน้อย (เนื่องจากคำตอบของ Moe นั้นสมบูรณ์สำหรับคำถามของ OP) แต่อาจคุ้มค่าที่จะดูความซับซ้อนของกระบวนการทั้งหมดของคุณตั้งแต่ต้นจนจบ หากคุณกำลังจัดเก็บสิ่งต่าง ๆ ในรายการที่เรียงลำดับ (ซึ่งเป็นที่ที่การค้นหาแบบไบนารี่จะช่วยได้) และจากนั้นเพียงตรวจสอบการมีอยู่ของคุณก็จะเกิดขึ้น (กรณีที่เลวร้ายที่สุดเว้นแต่จะระบุไว้):

รายการที่จัดเรียง

  • O (n log n) เพื่อสร้างรายการเริ่มต้น (ถ้าเป็นข้อมูลที่ไม่เรียงลำดับ O (n) ถ้าเรียงลำดับแล้ว)
  • การค้นหา O (บันทึก n) (นี่คือส่วนการค้นหาแบบไบนารี)
  • O (n) แทรก / ลบ (อาจเป็นกรณี O (1) หรือ O (บันทึก n) ขึ้นอยู่กับรูปแบบของคุณ)

ในขณะที่set()คุณกำลังเกิดขึ้น

  • O (n) เพื่อสร้าง
  • ค้นหา O (1)
  • O (1) ใส่ / ลบ

สิ่งที่รายการที่เรียงลำดับทำให้คุณเป็นจริงคือ "ถัดไป", "ก่อนหน้า" และ "ช่วง" (รวมถึงการแทรกหรือลบช่วง) ซึ่งเป็น O (1) หรือ O (| ช่วง |) ซึ่งได้รับดัชนีเริ่มต้น หากคุณไม่ได้ใช้การดำเนินการหลายประเภทเหล่านั้นบ่อยครั้งการจัดเก็บเป็นชุดและการเรียงลำดับสำหรับการแสดงผลอาจเป็นภาพรวมที่ดีกว่า set()เกิดค่าใช้จ่ายเพิ่มเติมเล็กน้อยในงูหลาม


7
มีอีกอย่างหนึ่งที่คุณเรียงลำดับรายการได้ O (n) สั่งการแวะผ่าน ด้วยชุดที่เป็น O (n log n) และคุณต้องคัดลอกการอ้างอิงไปยังข้อมูลลงในรายการ
Omnifarious

1
จริงพอ! ขอขอบคุณที่ขยายความหมายของการค้นหาระยะ Fwiw การสำรวจเส้นทางเต็มรูปแบบเหมือนกับการสืบค้นแบบช่วงระหว่าง min, max ซึ่งก็คือ O (k) โดยที่ k = n :)
Gregg Lind

14

อาจเป็นเรื่องที่ควรกล่าวถึงว่าตอนนี้เอกสาร bisect ให้ตัวอย่างการค้นหา: http://docs.python.org/library/bisect.html#searching-sorted-lists

(การเพิ่ม ValueError แทนที่จะส่งกลับ -1 หรือไม่มีเป็น pythonic มากกว่า - list.index () ทำเช่นนั้น แต่แน่นอนว่าคุณสามารถปรับตัวอย่างให้ตรงกับความต้องการของคุณได้)


11

ที่ง่ายที่สุดคือใช้bisectและตรวจสอบตำแหน่งหนึ่งกลับไปดูว่ามีรายการ:

def binary_search(a,x,lo=0,hi=-1):
    i = bisect(a,x,lo,hi)
    if i == 0:
        return -1
    elif a[i-1] == x:
        return i-1
    else:
        return -1

2
ดี แต่บาร์โค๊ดถ้าคุณไม่ผ่านค่า 'hi' ฉันจะเขียนแบบนี้: "def binary_search (a, x, lo = 0, hi = None): จากการนำเข้า bisect bisect i = bisect (a, x, lo, hi หรือ len (a)) return (i- 1 ถ้า a [i-1] == x else -1) "และทดสอบเช่นนี้:" สำหรับฉันในช่วง (1, 20): a = รายการ (ช่วง (i)) สำหรับ aa ใน a: j = binary_search (a, aa) ถ้า j! = aa: พิมพ์ i, aa, j "
hughdbrown

8

สิ่งนี้ถูกต้องจากคู่มือ:

http://docs.python.org/2/library/bisect.html

8.5.1 ค้นหารายการที่เรียงลำดับ

ฟังก์ชัน bisect ข้างต้นมีประโยชน์ในการค้นหาจุดแทรก แต่อาจยุ่งยากหรืออึดอัดใจที่จะใช้สำหรับงานการค้นหาทั่วไป ฟังก์ชันห้ารายการต่อไปนี้แสดงวิธีแปลงให้เป็นรายการค้นหามาตรฐานสำหรับรายการที่เรียงลำดับ:

def index(a, x):
    'Locate the leftmost value exactly equal to x'
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    raise ValueError

ดังนั้นด้วยการแก้ไขเล็กน้อยรหัสของคุณควรจะ:

def index(a, x):
    'Locate the leftmost value exactly equal to x'
    i = bisect_left(a, x)
    if i != len(a) and a[i] == x:
        return i
    return -1

6

ฉันยอมรับว่าคำตอบของ @ DaveAbrahamsโดยใช้โมดูล bisect เป็นวิธีที่ถูกต้อง เขาไม่ได้พูดถึงรายละเอียดที่สำคัญเพียงอย่างเดียวในคำตอบของเขา

จากเอกสาร bisect.bisect_left(a, x, lo=0, hi=len(a))

โมดูล bisection ไม่ต้องการให้อาร์เรย์ค้นหาทำการคำนวณล่วงหน้าล่วงหน้า คุณก็สามารถนำเสนออุปกรณ์ปลายทางไปbisect.bisect_leftแทนโดยใช้ค่าเริ่มต้นของและ0len(a)

สำคัญยิ่งกว่าสำหรับการใช้งานของฉันการค้นหาค่า X เพื่อให้ข้อผิดพลาดของฟังก์ชันที่กำหนดนั้นลดลง ในการทำเช่นนั้นฉันต้องการวิธีที่จะให้อัลกอริทึมของ bisect_left เรียกการคำนวณของฉันแทน มันง่ายจริงๆ

เพียงแค่ให้วัตถุที่กำหนด__getitem__เป็นa

ตัวอย่างเช่นเราสามารถใช้อัลกอริธึม bisect เพื่อค้นหาสแควร์รูทอย่างแม่นยำ!

import bisect

class sqrt_array(object):
    def __init__(self, digits):
        self.precision = float(10**(digits))
    def __getitem__(self, key):
        return (key/self.precision)**2.0

sa = sqrt_array(4)

# "search" in the range of 0 to 10 with a "precision" of 0.0001
index = bisect.bisect_left(sa, 7, 0, 10*10**4)
print 7**0.5
print index/(10**4.0)

ไม่สะอาด ใช้scipy.optimizeสำหรับสิ่งนี้
Neil G

4

หากคุณต้องการดูว่ามีอยู่หรือไม่ลองเปลี่ยนรายการเป็น dict:

# Generate a list
l = [n*n for n in range(1000)]

# Convert to dict - doesn't matter what you map values to
d = dict((x, 1) for x in l)

count = 0
for n in range(1000000):
    # Compare with "if n in l"
    if n in d:
        count += 1

บนเครื่องของฉัน "ถ้า n ใน l" ใช้เวลา 37 วินาทีในขณะที่ "ถ้า n ใน d" ใช้เวลา 0.4 วินาที


2
นั่นไม่ใช่ตัวเลือกที่ดีเสมอด้วยเหตุผลสองประการ: 1) dicts / sets ใช้หน่วยความจำมากขึ้น 2) ถ้าเขาไม่มีอะไรมากในรายการการค้นหาแบบไบนารีอาจเร็วกว่า 3) การแปลงรายการเป็น dict เป็นการดำเนินการ O (n) ในขณะที่การค้นหาแบบไบนารี่คือ O (log n)
เจสันเบเกอร์

3
ในฐานะที่เป็น FYI ค่าใช้จ่าย "เซ็ต" ในไพ ธ อนเมื่อเทียบกับรายการไพ ธ อนนั้นต่ำมาก และมันเร็วมากสำหรับการค้นหา ในกรณีที่การค้นหาแบบไบนารี่ดีเยี่ยมสำหรับการค้นหาช่วง
Gregg Lind

การแปลงรายการอาจเป็น O (n) แต่การเรียงลำดับข้อมูลในรายการซึ่งคุณต้องทำก่อนการค้นหาแบบไบนารีจะแย่กว่านั้น ข้อมูลมาจากที่ใดคุณอาจแทรกลงในพจนานุกรมเมื่อคุณไป ฉันยอมรับว่าหน่วยความจำอาจมีปัญหา
Mark Baker

4

อันนี้คือ:

  • ไม่ซ้ำ (ซึ่งทำให้หน่วยความจำมีประสิทธิภาพมากกว่าวิธีแบบเรียกซ้ำส่วนใหญ่)
  • ใช้งานได้จริง
  • รวดเร็วตั้งแต่ทำงานโดยไม่จำเป็นถ้ามีและเงื่อนไข
  • ตามการยืนยันทางคณิตศาสตร์ว่าพื้นของ (ต่ำ + สูง) / 2นั้นเล็กกว่าสูงเสมอโดยที่ต่ำคือขีด จำกัด ล่างและสูงคือขีด จำกัด บน

def binsearch(t, key, low = 0, high = len(t) - 1):
    # bisecting the range
    while low < high:
        mid = (low + high)//2
        if t[mid] < key:
            low = mid + 1
        else:
            high = mid
    # at this point 'low' should point at the place
    # where the value of 'key' is possibly stored.
    return low if t[low] == key else -1

คุณแชร์กรณีทดสอบได้ไหม
อายุการใช้งาน

2

ทางออกของ Dave Abrahams นั้นดี แม้ว่าฉันจะได้ทำมันเรียบง่าย:

def binary_search(L, x):
    i = bisect.bisect_left(L, x)
    if i == len(L) or L[i] != x:
        return -1
    return i

2

ในขณะที่ไม่มีอัลกอริทึมการค้นหาแบบไบนารีที่ชัดเจนใน Python มีโมดูลbisect- ออกแบบมาเพื่อค้นหาจุดแทรกสำหรับองค์ประกอบในรายการที่เรียงลำดับโดยใช้การค้นหาแบบไบนารี นี่สามารถ "หลอก" เพื่อทำการค้นหาแบบไบนารี ข้อได้เปรียบที่ใหญ่ที่สุดของข้อนี้คือข้อได้เปรียบเดียวกับที่ไลบรารี่ส่วนใหญ่มี - มันมีประสิทธิภาพสูงผ่านการทดสอบและใช้งานได้ดี (การค้นหาแบบไบนารี่โดยเฉพาะอาจทำได้ยากมากโดยเฉพาะอย่างยิ่ง

ประเภทพื้นฐาน

สำหรับประเภทพื้นฐานเช่น Strings หรือ ints ค่อนข้างง่าย - สิ่งที่คุณต้องการคือbisectโมดูลและรายการเรียงลำดับ:

>>> import bisect
>>> names = ['bender', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> bisect.bisect_left(names, 'fry')
1
>>> keyword = 'fry'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
True
>>> keyword = 'arnie'
>>> x = bisect.bisect_left(names, keyword)
>>> names[x] == keyword
False

คุณสามารถใช้สิ่งนี้เพื่อค้นหาสิ่งที่ซ้ำกัน:

...
>>> names = ['bender', 'fry', 'fry', 'fry', 'leela', 'nibbler', 'zoidberg']
>>> keyword = 'fry'
>>> leftIndex = bisect.bisect_left(names, keyword)
>>> rightIndex = bisect.bisect_right(names, keyword)
>>> names[leftIndex:rightIndex]
['fry', 'fry', 'fry']

เห็นได้ชัดว่าคุณสามารถกลับดัชนีมากกว่าค่าที่ดัชนีนั้นหากต้องการ

วัตถุ

สำหรับประเภทหรือวัตถุที่กำหนดเองสิ่งต่าง ๆ นั้นค่อนข้างซับซ้อน: คุณต้องแน่ใจว่าได้ใช้วิธีการเปรียบเทียบที่สมบูรณ์เพื่อให้มีการแบ่งออกเป็นสองส่วนเพื่อเปรียบเทียบอย่างถูกต้อง

>>> import bisect
>>> class Tag(object):  # a simple wrapper around strings
...     def __init__(self, tag):
...         self.tag = tag
...     def __lt__(self, other):
...         return self.tag < other.tag
...     def __gt__(self, other):
...         return self.tag > other.tag
...
>>> tags = [Tag('bender'), Tag('fry'), Tag('leela'), Tag('nibbler'), Tag('zoidbe
rg')]
>>> key = Tag('fry')
>>> leftIndex = bisect.bisect_left(tags, key)
>>> rightIndex = bisect.bisect_right(tags, key)
>>> print([tag.tag for tag in tags[leftIndex:rightIndex]])
['fry']

สิ่งนี้ควรใช้งานได้อย่างน้อย Python 2.7 -> 3.3


1

การใช้ dict นั้นไม่ต้องการการใช้งานหน่วยความจำของคุณเป็นสองเท่าเว้นแต่วัตถุที่คุณจัดเก็บมีขนาดเล็กมากเนื่องจากค่านั้นเป็นเพียงตัวชี้ไปยังวัตถุจริง:

>>> a = 'foo'
>>> b = [a]
>>> c = [a]
>>> b[0] is c[0]
True

ในตัวอย่างนั้น 'foo' จะถูกจัดเก็บเพียงครั้งเดียว นั่นสร้างความแตกต่างให้กับคุณหรือไม่? เราพูดถึงรายการกันกี่รายการกันแน่?


มันเกี่ยวกับตัวเลขและจำนวนมาก :) ฉันต้องการใช้อาร์เรย์เกือบใหญ่เท่ากับหน่วยความจำคอมพิวเตอร์ ฉันรู้ว่าปัญหาพื้นฐานของฉันอาจผิด แต่ฉันอยากรู้เกี่ยวกับการขาดวิธีการค้นหาแบบไบนารี
rslite

1
คุณไม่สามารถมีวัตถุสำคัญเล็กพอที่จะถือว่าเป็น "เล็กจริง ๆ " ที่นี่ วัตถุจะมีต้นทุนขั้นต่ำ 3 คำ (ประเภท refcount, payload) ในขณะที่รายการเพิ่ม 1 คำชุดเพิ่ม 1 คำและ dict เพิ่ม 2 คำ ทั้งสาม (list / set / dict) preallocate ช่องว่างในแฟชั่นบางอย่างเช่นกันซึ่งเป็นตัวคูณอื่น แต่ก็ยังไม่เพียงพอที่จะสำคัญ
Rhamphoryncus

1

รหัสนี้ทำงานกับรายการจำนวนเต็มในลักษณะวนซ้ำ ค้นหาสถานการณ์กรณีที่ง่ายที่สุดซึ่งก็คือ: ความยาวของรายการน้อยกว่า 2 ซึ่งหมายความว่าคำตอบนั้นมีอยู่แล้วและมีการทดสอบเพื่อตรวจสอบคำตอบที่ถูกต้อง หากไม่มีค่ากลางจะถูกตั้งค่าและทดสอบว่าถูกต้องหากไม่มีการแบ่งเป็นสองส่วนโดยการเรียกฟังก์ชันอีกครั้ง แต่ตั้งค่ากลางเป็นขีด จำกัด บนหรือล่างโดยเลื่อนไปทางซ้ายหรือขวา

def binary_search (intList, intValue, lowValue, highValue):
    if (highValue - lowValue) <2:
        return intList [lowValue] == intValue หรือ intList [highValue] == intValue
    middleValue = lowValue + ((highValue - lowValue) / 2)
    if intList [middleValue] == intValue:
        กลับ True
    ถ้า intList [middleValue]> intValue:
        ส่งกลับ binary_search (intList, intValue, lowValue, middleValue - 1)
   ส่งกลับ binary_search (intList, intValue, middleValue + 1, highValue)

1

ดูตัวอย่างวิกิพีเดียที่http://en.wikipedia.org/wiki/Binary_search_algorithm

def binary_search(a, key, imin=0, imax=None):
    if imax is None:
        # if max amount not set, get the total
        imax = len(a) - 1

    while imin <= imax:
        # calculate the midpoint
        mid = (imin + imax)//2
        midval = a[mid]

        # determine which subarray to search
        if midval < key:
            # change min index to search upper subarray
            imin = mid + 1
        elif midval > key:
            # change max index to search lower subarray
            imax = mid - 1
        else:
            # return index number 
            return mid
    raise ValueError

0
'''
Only used if set your position as global
'''
position #set global 

def bst(array,taget): # just pass the array and target
        global position
        low = 0
        high = len(array)
    while low <= high:
        mid = (lo+hi)//2
        if a[mid] == target:
            position = mid
            return -1
        elif a[mid] < target: 
            high = mid+1
        else:
            low = mid-1
    return -1

ฉันเดาว่านี่จะดีกว่าและมีประสิทธิภาพมาก โปรดแก้ไขฉัน :) ขอบคุณ


0
  • s เป็นรายการ
  • binary(s, 0, len(s) - 1, find) คือการโทรเริ่มต้น
  • ฟังก์ชันส่งคืนดัชนีของรายการที่สอบถาม -1ถ้าไม่มีรายการดังกล่าวก็จะส่งกลับ

    def binary(s,p,q,find):
        if find==s[(p+q)/2]:
            return (p+q)/2
        elif p==q-1 or p==q:
            if find==s[q]:
                return q
            else:
                return -1
        elif find < s[(p+q)/2]:
            return binary(s,p,(p+q)/2,find)
        elif find > s[(p+q)/2]:
            return binary(s,(p+q)/2+1,q,find)

0
def binary_search_length_of_a_list(single_method_list):
    index = 0
    first = 0
    last = 1

    while True:
        mid = ((first + last) // 2)
        if not single_method_list.get(index):
            break
        index = mid + 1
        first = index
        last = index + 1
    return mid

0

ค้นหาแบบทวิภาค:

// List - values inside list
// searchItem - Item to search
// size - Size of list
// upperBound - higher index of list
// lowerBound - lower index of list
def binarySearch(list, searchItem, size, upperBound, lowerBound):
        print(list)
        print(upperBound)
        print(lowerBound)
        mid = ((upperBound + lowerBound)) // 2
        print(mid)
        if int(list[int(mid)]) == value:
               return "value exist"
        elif int(list[int(mid)]) < value:
             return searchItem(list, value, size, upperBound, mid + 1)
        elif int(list[int(mid)]) > value:
               return searchItem(list, value, size, mid - 1, lowerBound)

// เพื่อเรียกใช้ฟังก์ชันข้างต้น:

list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
searchItem = 1        
print(searchItem(list[0], item, len(list[0]) -1, len(list[0]) - 1, 0))

0

ฉันต้องการค้นหาแบบไบนารีในไพ ธ อนและแบบทั่วไปสำหรับรุ่น Django ในโมเดล Django โมเดลหนึ่งสามารถมี foreign key ไปยังโมเดลอื่นได้และฉันต้องการค้นหาบางอย่างเกี่ยวกับวัตถุโมเดลที่ดึงข้อมูลมา ฉันเขียนฟังก์ชันต่อไปนี้คุณสามารถใช้สิ่งนี้

def binary_search(values, key, lo=0, hi=None, length=None, cmp=None):
    """
    This is a binary search function which search for given key in values.
    This is very generic since values and key can be of different type.
    If they are of different type then caller must specify `cmp` function to
    perform a comparison between key and values' item.
    :param values:  List of items in which key has to be search
    :param key: search key
    :param lo: start index to begin search
    :param hi: end index where search will be performed
    :param length: length of values
    :param cmp: a comparator function which can be used to compare key and values
    :return: -1 if key is not found else index
    """
    assert type(values[0]) == type(key) or cmp, "can't be compared"
    assert not (hi and length), "`hi`, `length` both can't be specified at the same time"

    lo = lo
    if not lo:
        lo = 0
    if hi:
        hi = hi
    elif length:
        hi = length - 1
    else:
        hi = len(values) - 1

    while lo <= hi:
        mid = lo + (hi - lo) // 2
        if not cmp:
            if values[mid] == key:
                return mid
            if values[mid] < key:
                lo = mid + 1
            else:
                hi = mid - 1
        else:
            val = cmp(values[mid], key)
            # 0 -> a == b
            # > 0 -> a > b
            # < 0 -> a < b
            if val == 0:
                return mid
            if val < 0:
                lo = mid + 1
            else:
                hi = mid - 1
    return -1

0

วิธีแก้ปัญหาที่ดีหลายข้อด้านบน แต่ฉันไม่ได้เห็นง่าย ๆ (KISS ทำให้มันง่าย (เพราะฉัน) การใช้ Python ที่สร้างขึ้นใน / ฟังก์ชัน bisect ทั่วไปเพื่อทำการค้นหาแบบไบนารีด้วยรหัสบิตรอบฟังก์ชัน bisect ฉันคิดว่าฉันมีตัวอย่างด้านล่างที่ฉันได้ทดสอบทุกกรณีสำหรับชื่อของสตริงขนาดเล็กโซลูชันข้างต้นบางอย่างอ้างถึง / พูดแบบนี้ แต่หวังว่ารหัสง่ายๆด้านล่างจะช่วยให้ทุกคนสับสนเหมือนฉัน

Python bisect ใช้เพื่อระบุตำแหน่งที่จะแทรกรายการ / ค่าการค้นหาใหม่ลงในรายการที่เรียงลำดับ โค้ดด้านล่างซึ่งใช้ bisect_left ซึ่งจะส่งคืนดัชนีของการเข้าชมหากพบรายการการค้นหาในรายการ / อาร์เรย์ (หมายเหตุ bisect และ bisect_right จะส่งคืนดัชนีขององค์ประกอบหลังจากการเข้าชมหรือการจับคู่เป็นจุดแทรก) หากไม่พบ bisect_left จะส่งกลับดัชนีไปยังรายการถัดไปในรายการที่เรียงซึ่งจะไม่ == ค่าการค้นหา อีกกรณีหนึ่งคือรายการค้นหาจะไปที่ส่วนท้ายของรายการซึ่งดัชนีที่ส่งคืนนั้นจะอยู่นอกเหนือส่วนท้ายของรายการ / อาร์เรย์และในรหัสด้านล่างทางออกแรกโดย Python ด้วย "และ" การจัดการเชิงตรรกะ (เงื่อนไขแรก False Python ไม่ได้ตรวจสอบเงื่อนไขที่ตามมา)

#Code
from bisect import bisect_left
names=["Adam","Donny","Jalan","Zach","Zayed"]
search=""
lenNames = len(names)
while search !="none":
    search =input("Enter name to search for or 'none' to terminate program:")
    if search == "none":
        break
    i = bisect_left(names,search)
    print(i) # show index returned by Python bisect_left
    if i < (lenNames) and names[i] == search:
        print(names[i],"found") #return True - if function
    else:
        print(search,"not found") #return False – if function
##Exhaustive test cases:
##Enter name to search for or 'none' to terminate program:Zayed
##4
##Zayed found
##Enter name to search for or 'none' to terminate program:Zach
##3
##Zach found
##Enter name to search for or 'none' to terminate program:Jalan
##2
##Jalan found
##Enter name to search for or 'none' to terminate program:Donny
##1
##Donny found
##Enter name to search for or 'none' to terminate program:Adam
##0
##Adam found
##Enter name to search for or 'none' to terminate program:Abie
##0
##Abie not found
##Enter name to search for or 'none' to terminate program:Carla
##1
##Carla not found
##Enter name to search for or 'none' to terminate program:Ed
##2
##Ed not found
##Enter name to search for or 'none' to terminate program:Roger
##3
##Roger not found
##Enter name to search for or 'none' to terminate program:Zap
##4
##Zap not found
##Enter name to search for or 'none' to terminate program:Zyss
##5
##Zyss not found
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.