提高 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 givesnext_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 assignment
。 next_bigger(111)
和 next_bigger(100)
也是如此。该错误引用了if len(lis) > 1:
行。您还需要为无法放大的琐碎案例添加检查,即-1
结果。以上是关于提高 itertools.permutations 性能的主要内容,如果未能解决你的问题,请参考以下文章
当重复次数高于 9 时,itertools.permutations 不起作用
python Python.Math.Itertools.Permutations.Combinations
如何将 itertools.permutations("0123456789") 的结果(在 python 中)转换为字符串列表