在这篇文章中,我们解释了使用二进制搜索寻找立方体根的算法,以及时间和空间的复杂性分析。
目录:
- 简介
- 使用二进制搜索寻找立方根
- 完美的立方体
- 精确的立方体根
- 时间和空间复杂度
前提条件:
简介
当有人需要找到一个数字的立方体时,他们只需将这个数字乘以它自己三次,然后,唉!他们就得到了答案。他们就得到了答案。但是,当人们需要从另一个方向(寻找立方根)时,他们没有直接的公式来寻找这个数字,因此,需要在整个[1, N]的空间内搜索这个数字。
漫步在线性搜索的小路上
如果你愿意,你可以跳过这一步,去找解决方案
所以,我很好奇,为什么不是线性搜索?我的意思是,它是∛N。也许一个数学家看一眼就知道哪一个是小的或大的∛N或 log2N.
那么在我的例子中,我去把一堆数字的log ,然后把它们放在一起比较,得到了以下输出。
from math import ceil, log
for i in range(1, 30, 2):
print("{} - {} | {} - {}".format(
i,
ceil(log(i**3, 2)),
i+1,
ceil(log((i+1)**3, 2))
)
)
1 - 0 | 2 - 3
3 - 5 | 4 - 6
5 - 7 | 6 - 8
7 - 9 | 8 - 9
9 - 10 | 10 - 10
11 - 11 | 12 - 11
13 - 12 | 14 - 12
15 - 12 | 16 - 12
17 - 13 | 18 - 13
19 - 13 | 20 - 13
21 - 14 | 22 - 14
23 - 14 | 24 - 14
25 - 14 | 26 - 15
27 - 15 | 28 - 15
29 - 15 | 30 - 15
结论:
随着你的深入,日志开始变得越来越小,因此。
log2N<∛N。所以,二进制搜索会比线性搜索更好。
好了,回到主题...
使用二进制搜索寻找立方体根
完美的立方体
如果我们要为一个完美的立方体寻找立方根,那么我们可以直接在[1,N]的空间上应用二进制搜索,然后返回x ,其中x ,等于 x3.
这应该看起来像下面这样:
1 def perfect_cube_root(N):
2 start = 1
3 end = N
4 mid = (start + end) // 2
5
6 while mid != start and mid**3 != N:
7 if mid**3 > N:
8 end = mid
9 else:
10 start = mid
11 mid = (start + end) // 2
12 return mid
(mid != start) and 条件,在while循环中,有助于在循环没有找到完美立方根的情况下返回一个较小的值。如果没有这个条件,该循环将无限地运行。
因此,举例来说:
如果我们做perfect_cube_root(9) ,因为9 在两个完美立方体8 和27 之间,因为这样,上面的循环将返回2 ,它是8 的立方根。
为什么这样写这个函数呢?
好吧,我们将在下一个cube_root 的精度函数中使用它。
带精度的立方根
现在要找到立方根的十进制值,我们可以先用perfect_cube_root(N) 搜索一个较小的结果,如果它返回一个完美的立方根,那么就返回它或继续搜索精度。
这里的想法是找到一个十进制的值,它将是一个完美的立方根或一个最接近的小值。
换句话说,我们将在.0 和.9 之间搜索,一个完美的匹配或最接近的较小值,然后在.0 和.9 的情况下降低功率,另一个搜索将发生在.00 和.09 之间。
当上述搜索发生时,我们应该不断地将找到的值添加到结果中,并提高精度。
上述想法的代码应该像下面这样:
1 def cube_root(N, precision=5):
2 P = lambda a, b: a * 10 ** (-b)
3 result = perfect_cube_root(N)
4 if result**3 == N:
5 return result
6
7 for i in range(1, precision + 1):
8 start = 0
9 end = 9
10 mid = (start + end) // 2
11
12 cube = lambda: (result + P(mid, i))**3
13
14 while start != mid:
15 if cube() > N:
16 end = mid
17 elif cube() < N:
18 start = mid
19 elif cube() == N:
20 return result + P(mid, i)
21
22 mid = (start + end) // 2
23
24 result += P(mid, i)
25
26 return round(result, precision)
让我们看一个例子,
如果我们运行cube_root(16.003008) ,它应该返回2.52 。
首先,cube_root 将调用perfect_cube_root ,并且肯定会得到2 的回报。
现在在line 7 ,我们开始for-loop,范围为[1, precision + 1],并初始化start,end 和mid 为0, 9 和 4。
注意,start,end, 和mid 都不是小数,原因是我不想处理浮点数。用浮点数计算mid 是一个混乱的过程,而且,这种方法使它更容易捕捉到一个较小的值,如果它没有找到任何值,形成一个完美立方根的结果。
总之,因为i = 1 ,所以这里我们要处理的是 10-1精度,意味着我们要在2.0 和2.9 之间寻找完美的或小的,在这一点上,当循环将采取start 和mid 在5 ,start != mid 将打破循环,并将进行 10-2.
因此,它将在2.50 和2.59 之间开始搜索,在这个循环中,将找到2.52 作为一个完美的匹配,并且,line 20 将执行,这将把0.02 ( P(mid, i) ) 添加到result 并返回。
时间和空间复杂度
时间复杂度
首先,我们进行纯粹的二进制搜索,应该花费O(logN)时间。
然后,我们在[0, 9]的空间上进行二进制搜索,进行P次,
总的来说,Plog(10),也就是4P。
时间复杂度=O(logN+P)
空间复杂度
该函数本身需要一个数字,并创建一个恒定数量的变量,使用一个恒定数量的空间,
因此:
- 空间复杂度 =O(1)
通过OpenGenus的这篇文章,你一定对使用二进制搜索寻找立方根有了完整的了解。