首页 > 技术文章 > 计算第1500个丑数

wingor 2015-05-11 16:03 原文

题目:
把只包含质因子2、3和5的数称作丑数(Ugly Number),例如:2,3,4,5,6,8,9,10,12,15,等,要求写一个算法获取第1500个丑数。

一,顺序查找,主要用来检验其他方法是否正确

数字递增,挨个判断是否是丑数

def if_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

@timeit
def get_ugly(count):
    res_list = []
    temp_count = 0
    num = 2
    while(temp_count<count):
        if(if_ugly(num)):
            temp_count += 1
            res_list.append(num)
        num += 1   
    return res_list

二,考虑到丑数都是2,3,5的多次幂相乘得到,假设第1500个丑数是2的n次幂、3的m次幂、5的i次幂的积,先假设一个最高次幂q(n+m+i<=q),n、m、i进行排列组合来生成一批丑数,再进行排列。但是很不幸这样做不能保证排序好的队列是与实际的丑数序列完全相同的,就是说队列中有部分数字丢了,但是起码保证了2的q次幂及之前的序列是正确的,如果2的q次幂及之前的序列不够1500个,则需要将幂改为q+1

def get_times(num):
    temp_count = 1
    temp = 2
    while(temp<num):
        temp_count += 1
        temp *= 2
    return temp_count


@time_used_it
def get_ugly2(count):
    res_list = []
    times = get_times(count)
    while(True):
        res_list = []
        for num2 in range(times+1):
            for num3 in range(times+1):
                for num5 in range(times+1):
                    res_list.append(times_num(2, num2) * times_num(3, num3) *times_num(5, num5))
        res_list.sort()
        if(res_list.index(times_num(2, times))+1 > count):
            break
        times += 1 
    return res_list[:count]

三,假设已有丑数队列[1,2,3,5...],下一个丑数肯定是这个队列分别与2,3,5相乘得到的数中不在现有丑数队列里的最小数。但是我们没有必要全部相乘再比较,因为最小的应该是前面某个数。

2,3,5分别与队列中某个位置的数相乘,相乘数的位置分别几位ind2、ind3、ind5。初始化丑数队列为[1],ind2、ind3、ind5初始化为0,2,3,5分别与相应ind指示的位置上的丑数相乘,获取的三个新数取最小的一个,如果这个数在原有丑数队列中不存在,则为下一个丑数,加到队列尾端,并将获得该丑数的ind(ind2、ind3、ind5中的一个)加一到下一位置,再进行下一次循环。

def get_ugly3(count):
    res_list = [1]
    ind2 = 0
    ind3 = 0
    ind5 = 0
    temp_count = 0
    mult_nums = [res_list[ind2]*2, res_list[ind3]*3, res_list[ind5]*5]
    
    while(temp_count<count):
        new_min = min(mult_nums)
        if(new_min>res_list[-1]):
            res_list.append(new_min)
            temp_count += 1
        
        if(new_min == res_list[ind2]*2):
            ind2 += 1
            mult_nums[0] = res_list[ind2]*2
        elif(new_min == res_list[ind3]*3):
            ind3 += 1
            mult_nums[1] = res_list[ind3]*3
        else:
            ind5 += 1
            mult_nums[2] = res_list[ind5]*5
            
    return res_list

分别花费时间

get_ugly
used_time: 619.229081741
get_ugly2
used_time: 1.06278842749
get_ugly3
used_time: 0.00205379498129

【注】方法三部分思想参考了http://blog.csdn.net/joanlynnlove/article/details/7623799

推荐阅读