January Star
  • Home
  • Categories
  • Tags
  • Archives

bisect标准库源码学习

Contents

  • 简介
  • bisect&&bisect_right
  • bisect_left
  • insort&&insort_right
  • insort_left

简介

这个库还是比较简单的。

基本的要点有两个:

  1. 它内部的所有操作都使用 二分查找法
  2. 它操作的所有序列都要求为升序,且操作最好为线性表(即数组,如果使用 list,性能不佳)

它提供的函数分为两类:

  1. bisect bisect_left bisect_right

    查找某元素在序列中的插入位置

    如果某元素在序列中已存在, bisect_left 则返回该元素左边的位置, bisect_right 返回该元素右边的位置

    1
    bisect = bisect_right   # 向后兼容 
    
  2. insort insort_left insort_right

    将某元素插入到序列中

    如果某元素在序列中已存在 , insort_left 插入到该元素的左边,insort_right 插入到该元素的右边

    1
    insort = insort_right   # 向后兼容 
    

当然,如果直接使用 Python 来实现这些功能,性能是不怎么样的,所以在源码中,一开始给出了 Python 版本的定义,但是在文件最后,还是会尝试导入 C 语言版本。

1
2
3
4
try:
    from _bisect import *
except ImportError:
    pass

bisect&&bisect_right

bisect_right 的签名还是很简单的:

a 为操作的序列

x 为某元素

lo hi 用来指定 a 中的开始查找和结束查找位置,默认是指整个 a 序列

原代码实现(Python 版本):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def bisect_right(a, x, lo=0, hi=None):
    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2 # 使用 floor 除法,保证 a[mid] 不会抛出 IndexError
        if x < a[mid]: hi = mid
        else: lo = mid+1
    return lo

上面的源代码没啥好解释的,知道 二分查找 是啥,基本就能懂了。

但是我还是想用递归思想重写一遍。

因为二分查找的算法定义是什么样子,它对应的代码就是什么样子,能够和定义一一对应,一眼就能看明白。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def help_mybisect_right(a, x, lo, hi):
    if lo > hi:
        return lo

    mid_index = (lo + hi) // 2
    mid = a[mid_index]
    if x == mid:
        return mid_index + 1
    elif x < mid:
        return help_mybisect_right(a, x, lo, mid_index - 1)
    else:
        return help_mybisect_right(a, x, mid_index + 1, hi)

def mybisect_right(a, x, lo=0, hi=None):
    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a) - 1
    return help_mybisect_right(a, x, lo, hi)

嗯,咱们比较一下两者的性能。

1
2
3
4
5
6
7
8
9
In [164]: import array

In [165]: a = array.array("i", xrange(1000))

In [178]: %timeit -n10000 -r100 bisect_right(a, 7)
10000 loops, best of 100: 1.45 µs per loop

In [179]: %timeit -n10000 -r100 mybisect_right(a, 7)
10000 loops, best of 100: 2.86 µs per loop

性能相差两倍左右 ... ...,SO,我还挺希望 Python 的解释器有尾递归优化的,甚至尾调用优化。

bisect_left

原代码实现(Python 版本):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def bisect_left(a, x, lo=0, hi=None):
    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    while lo < hi:
        mid = (lo+hi)//2
        if a[mid] < x: lo = mid+1
        else: hi = mid
    return lo

使用递归来实现一下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
def help_mybisect_left(a, x, lo, hi):
    if lo > hi:
        return lo

    mid_index = (lo + hi) // 2
    mid = a[mid_index]
    if x == mid:
        return mid_index
    elif x < mid:
        return help_mybisect_left(a, x, lo, mid_index - 1)
    else:
        return help_mybisect_left(a, x, mid_index + 1, hi)

def mybisect_left(a, x, lo=0, hi=None):
    if lo < 0:
        raise ValueError('lo must be non-negative')
    if hi is None:
        hi = len(a)
    return help_mybisect_left(a, x, lo, hi)

help_mybisect_left help_mybisect_right 大体差不多么,将公共部分抽出来,用连续的概念,再添加一个参数 continue_func 。用来处理 help_mybisect 的结果。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def help_mybisect(a, x, lo, hi, continue_func):
    if lo > hi:
        return lo

    mid_index = (lo + hi) // 2
    mid = a[mid_index]
    if x == mid:
        return continue_func(mid_index)
    elif x < mid:
        return help_mybisect_left(a, x, lo, mid_index - 1)
    else:
        return help_mybisect_left(a, x, mid_index + 1, hi)

help_mybisect_left = lambda a, x, lo, hi: help_mybisect(a, x, lo, hi, lambda num: num)

help_mybisect_right = lambda a, x, lo, hi: help_mybisect(a, x, lo, hi, lambda num: num + 1)

insort&&insort_right

这个函数的代码基本上就是先用 bisect_right 查找出需要插入的位置,然后调用 a.insert 即可。

insort_left

这个函数的代码基本上就是先用 bisect_left 查找出需要插入的位置,然后调用 a.insert 即可。

Comments
comments powered by Disqus

Published

Sep 24, 2014

Category

Python

Tags

  • python 23
  • stdlibs 15

Contact

  • Powered by Pelican. Theme: Elegant by Talha Mansoor