我有一个数组:
array([[ 4, 10],
[ 4, 2],
[ 0, 7],
[ 5, 11],
[ 6, 8],
[ 3, 6],
[ 9, 7],
[ 2, 11],
[ 9, 5],
[ 8, 1]])
我想要一种方法来对值对进行排序,以便尽可能多的成对2元素集具有公共值。这是所需有序数组的示例:
array([[ 4, 10],
[ 4, 2],
[ 2, 11],
[ 5, 11],
[ 9, 5],
[ 9, 7],
[ 0, 7], #note the gap here:
[ 8, 1],
[ 6, 8],
[ 3, 6]])
这些数组的几个条件都是正确的。没有重复对(即:[1,0]或[0,1]将出现在数组的其他位置,如果[0,1]已经存在)。没有对具有相同的值(即:[1,1]将不存在)。没有对将有两个以上的匹配(喵:在整个数组中没有值存在两次以上。)但是一对可以有零匹配(注意在上面的数组中没有匹配的间隙)。 / p>
显然,我可以创建数组的每个排列,但这看起来很粗糙。我认为可能有某种方法可以切割平台并以合理的方式重新堆叠,以便在少量切割中进行分类。但在我走这条路之前,我想:1)确保没有numpy
或collections
功能已经执行此操作。 2)知道使用numpy .sort()
(或类似的)来做这件事没有棘手的天才方法。 3)确定这是否是一项常见任务,并且有算法可以执行此操作。 (“哦,那是Blumen-Funke算法!”)
以下是一些生成混洗测试数组并检查已排序数组的代码:
def shuffled(N=12, ans=1):
'''returns is now the unsorted test array'''
r = range(N)
random.shuffle(r)
l = []
for i in range(N):
l.append((r[i-1], r[i]))
random.shuffle(l)
return np.array(l)[ans+1:]
# step 2: ???
def test_ordered(a):
'''checks if generated array has been sorted'''
c0 = a[1:,0]==a[:-1,0]
c1 = a[1:,0]==a[:-1,1]
c2 = a[1:,1]==a[:-1,0]
c3 = a[1:,1]==a[:-1,1]
cond = c0+c1+c2+c3
ans = sum(numpy.logical_not(cond))
# when sorted this should return the same number input into
# shuffled() as 'ans':
return ans
(这是一个主观问题吗?我被警告说是。)
非常感谢你的帮助。 Sven的解决方案比Paul的解决速度快20%,并且很高兴,他们都在线性时间运行,Doug的答案并没有解决问题。在输入数据中,性能对“中断”或“间隙”的数量存在很高但也很大程度上线性的依赖性。见下图。 10,000幅度轴是N. 0.5轴是断裂的百分比。 z轴是以秒为单位的性能。
再次感谢!
答案 0 :(得分:5)
您已经描述了一个图形,其中顶点是数字,边缘是您的对。
您的条件指定每个号码在列表中显示一次或两次。这意味着图表中的连接组件是线(或循环)。您可以使用此算法找到它们:
您可以有效地运行此算法:维护一组1度的顶点和2度的另一个顶点。当您使用边缘(原始列表中的一对)时,适当地修改集合:从中删除1级的端点第一组,并将端点从2级设置移动到1级。或者,使用优先级队列。
你还需要对你的对进行有效的查找:从顶点到相邻顶点列表构建一个dict。
使用这些想法,您可以在线性时间内找到最佳排序(假设O(1)set和dict实现)。
这是一个有点笨重的实现。
import collections
def handle_vertex(v, vs):
if v in vs[0]:
vs[0].remove(v)
elif v in vs[1]:
vs[0].add(v)
vs[1].remove(v)
def follow(edgemap, start, vs):
"""Follow a path in the graph, yielding the edges."""
v0 = start
last = None
while True:
# All vertices that we can go to next.
next_v = [v for v in edgemap[v0] if v != last]
if not next_v:
# We've reached the end (we must have been a line).
return
assert len(next_v) == 1 or (v0 == start and len(next_v) == 2)
next_v = next_v[0]
# Remove the endpoints from the vertex-degree sets.
handle_vertex(v0, vs)
handle_vertex(next_v, vs)
yield v0, next_v
if next_v == start:
# We've got back to the start (we must have been a cycle).
return
v0, last = next_v, v0
def pairsort(edges):
edgemap = collections.defaultdict(list)
original_edges = {}
for a, b in edges:
# Build the adjacency table.
edgemap[a].append(b)
edgemap[b].append(a)
# Keep a map to remember the original order pairs appeared in
# so we can output edges correctly no matter which way round
# we store them.
original_edges[a, b] = [a, b]
original_edges[b, a] = [a, b]
# Build sets of degree 1 and degree 2 vertices.
vs = [set(), set()]
for k, v in edgemap.iteritems():
vs[len(v) - 1].add(k)
# Find all components that are lines.
while vs[0]:
v0 = vs[0].pop()
for e in follow(edgemap, v0, vs):
yield original_edges[e]
# Find all components that are cycles.
while vs[1]:
v0 = vs[1].pop()
for e in follow(edgemap, v0, vs):
yield original_edges[e]
input = [
[ 4, 10],
[ 4, 2],
[ 0, 7],
[ 5, 11],
[ 6, 8],
[ 3, 6],
[ 9, 7],
[ 2, 11],
[ 9, 5],
[ 8, 1]]
print list(pairsort(input))
答案 1 :(得分:2)
我不确定我理解你问题的每一个细节;但如果我这样做,那么这应该做你想要的。
基本思路非常简单:对于两个1D阵列,您可以循环遍历所有成对 将它们排成一行,保持一个静止,然后一次向前滚动第二个增量。如果您使用NumPy的 roll 功能,那么当您向前滚动时,从数组末端掉落的值只会被推回到前面,就像跑步机。
在设置之后,只需沿正确的轴对两个向量进行差分 并且总和为0。跟踪这些总和( tx ,下面)然后得到对应的索引 这些总和的最大值, NP.argmax(tx)。
import numpy as NP
# create some data:
c1 = NP.random.randint(0, 10, 15)
c2 = NP.random.randint(0, 10, 15)
c12 = NP.concatenate((c1, c2)).reshape(15, 2)
tx = [] # to hold the indices of the various orderings
for i in range(15) :
v = NP.diff(c12, axis=0)
num_equal_neighbors = NP.sum( NP.sum(v==0, axis=0) )
tx.append(num_equal_neighbors)
c2 = NP.roll(c2, 1)
c12[:,1] = c2
现在找到两个向量的哪个排序给出最“成对”匹配:
best_order = NP.argmax(tx)
因此,当排列两个1D阵列时,发生所需的排序
这样第二个数组就滚动了* best_order *个数量
(并保留第一个数组)
答案 2 :(得分:1)
这是使用不同数据结构在Paul Hankin's answer中描述的基本相同算法的更简单实现。它也以线性时间运行。
edges = [[4, 10], [4, 2], [0, 7], [5, 11], [6, 8],
[3, 6], [9, 7], [2, 11], [9, 5], [8, 1]]
def add_edge(vertex, adj):
edge = edges[adj[0]][:]
ordered_edges.append(edge[:])
edge.remove(vertex)
new_vertex = edge[0]
new_adj = adj_edges[new_vertex]
new_adj.remove(adj[0])
del adj[0]
return new_vertex, new_adj
adj_edges = {}
for i, edge in enumerate(edges):
for vertex in edge:
adj_edges.setdefault(vertex, []).append(i)
ordered_edges = []
for vertex, adj in adj_edges.iteritems():
while len(adj) == 1:
vertex, adj = add_edge(vertex, adj)
for vertex, adj in adj_edges.iteritems():
while adj:
vertex, adj = add_edge(vertex, adj)
答案 3 :(得分:0)
另见 Longest path problem: NP对于一般图表是完整的,或者对于非循环的权重否定的最短路径 另外,请尝试显示的图表 Spanning tree: 最长的比较长也难。