我正在尝试解决一个问题,我有像这样的对:
A C
B F
A D
D C
F E
E B
A B
B C
E D
F D
我需要将它们组成3组,我必须从该列表中获得匹配的三角形。基本上我需要一个结果,如果它可能或不可以分组。
因此,可能的群组是(ACD
和BFE
),或(ABC
和DEF
),此群集可以分组,因为所有字母都可以分组3,没有人被遗漏。
我创建了一个脚本,我可以通过这些脚本来实现这一点,但是对于大型安装,它会变得太慢。
我的逻辑是:
make nested loop to find first match (looping untill I find a match)
> remove 3 elements from the collection
> run again
我这样做直到我没有信件。由于可以有不同的组合,我会从不同的字母开始多次运行,直到找到匹配为止。
我可以理解,这给了我至少N^N
的循环,并且可能变得太慢。这些问题有更好的逻辑吗?可以在这里使用二叉树吗?
答案 0 :(得分:6)
此问题可以建模为图Clique cover problem。每个字母都是一个节点,每一对都是边缘,您希望将图形划分为大小为3(三角形)的顶点不相交派系。如果您希望分区具有最小基数,那么您需要最小集团封面。
实际上这将是一个k-clique封面问题,因为在clique封面问题中你可以拥有任意/不同大小的派系。
答案 1 :(得分:2)
正如Alberto Rivelli所说,这个问题可以简化为Clique Cover problem,这是NP难的。 它也可以减少找到特定/最大尺寸的集团的问题。也许还有其他人,而不是你的特殊情况可以减少的NP难题,但我没有想到任何问题。
然而,做存在的算法可以在多项式时间内找到解,尽管并非总是在最坏的情况下。其中一个是Bron–Kerbosch algorithm,它是迄今为止最有效的查找最大团的算法,并且可以在最坏的情况下找到一个团(O ^(3 ^(n / 3))。我不知道你输入的大小,但我希望它足以解决你的问题。
这是Python中的代码,准备好了:
#!/usr/bin/python3
# @by DeFazer
# Solution to:
# stackoverflow.com/questions/40193648/algorithm-to-group-items-in-groups-of-3
# Input:
# N P - number of vertices and number of pairs
# P pairs, 1 pair per line
# Output:
# "YES" and groups themselves if grouping is possible, and "NO" otherwise
# Input example:
# 6 10
# 1 3
# 2 6
# 1 4
# 4 3
# 6 5
# 5 2
# 1 2
# 2 3
# 5 4
# 6 4
# Output example:
# YES
# 1-2-3
# 4-5-6
# Output commentary:
# There are 2 possible coverages: 1-2-3*4-5-6 and 2-5-6*1-3-4.
# If required, it can be easily modified to return all possible groupings rather than just one.
# Algorithm:
# 1) List *all* existing triangles (1-2-3, 1-3-4, 2-5-6...)
# 2) Build a graph where vertices represent triangles and edges connect these triangles with no common... vertices. Sorry for ambiguity. :)
# 3) Use [this](en.wikipedia.org/wiki/Bron–Kerbosch_algorithm) algorithm (slightly modified) to find a clique of size N/3.
# The grouping is possible if such clique exists.
N, P = map(int, input().split())
assert (N%3 == 0) and (N>0)
cliquelength = N//3
pairs = {} # {a:{b, d, c}, b:{a, c, f}, c:{a, b}...}
# Get input
# [(0, 1), (1, 3), (3, 2)...]
##pairlist = list(map(lambda ab: tuple(map(lambda a: int(a)-1, ab)), (input().split() for pair in range(P))))
pairlist=[]
for pair in range(P):
a, b = map(int, input().split())
if a>b:
b, a = a, b
a, b = a-1, b-1
pairlist.append((a, b))
pairlist.sort()
for pair in pairlist:
a, b = pair
if a not in pairs:
pairs[a] = set()
pairs[a].add(b)
# Make list of triangles
triangles = []
for a in range(N-2):
for b in pairs.get(a, []):
for c in pairs.get(b, []):
if c in pairs[a]:
triangles.append((a, b, c))
break
def no_mutual_elements(sortedtupleA, sortedtupleB):
# Utility function
# TODO: if too slow, can be improved to O(n) since tuples are sorted. However, there are only 9 comparsions in case of triangles.
return all((a not in sortedtupleB) for a in sortedtupleA)
# Make a graph out of that list
tgraph = [] # if a<b and (b in tgraph[a]), then triangles[a] has no common elements with triangles[b]
T = len(triangles)
for t1 in range(T):
s = set()
for t2 in range(t1+1, T):
if no_mutual_elements(triangles[t1], triangles[t2]):
s.add(t2)
tgraph.append(s)
def connected(a, b):
if a > b:
b, a = a, b
return (b in tgraph[a])
# Finally, the magic algorithm!
CSUB = set()
def extend(CAND:set, NOT:set) -> bool:
# while CAND is not empty and there is no vertex in NOT connected to *all* vertexes in CAND
while CAND and all((any(not connected(n, c) for c in CAND)) for n in NOT):
v = CAND.pop()
CSUB.add(v)
newCAND = {c for c in CAND if connected(c, v)}
newNOT = {n for n in NOT if connected(n, v)}
if (not newCAND) and (not newNOT) and (len(CSUB)==cliquelength): # the last condition is the algorithm modification
return True
elif extend(newCAND, newNOT):
return True
else:
CSUB.remove(v)
NOT.add(v)
if extend(set(range(T)), set()):
print("YES")
# If the clique itself is not needed, it's enough to remove the following 2 lines
for a, b, c in [triangles[c] for c in CSUB]:
print("{}-{}-{}".format(a+1, b+1, c+1))
else:
print("NO")
如果此解决方案仍然太慢,则可能更有效地解决Clique Cover问题。如果是这种情况,我可以尝试为它找到合适的算法。
希望有所帮助!
答案 2 :(得分:1)
我已经在JS中实现了我最自信的工作。我还尝试了从26个字母中随机选择的100000条边。如果它们都是唯一的而不是诸如["A",A"]
这样的点,则它会在90~500毫秒内解析。最复杂的部分是获得不相同的群体,那些不仅仅是三角形变化的群体。对于给定的边缘数据,它在1毫秒内解析。
作为总结,第一个reduce阶段找到三角形,第二个reduce阶段将断开连接的阶段分组。
function getDisconnectedTriangles(edges){
return edges.reduce(function(p,e,i,a){
var ce = a.slice(i+1)
.filter(f => f.some(n => e.includes(n))), // connected edges
re = []; // resulting edges
if (ce.length > 1){
re = ce.reduce(function(r,v,j,b){
var xv = v.find(n => e.indexOf(n) === -1), // find the external vertex
xe = b.slice(j+1) // find the external edges
.filter(f => f.indexOf(xv) !== -1 );
return xe.length ? (r.push([...new Set(e.concat(v,xe[0]))]),r) : r;
},[]);
}
return re.length ? p.concat(re) : p;
},[])
.reduce((s,t,i,a) => t.used ? s
: (s.push(a.map((_,j) => a[(i+j)%a.length])
.reduce((p,c,k) => k-1 ? p.every(t => t.every(n => c.every(v => n !== v))) ? (c.used = true, p.push(c),p) : p
: [p].every(t => t.every(n => c.every(v => n !== v))) ? (c.used = true, [p,c]) : [p])),s)
,[]);
}
var edges = [["A","C"],["B","F"],["A","D"],["D","C"],["F","E"],["E","B"],["A","B"],["B","C"],["E","D"],["F","D"]],
ps = 0,
pe = 0,
result = [];
ps = performance.now();
result = getDisconnectedTriangles(edges);
pe = performance.now();
console.log("Disconnected triangles are calculated in",pe-ps, "msecs and the result is:");
console.log(result);
&#13;
您可以生成不同长度的随机边缘,并使用代码here
进行播放