python codewars每日一刷-记第一个自力更生的算法问题

今天遇到如下问题,大致意思是:

一个数需要满足 2**i * 3**j * 5**k ,求第5000个满足这条件的最小值

A Hamming number is a positive integer of the form 2i3j5k, for some non-negative integers i, j, and k.

Write a function that computes the nth smallest Hamming number.

Specifically:

    The first smallest Hamming number is 1 = 203050
    The second smallest Hamming number is 2 = 213050
    The third smallest Hamming number is 3 = 203150
    The fourth smallest Hamming number is 4 = 223050
    The fifth smallest Hamming number is 5 = 203051

The 20 smallest Hamming numbers are given in example test fixture.

Your code should be able to compute all of the smallest 5,000 (Clojure: 2000) Hamming numbers without timing out.
乍一看不难啊,直接用两层循环开搞(外层循环记录次数,内层循环判断是否满足条件)

第一次脚本如下:

def hamming(n):
  """Returns the nth hamming number"""
  num = 1   
  count = 0 #记录次数
  while 1:    #外层循环记录个数
    num_temp = num 
    while 1:    #内层循环判断条件
        if num_temp%2 == 0:
            num_temp /= 2
        elif num_temp%3 == 0:
            num_temp /= 3
        elif num_temp%5 == 0:
            num_temp /= 5
        else:
            break
    if num_temp == 1:
        count += 1
        if count >= n:
            return num
        else:
            num += 1
    else:
        num += 1
逻辑没问题,但计算第5000个数已经力不从心了,现在才反应过来这道题原来考的是算法

第二次
出发点是尽量缩减之前代码遍历的区域,试着能不能计算出来
思路如下:
1、按顺序将满足条件的数存入列表result
2、新生成的数是由
    1、之前的数乘以2或3或5
    2、并且大于result[-1]
    3、取其中的最小值

得到的

def hamming(n):
    """Returns the nth hamming number"""
    result = [1]
    for i in range(n-1):
        a = int(len(result)/5)
        add = [x*y for x in result[a:]
                for y in [2,3,5] if x*y > result[-1]]    #在此之前的每个数分别乘以2/3/5,并且大于result[-1]的最小值
        result.append(min(add))
    return result[-1]
现在计算第5000个数,已经能在10s左右计算出来
但丢进测试窗口后,仍返回超时异常。

第三次
由于如果不能成功提交代码,不能参考其他网友的思路,革命只有靠自己了
静下心来一想,之前的思路全是对每个数进行循环遍历,当数不断增大时,计算量几何增长
能不能找到其中的部分规律以简化,由于远离数学问题一个世纪,此处略去一天的思考时间
最后的大致思路如下:
1、新的数(new)肯定为之前某个数(x) 乘以2或3或5 得到
2、如果 x 被使用后,之后的计算肯定是从x以后的数开始计算(所以重点是记录x的下标ind,每当计算x满足要求时,ind向后移动一位)
3、现在的问题是:可以 乘以2或3或5 ,那么需要三个参数记录对应的下标(ind2、ind3、ind5)
    此处比较难以理解,尝试解释下:
    1、新的数new是目前为止最大的数
    2、当new只是2的倍数时,那么这个new如果在下一次调用时(在此时,这个数应该是上文中的x),只可能*2(因为乘以其他数肯定比乘以2大,不满足尽量小的条件)
    3、当new是2/3的公倍数时,那么new如果在下一次调用时(在此时,这个数应该是上文中的x),首先只可能*2,但在下下次调用时,*3可能会满足要求
    4、当new是2/3/5或者3/5的公倍数时,同理3
    5、为了要记录x的index,所以每当new是2|3|5的倍数时,对应的ind应该自增1
不基于数学理论聊算法问题就是耍流氓, 大家看不明白的话直接看代码吧,可能明白点

(以后一定要学习数学了)

def hamming(n):
    """Returns the nth hamming number"""
    result = [1]
    ind2 = ind3 = ind5 = 0    #每当add为对应数的倍数时,index += 1
    count = 1
    while n>count:
        add = min(result[ind2]*2, result[ind3]*3, result[ind5]*5)
        if add == result[ind2]*2:
            ind2 += 1
        if add == result[ind3]*3:
            ind3 += 1
        if add == result[ind5]*5:
            ind5 += 1
        count += 1
        result.append(add)
    return result[-1]
此时,计算第5000个数【50837316566580】仅仅0.3s,顺利通过测试
欣喜若狂!!!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值