使用二进制搜索查找立方体根的算法

174 阅读3分钟

在这篇文章中,我们解释了使用二进制搜索寻找立方体根的算法,以及时间和空间的复杂性分析。

目录:

  1. 简介
  2. 使用二进制搜索寻找立方根
    • 完美的立方体
    • 精确的立方体根
  3. 时间和空间复杂度

前提条件:

简介

当有人需要找到一个数字的立方体时,他们只需将这个数字乘以它自己三次,然后,唉!他们就得到了答案。他们就得到了答案。但是,当人们需要从另一个方向(寻找立方根)时,他们没有直接的公式来寻找这个数字,因此,需要在整个[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 在两个完美立方体827 之间,因为这样,上面的循环将返回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,endmid 为0, 9 和 4。

注意,start,end, 和mid 都不是小数,原因是我不想处理浮点数。用浮点数计算mid 是一个混乱的过程,而且,这种方法使它更容易捕捉到一个较小的值,如果它没有找到任何值,形成一个完美立方根的结果。

总之,因为i = 1 ,所以这里我们要处理的是 10-1精度,意味着我们要在2.02.9 之间寻找完美的或小的,在这一点上,当循环将采取startmid5start != mid 将打破循环,并将进行 10-2.

因此,它将在2.502.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的这篇文章,你一定对使用二进制搜索寻找立方根有了完整的了解。