【问题标题】:Increasing itertools.permutations performance提高 itertools.permutations 性能
【发布时间】:2020-05-10 23:01:32
【问题描述】:

我正在完成一个问题,我创建了一个函数,该函数接受一个正整数并返回下一个更大的数字,该数字可以通过重新排列其数字来形成。例如:12 --> 21, 513 --> 531, 12435 --> 12453, 9817121211 --> 9817122111。

我一遍又一遍地重新编译我的代码以提高性能,但最终停止了,我无法更快地获得它。有人有建议吗?它的 itertools.permutations 行占用了绝大多数时间。

def next_bigger(n):
    num = str(n)
    num1 = set(int(x) for x in str(num))
    if num == num[0] *len(num):
        return -1
    #full_set = set(num)
    lis = set(int(''.join(nums)) for nums in itertools.permutations(num, len(num)))   
    lis = sorted(lis)
    try:
        return int(lis[lis.index(n)+1])
    except Exception:
        return -1

问题链接:https://www.codewars.com/kata/55983863da40caa2c900004e/train/python

【问题讨论】:

  • 建议是,排列仍然是暴力破解你的方式。在使用的算法本身不是最理想的情况下,责怪实现的性能是没有意义的。那么,接下来的问题是:什么是“更智能”的方式来做到这一点?
  • @ParitoshSingh ,您是指一种更聪明的排列方式,还是您认为我通过这种整体方法走上了一条死胡同。我试图编写一个代码,其中 itertools 只返回第一个数字至少与 num[0] 一样高但可以使其工作的解决方案。
  • 如果我让你做这个任务,作为一个人,而不是一台电脑,你会怎么做?您可能不会开始暴力破解所有可能的数字排列。手动尝试,并利用获得的见解编写更好的程序。
  • 确实,比遍历所有排列更智能的算法会提供最好的加速。但是,如果您需要继续使用排列方法,可以在这里进行一些加速:跳过计算set(您不关心这里的重复),尽快丢弃较小的数字(在理解中使用 if 子句),然后使用 min 查找下一个数字而不是排序。您也可以尝试跳过循环中的int,这可能更快或更慢。
  • @user2357112supportsMonica 感谢您的建议。根据您的建议更改了我的代码。已在下方回答。

标签: python performance itertools


【解决方案1】:

如果您正在寻找更好的“时间复杂度”性能,方法是找到算法的“关键”。在这种情况下,您应该问自己,创建下一个更大的数字意味着什么?答案就像两个相邻数字之间的交换一样简单。代码应该是这样的。

def next_bigger(n):
    num_string = list(str(n))
    for i in range(1, len(num_string)):
        if i == len(num_string):
            return -1

        #find two the two numbers one bigger than the other with the minimun order
        if num_string[-i] > num_string[-i-1]:

            compare_reference = num_string[-i]
            index_reference = -i

            #check if the current number is smaller than any of the tail 
            for k, current in enumerate(num_string[-i:]):
                if num_string[-i-1] < current and current < compare_reference:
                    compare_reference = current
                    index_reference = -i+k

            #interchange the locations:
            num_string[index_reference] = num_string[-i-1]
            num_string[-i-1] = compare_reference

            #check if the tail is larger than one digit
            if i > 1:
                #order the rest of the vector to create the smaller number (ordering it).
                lower_part_ordered = sort_ascendant(num_string[-i:])
            else:
                lower_part_ordered = [num_string[-i]]


            # create a string from the list
            return int("".join(num_string[:-i] + lower_part_ordered))        

    # no match found means a number like 65311
    return -1

【讨论】:

【解决方案2】:

虽然不是提高排列函数本身性能的方法,但这是我发现的提高代码性能的方法。非常感谢所有提供帮助的人!

def next_bigger(n):
    num_string = list(str(n))
    a = []
    for i in range(1, len(num_string)):
        if i == len(num_string):
            return -1
        p = int(num_string[-i])
        q = int (num_string[-(i+1)])
        if p > q:
            a.append(num_string[:-(i+1)])
            lis = list(num_string[-(i+1):])
        if len(lis) > 1:
            lis2 = list(set(lis))
            lis2.sort()
            qindex = lis2.index(str(q))
            first = lis2[qindex+1]
            a[0].append(first)
            lis.remove(first)
            lis.sort()
        for j in range (len(lis)):
            a[0].append(lis[j])
        return int("".join(a[0]))
    return -1

【讨论】:

  • @user2357112supportsMonica 通过了所有 150 次测试?哪些数字输入不起作用?
  • 根据@user2357112supportsMonica 的链接,next_bigger(35421) 给出了错误UnboundLocalError: local variable 'lis' referenced before assignmentnext_bigger(111)next_bigger(100) 也是如此。该错误引用了if len(lis) &gt; 1: 行。您还需要为无法放大的琐碎案例添加检查,即-1 结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-12
  • 2021-12-18
  • 2011-05-07
  • 2016-04-19
  • 2016-10-07
  • 2011-04-08
  • 2022-01-06
相关资源
最近更新 更多