Python 对混合整数序列重新排序以匹配特定条件(Codewars)

Python 对混合整数序列重新排序以匹配特定条件(Codewars),python,optimization,permutation,Python,Optimization,Permutation,我是一个初学者,我正在做一些代码战的练习。以下是练习的说明: 假设我们取一个数字x并执行以下任一操作: a) 将x除以3(如果它可以被3整除),或 b) 将x乘以2 每次操作后,我们都记录下结果。如果我们从9开始,我们可以得到如下序列: [9,3,6,12,4,8]--9/3=3-->3*2=6-->6*2=12-->12/3=4-->4*2=8 您将得到一个无序的整数序列,您的任务是对它们重新排序,使它们符合上述序列。总会有答案的 对于上述示例: 求解([12,3,9,4,6,8

我是一个初学者,我正在做一些代码战的练习。以下是练习的说明:

  • 假设我们取一个数字x并执行以下任一操作:
a) 将x除以3(如果它可以被3整除),或

b) 将x乘以2

  • 每次操作后,我们都记录下结果。如果我们从9开始,我们可以得到如下序列:
[9,3,6,12,4,8]--9/3=3-->3*2=6-->6*2=12-->12/3=4-->4*2=8

  • 您将得到一个无序的整数序列,您的任务是对它们重新排序,使它们符合上述序列。总会有答案的

  • 对于上述示例:

    求解([12,3,9,4,6,8])
    =
    [9,3,6,12,4,8]


我的代码通过了第一次测试。然后,当我尝试尝试时(这是第二步,包括更多的测试和更复杂的测试),我的代码花了很长时间。时间限制为12000毫秒

我正在寻找有关如何加快代码速度的提示。(或者,可能是必要的,如何更改方法以获得更快的代码)

这是我的代码:

从itertools导入置换
def解算(arr):
#首先,我使用排列构造所有可能序列的列表。
perm=[i代表置换中的i(arr,len(arr))]
perm=[perm中j的列表(j)]
#然后我开始浏览这个列表,直到找到一个有效的序列。
对于perm中的p:
#用于序列中的索引和元素
对于ind,枚举中的el(p[:-1]):
#我计算n1,n2:序列中接下来两个可能的数字
n1=el/3
如果n1.是_整数():
n1=int(n1)
n2=el*2
#如果n1或n2不是序列中的下一个数字,我打断并查看
#在下一个序列p。
如果(n1!=p[ind+1])和(n2!=p[ind+1]):
打破
#否则我就可以走完整个序列
#这意味着我已经找到了正确的一个,我可以退回它
其他:
返回p

通过直接迭代
置换
生成器,可以显著提高程序的效率

原因是,一旦程序找到解决方案,它就可以跳出循环,而不必重复进行更多不必要的排列,就像将它们存储在列表中所做的那样:

from itertools import permutations

def solve(arr):
    for p in permutations(arr, len(arr)):
        p = list(p)
        for ind, el in enumerate(p[:-1]):
            n1 = el/3
            if n1.is_integer():
                n1 = int(n1)
            n2 = el * 2

            if (n1 != p[ind+1]) and (n2 != p[ind+1]):
                break
        else:
            return p
print(solve([12, 3, 9, 4, 6, 8]) == [9, 3, 6, 12, 4, 8])
输出:

True
[9, 3, 6, 12, 4, 8]

仔细研究每一种可能的排列方式绝对是解决这个问题的错误方法。如果给你30件物品,你就永远不会完成全部30件!可能性。 有一个更简单的解决方案

对于数组的每个元素,查找下一个值:

my_sort = { a : a // 3 if a % 3  == 0 else 2 * a for a in arr}
查看
my_sort
。您会发现,在
my\u sort.keys()
中必须有一个值,而该值在
my\u sort.values()
中不存在。这是你的第一把钥匙。您知道这一点,因为每个键都必须是除第一个键之外的其他某个键的结果值

all_values = set(my_sort.values())
first_key = next(x for x in my_sort.keys() if x not in my_values)
现在只需生成列表,从
第一个\u键开始

n算法现在是线性的

==已编辑===

这个解决方案是不正确的。我误解了算法。我认为12后面必须跟4,我没有意识到它后面可以跟4或24

因此,对于列表中的每个元素,让我们看看所有可能的后续元素:

{12: [4, 24], 3: [1, 6], 9: {3, 18}, 4: [8], 6: [2, 12], 8: [16]}
将继承者列表修剪为列表中实际存在的继承者列表:

{12: [4], 3: [6], 9: [3], 4: [8], 6: [12], 8: []}
在这一点上,很明显,您的列表从9开始[没有任何东西有9作为继承者],到8结束[它无处可去]。你可以把答案读成9,3,6,12,4,8

事实可能证明,您所有的测试示例都是如此简单。或者,如果每个元素都有一个前导项(您只需在第一项中尝试各种可能性),或者如果某个元素有两个可能的后继项,则需要做更多的工作,您必须同时检查它们。但是你的搜索空间现在小多了。你只看到有意义的排列,而不是所有排列


您的所有测试可能都是这么简单。

我可能会晚一点做这件事,但正如您所说,您正在寻找提示,我将把所有代码都放在扰流板中

首先,没有任何证据

因为唯一的运算是
将x除以3
将x乘以2
2
3
是素数,一旦执行其中一个运算,就永远无法返回原始整数

唯一的例外是
0
。问题中没有说明,但任何包含
0
的输入列表都只是一个零列表。让我们假设
0
永远不会发生。无论如何,使用这两个操作绝对不能达到零

在一句话中,我会说这是因为所有自然数都可以表示为素因子的唯一组合。更多信息,请查看素因子分解

这非常重要,因为这意味着序列中不能出现两次数字。这意味着没有循环,没有重复,最重要的是,只有一个答案(有序序列)

利用这一点,您可以创建所有可能的元素对的列表

导入itertools

输入=[12,3,9,4,6,8]

在输入列表中创建两个数字的所有排列

对=[i代表itertools中的i。排列(输入,2)]

打印(对)

从一个列表中查找两个元素的所有排列实际上并不是很慢。例如,一个包含40个数字的列表只能生成1600个唯一对。远低于
1
[(12, 4), (3, 6), (9, 3), (4, 8), (6, 12)]
[9, 3, 6, 12, 4, 8]