问题描述
我有一个名为 x
的 2 元组列表。 2 元组可以是任何东西,只要元组的第一个元素等于列表中前一个元组的第二个元素。例如整数:
x = [(0,150),(150,170),(170,158),(158,0),(0,150)]
第一个元组的第一个元素和最后一个元组的最后一个元素是不受约束的。现在这个列表由于某种原因被打乱了:
xs = [(170,150)]
并且我希望对其进行排序,以便恢复原始排序,即 x = sort(xs)
。请注意,如果可能有多种方法对加扰列表进行排序,以便上述链接条件成立,那么我不在乎,只是对这样的一种解决方案感兴趣 - 但我可以确定至少存在一种解决方案。
我看过 this question 但对我来说拓扑排序似乎不合适,因为我的图中可以有循环。到目前为止,我只设法暴力解决了一个解决方案:
import itertools
import numpy as np
def brute_chain_sort(scrambled):
starts,ends = zip(*scrambled)
starts = np.asarray(starts)
ends = np.asarray(ends)
for permutation in itertools.permutations(range(len(scrambled))):
permutation = np.asarray(permutation)
solved = (starts[permutation][1::] == ends[permutation][:-1]).all()
if solved:
return permutation
print("No sorting exists!")
scrambled = [(170,150)]
idx = brute_chain_sort(scrambled)
starts,ends = zip(*scrambled)
unscrambled = list(zip(list(np.asarray(starts)[idx]),list(np.asarray(ends)[idx])))
print(unscrambled)
# [(0,1)]
解决方法
一旦您在实现图算法方面有足够的经验,解决此类问题的难点就是确定您可以将其建模为哪个经典图问题。
拓扑排序是个好主意,但您的问题不是拓扑排序问题,因为该图不是无环的。更重要的是,这不是拓扑排序问题,因为要求是在结果序列中哪些元素必须相邻,而拓扑排序是关于哪些元素必须任何地方出现在之前或之后的其他元素。
这个问题可以被建模为找到一个 Eulerian path,这意味着一条路径只经过图的每条边一次(必须多次访问某些顶点)。该图中的顶点是数字,(有向)边是元组。
如果存在欧拉路径,则有一种直接的方法可以找到它,但由于图是有向的,因此会稍微复杂一些。如果一条路径经过每条边,那么每个顶点的入边数和出边数必然相同,除非可能是起始顶点多出一条边,结束顶点多出一条边,如果路径不在同一个地方开始和结束。从这个观察来看,用于寻找欧拉路径的 Hierholzer's algorithm 可以很容易地适用于有向图。该算法在边数为 O(E) 的线性时间内运行。
@chepner 在评论中指出该问题也可以建模为 Hamiltonian path 问题。不幸的是,找到哈密顿路径比找到欧拉路径要困难得多,而且没有有效的算法。尽管如此,@chepner 是正确的。