剑指offer----丑数

题目:我们把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。求从小到大的顺序的第 1500个丑数。

举例说明:

例如 6、8 都是丑数,但 14 不是,它包含因子 7。习惯上我们把 1 当做第一个丑数。

看到题目后第一反应就是在纸上写了写前面的一些丑数:

1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20, 24, 25, 27......

前面的数挨的还比较近,后面肯定会越拉越远,既然只需要第1500个数,不如从1开始挨个找,直到找出第1500个,应该不会太久(too young too naive)

问题来了,怎么判断一个数是不是丑数呢?很快便能发现,丑数的数学表达式为:2^n * 3^n * 5^n,那么判断一个数是否是丑数便可以这样实现(python):

def is_ugly(num):
    while num % 2 == 0:
        num = num // 2
    while num % 3 == 0:
        num = num // 3
    while num % 5 == 0:
        num = num // 5
    if num == 1:
        return True
    else:
        return False

好了,接下来就是找出第1500个了,写一个找第N个丑数的方法:

def find_rank(num):
    to_match = 1
    match_num = 0
    while True:
        if is_ugly(to_match):
            match_num += 1
            print('match: ' + str(match_num))
            if match_num == num:
                return to_match
        to_match += 1

执行find_rank(1500),等着看结果。

。。。。。。几十秒过去了,还没出结果,不对劲,加个语句,每找到一条,打印值。发现后面越来越慢,几分钟之后才显示结果为:859963392。这个值和整个耗时显然超出我的预期。

根据吴军老师常常说的适用于做人和编程的原则:少做事。为了找出1500个数,判断了近10亿个数,相当于做了近百万倍的无用计算,这显然不是一个好办法。

想了会没什么好办法,遂看了看剑指offer的讲解,发现书中给出的第一个方法跟自己的一样,还给出了个优化算法,便看了看,觉得甚好,便用Python实现了一下:

from numpy import min

ugly_list = [1]
multiply_2_index = 0
multiply_3_index = 0
multiply_5_index = 0

while len(ugly_list) < 1500:
    next_ugly = min([ugly_list[multiply_2_index] * 2, ugly_list[multiply_3_index] * 3,
                     ugly_list[multiply_5_index] * 5])
    ugly_list.append(next_ugly)
    while ugly_list[multiply_2_index] * 2 <= next_ugly:
        multiply_2_index += 1
    while ugly_list[multiply_3_index] * 3 <= next_ugly:
        multiply_3_index += 1
    while ugly_list[multiply_5_index] * 5 <= next_ugly:
        multiply_5_index += 1

print(next_ugly)

运行一下,秒出结果:859963392。

这个算法精髓之处在于:

  1. 清晰地认识到,所有的丑数都可以通过其他丑数乘以2,或者3,或者5来得出。
  2. 每次只算出下一个丑数,不贪婪,如果每次都将能够求得的丑数加入数组,势必要涉及排序,提高了复杂度
  3. 使用一个游标,记录丑数列表中第一个乘以2,3,5后大于最后一个丑数的下标。避免了每次都要对整个数组做乘法和判断

2个算法相比较,孰优孰劣一目了然,这个优化算法也完美的阐明了吴军老师的智慧,值得好好学习。

猜你喜欢

转载自blog.csdn.net/zhangyonguu/article/details/80680620