提高 itertools.permutations 性能

Posted

技术标签:

【中文标题】提高 itertools.permutations 性能【英文标题】:Increasing itertools.permutations performance 【发布时间】: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 感谢您的建议。根据您的建议更改了我的代码。已在下方回答。 【参考方案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

【讨论】:

This gives next_bigger(231) == 321 instead of 312. 没错,让我来解决它。它是一个索引未命中的解释。 This still gives next_bigger(231) == 321. 切换两个索引是不够的。 你说得对,我错过了一个关键点。我想现在答案已经完成了 按照@user2357112supportsMonica 的建议更正。并测试: f(35421) --> 41235 f(454321) --> 512344 f(12) --> 21 f(513342342342342342234) --> 513342342342342342243【参考方案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 结果。

以上是关于提高 itertools.permutations 性能的主要内容,如果未能解决你的问题,请参考以下文章

当重复次数高于 9 时,itertools.permutations 不起作用

python Python.Math.Itertools.Permutations.Combinations

如何将 itertools.permutations("0123456789") 的结果(在 python 中)转换为字符串列表

尝试使用 itertools.permutations 时出现 MemoryError,如何使用更少的内存?

python 编写排列组合

PYTHON ITERTOOLS