bisect模块包含两个主要函数: bisect和insort。 bisect和insort在内部利用了二分搜索算法,分别用于搜索和插入规则数组中的元素。
bisect /basekt/
将to divide sth into two equal parts分成两半; 平分
1 bisect函数
Luciano Ramalho举了在这样的干草堆中捞针的例子,说明了bisect.bisect和bisect.bisect_left的用法。
haystack=[ 1,4,5,6,8,12,15,20,21,23,26,29,30 ]
need les=[ 0,1,2,5,8,10,22,23,29,30,31 ]
row _ fmt=' { 0:2 d } @ { 1:2 d } {2} { 0:2 d }
defdemo(bisect_fn ) :
forNeedleinreversed(Needles ) :
position=bisect_fn(HayStack,needle ) ) ) ) ) ) )。
offset=position * ' |'
print(row_fmt.format(Needle,position,offset ) )
if __name__=='__main__':
if sys.argv[-1]=='left':
bisect_fn=bisect.bisect_left
else:
bisect_fn=bisect.bisect
print(demo: ),bisect_fn.__name__ ) ) ) )。
print('Haystack-',' '.join ) '-'%nforninHaystack ) )
demo(bisect_fn ) )为
执行结果:
DEMO: bisect_right
haystack-14568 12 15 20 21 23 23 26 29 30
31 @ 14|||||||||||||||||| 31
30 @ 14|||||||||||30
29 @ 13|||||||||||||29
23 @ 11|||||||||||| 23
22 @9||||||||||22
10 @ 5 | | | | |10
8 @ 5 | | | | |8
5 @ 3 | | |5
2 @ 1 |2
1 @ 1 |1
0@0
Python函数的一个特征是可以将函数名称用作参数,如示例中的bisect_fn所示。 这样,函数就更加灵活,可以动态地将函数名称作为程序执行参数加载。
HAYSTACK是一堆干草,NEEDLES是一堆针。 在干草堆里捞针,本质上是在已经确定顺序的数列里寻找某个数。
自定义的demo(bisect_fn )函数,首先计算position,然后利用位置计算需要多少分隔符作为打印偏移,并以最后定义的格式打印。
str.format ) )用于设置字符串的格式,并可以指定参数的位置。 语法如{0:2d}中的0表示第一个条目,2d表示总长度,不足时以空格作为占位符。 d表示十进制的有符号整数。
str.format ) )格式还可以设置对齐方式。 ^、居中、左对齐和右对齐。 因此,{0:2d}表示第一个条目左对齐,占两位数的有十进制符号整数。
__name__是python的内置类属性,存在于表示相应程序名称的python程序中。 对于主线程,其内置名称为__main__。
如果在程序运行时添加left参数,则会在程序定制的函数中调用bisect_left函数。 bisect函数实际上是bisect_right函数的别名。
bisect_left函数与bisect函数的区别如下:
bisect_left函数返回与原始序列中插入的元素相等的元素的位置。 插入新元素时,新元素将位于等效元素之前。
2.bisect函数返回与原始序列中插入的元素相等的元素之后的位置。 插入新元素时,新元素将被放置在等效元素之后。
bisect_left函数的执行结果:
DEMO: bisect_left
haystack-14568 12 15 20 21 23 23 26 29 30
31 @ 14 |
| | | | | | | | | | | | |3130 @ 13 | | | | | | | | | | | | |30
29 @ 12 | | | | | | | | | | | |29
23 @ 9 | | | | | | | | |23
22 @ 9 | | | | | | | | |22
10 @ 5 | | | | |10
8 @ 4 | | | |8
5 @ 2 | |5
2 @ 1 |2
1 @ 0 1
0 @ 0 0
python 官方文档还举了一个利用 bisect 函数,来输出考试成绩的示例程序:
def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
i = bisect.bisect(breakpoints, score)
return grades[i]
if __name__ == '__main__':
results = [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
logging.info('results -> %s', results)
运行结果:
INFO - results -> ['F', 'A', 'C', 'C', 'B', 'A', 'A']
自定义的 grade() 定义了三个参数:
参数名
说明
score
考试分数
breakpoints
分数等级边界值;这里分为 5 档;90 及以上、80 ~ 89、70 ~ 79、60 ~ 69 以及 60 以下。
grades
评测分范围。
grade() 函数首先根据传入的分数,通过 bisect() 函数找出其所在位置,然后把这一位置传入 grades 序列得到评测分。
在主线程中,通过 for in 语法迭代表示学生成绩的序列,把成绩传入 grade() 函数计算出评测分,最后通过序列一次性输出。
2 insort 函数
因为排序是一项很耗时的工作,所以对于一个有序的序列来说,新增一个元素时,最好是仍然保持有序。 insort 函数在插入时,会确保这个序列始终有序。
SIZE=10
my_list=[]
for i in range(SIZE):
new_item=random.randrange(SIZE*3)
bisect.insort(my_list,new_item)
print('%2d -> '% new_item,my_list)
运行结果:
18 -> [18]
8 -> [8, 18]
21 -> [8, 18, 21]
5 -> [5, 8, 18, 21]
19 -> [5, 8, 18, 19, 21]
13 -> [5, 8, 13, 18, 19, 21]
20 -> [5, 8, 13, 18, 19, 20, 21]
4 -> [4, 5, 8, 13, 18, 19, 20, 21]
15 -> [4, 5, 8, 13, 15, 18, 19, 20, 21]
2 -> [2, 4, 5, 8, 13, 15, 18, 19, 20, 21]
randrange() 会返回给定入参范围内的随机数,但不包括边界值。
可以看到,每次插入时,序列始终保持有序。
print('%2d -> '% new_item,my_list) 采用了 %s 格式化语法,%2d 定义了 new_item 值的格式,而 my_list 会自动挂在格式之后。所以这里在第二个百分号之后没有加上括号,圈出需要格式化的参数。
insort 也有个兄弟叫 insort_left,底层使用的是 bisect_left。insort_left 函数会把新元素放置在与它相等的元素前面。
另外 bisect 函数与insort 函数,都有两个可选参数(lo 与 hi),利用它们可以缩小需要查找的序列范围。lo 的默认值是 0,hi 的默认值是序列的长度。