二分查找经常被称赞为在有序数据集中查找元素的最高效算法之一。它快得令人咋舌——而且随着搜索范围的扩大,它的效率优势更加突出。但在计算机每秒能完成数百万次运算的世界里,我们又该如何真正感受到它的速度呢?
让我们换个思路:我们故意通过在每次循环中添加一秒钟的延迟来故意放慢算法的速度。这个简单的变化将帮助我们体验二分查找的强大,并理解其对数效率是如何随着数据集大小的变化。
什么是二分搜索??
在开始看代码之前,我们快速回顾一下二分查找。给定一个已排序的列表,二分查找在每一步都将列表分成两部分,并检查目标值是在左侧还是右侧。这个过程会一直进行,直到找到目标值或搜索空间缩小为零。
这里高效的原因是:每一步中,搜索空间的大小都会减少一半。例如:
- 1,000 个元素的列表最多需要 10 次比较。
- 1 百万个元素的列表呢?也只需要 20 次比较。
- 10 亿个元素的列表呢?也只需要 30 次比较。
为了使二分查找的效率变得更具体,我们将通过在每次迭代间插入一秒延迟来修改算法。这样,花费的时间将直接反映出执行的步骤数。观察算法处理不同大小的数据集的过程,我们将获得对其扩展能力的直观感受。
这是实现,
import time
import sys
def binary_search(arr, target):
"""此函数执行带有每轮1秒延迟的二分查找。"""
low, high = 0, len(arr) - 1
iterations = 0 # 记录迭代次数
while low <= high:
iterations += 1
mid = (low + high) // 2
print(f"Iteration {iterations}: 正在检查索引 {mid},其值为 {arr[mid]}")
# 人工延迟以模拟时间流逝
time.sleep(1)
if arr[mid] == target:
print(f"在经过 {iterations} 秒后找到了 {target},索引为 {mid}。")
return mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
print(f"在经过 {iterations} 秒后未能找到 {target}。")
return -1
if __name__ == "__main__":
# 解析命令行参数
if len(sys.argv) != 3:
print("用法:python binary_search.py <array_size> <target_value>")
sys.exit(1)
try:
array_size = int(sys.argv[1])
target = int(sys.argv[2])
except ValueError:
print("array_size 和 target_value 必须是整数。")
sys.exit(1)
# 接下来生成一个大小为 {array_size} 的排序数组
array = list(range(array_size))
print(f"接下来将在大小为 {array_size} 的数组中查找 {target},请稍等...\n")
# 运行二分查找
binary_search(array, target)
按一下全屏,再按一下退出全屏
如何运行这个脚本?
python binary_search.py <长度> <目标>
全屏查看;退出全屏
例子
10个要素
% python3 script.py 10 4
开始在大小为10的数组中寻找数字4,使用二分查找算法
第一次迭代:检查索引4(值为4)
1秒后,在索引4处找到了数字4。
进入全屏,退出全屏
1000个元素
% python3 script.py 1000 572
开始在一个大小为1000的数组中寻找572,使用二分查找算法...
第一次迭代:检查索引499(值:499)
第二次迭代:检查索引749(值:749)
第三次迭代:检查索引624(值:624)
第四次迭代:检查索引561(值:561)
第五次迭代:检查索引592(值:592)
第六次迭代:检查索引576(值:576)
第七次迭代:检查索引568(值:568)
第八次迭代:检查索引572(值:572)
经过大约8秒,在索引572处找到了572。
切换到全屏 切换回正常模式
100万个元素
% python3 script.py 1000000 78246
开始在大小为 1000000 的数组中寻找数字 78246(使用二分查找算法)...
第 1 轮:检查索引 499999(值:499999)
第 2 轮:检查索引 249999(值:249999)
第 3 轮:检查索引 124999(值:124999)
第 4 轮:检查索引 62499(值:62499)
第 5 轮:检查索引 93749(值:93749)
第 6 轮:检查索引 78124(值:78124)
第 7 轮:检查索引 85936(值:85936)
第 8 轮:检查索引 82030(值:82030)
第 9 轮:检查索引 80077(值:80077)
第 10 轮:检查索引 79100(值:79100)
第 11 轮:检查索引 78612(值:78612)
第 12 轮:检查索引 78368(值:78368)
第 13 轮:检查索引 78246(值:78246)
在 13 秒后找到数字 78246,索引位置为 78246。
全屏
退出全屏
10亿项
% python3 script.py 1000000000 708246
开始在大小为10亿的数组中进行二分查找708246...
第1步:检查索引499999999(值:499999999)
第2步:检查索引249999999(值:249999999)
第3步:检查索引124999999(值:124999999)
第4步:检查索引62499999(值:62499999)
第5步:检查索引31249999(值:31249999)
第6步:检查索引15624999(值:15624999)
第7步:检查索引7812499(值:7812499)
...
第25步:检查索引708248(值:708248)
第26步:检查索引708233(值:708233)
第27步:检查索引708240(值:708240)
第28步:检查索引708244(值:708244)
第29步:检查索引708246(值:708246)
在29秒后,在索引708246处找到了数字708246。
切换到全屏 退出全屏
这很重要这一实验(希望如此)展示了二分查找的优美之处。通过减慢其速度,我们使二分查找的对数特性更易于观察和欣赏。无论数据集多么庞大,所需的步骤数增长速度都是可控的。
自己试一试运行代码,调整数据集的大小,并实验不同的目标值。如果你正在教别人算法或自己学习算法,这种慢速的二分查找版本可能是一个让他们“体验”到这种算法的好方法。
你觉得这种方法怎么样?你还有哪些创新的方式来理解算法?在下面的评论区留言分享吧!
点击查看更多内容
为 TA 点赞
评论
共同学习,写下你的评论
评论加载中...
作者其他优质文章
正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦