我遇到类似于this one的问题。但是,SO问题严格地集中在三个变量上。我正在寻找一种适用于三个以上的解决方案。
这是我的两个变量代码:
for track_a in collection_a:
for track_b in collection_b:
t1 = track_a["tempo"]
t2 = track_b["tempo"]
k1 = track_a["key"]
k2 = track_b["key"]
m1 = track_a["mode"]
m2 = track_b["mode"]
if (t1 == t2) and (k1 == k2) and (m1 == m2):
collection_c.append((track_a, track_b))
这是我对三个变量的解决方案:
for track_a in collection_a:
for track_b in collection_b:
for track_c in collection_c:
t1 = track_a["tempo"]
t2 = track_b["tempo"]
t3 = track_c["tempo"]
k1 = track_a["key"]
k2 = track_b["key"]
k3 = track_c["key"]
m1 = track_a["mode"]
m2 = track_b["mode"]
m3 = track_c["mode"]
a = (t1 == t2) and (k1 == k2) and (m1 == m2)
b = (t2 == t3) and (k2 == k3) and (m2 == m3)
c = (t3 == t1) and (k3 == k1) and (m3 == m1)
if a: collection_c.append((track_a, track_b))
if b: collection_c.append((track_b, track_c))
if c: collection_c.append((track_c, track_a))
显然,此解决方案不可扩展且速度慢。考虑到我必须检查所有这些事实,我怀疑它会很快,因为我们必须遍历所有可能的组合,但是我至少可以使其扩展吗? (最多5个)。另外,如有可能,允许以后添加更多比较特征。
答案 0 :(得分:1)
解决线性时间问题的一种有效方法是将dict转换为键值元组的冻结集合(用于相等性测试的键之上),以便它们可以被哈希化并用作dict键(签名)。本身,这样您就可以简单地使用集合的字典将它们分组:
groups = {}
for track in collections: # collections is a combination of all the collections you have
groups.setdefault(frozenset((k, track[k]) for k in ('tempo', 'key', 'mode')), set()).add(track['name'])
这样:
[group for group in groups.values() if len(group) >= 3]
将为您返回签名相同的3条轨道的名称列表。
答案 1 :(得分:0)
这是一种可在逻辑上扩展的解决方案,对于将n
个值进行比较的m
个字典,需要花费时间n*m
进行评估。
请注意,如果三个匹配,我将返回一组3。很容易将其炸毁到所有匹配对。但是,如果这样做,则可能返回的大小为n*n
。我已经向您展示了两者的外观。
def group_on(variables, *tracks):
# Build a trie first.
trie = {}
for track in tracks:
this_path = trie
for variable in variables:
value = track[variable]
if value not in this_path:
this_path[value] = {}
this_path = this_path[value]
if 'final' not in this_path:
this_path['final'] = [track]
else:
this_path['final'].append(track)
def find_groups(this_path, count):
if 0 == count:
if 1 < len(this_path['final']):
yield this_path['final']
else:
for next_path in this_path.values():
for group in find_groups(next_path, count-1):
yield group
for group in find_groups(trie, len(variables)):
yield group
def group_to_pairs(group):
for i in range(len(group)-1):
for j in range(i+1, len(group)):
yield (group[i], group[j])
print('Efficient version')
for group in group_on(['tempo', 'key', 'mode'],
{'track': 1, 'tempo': 1, 'key': 'A', 'mode': 'minor'},
{'track': 2, 'tempo': 1, 'key': 'A', 'mode': 'major'},
{'track': 3, 'tempo': 1, 'key': 'A', 'mode': 'minor'},
{'track': 4, 'tempo': 1, 'key': 'A', 'mode': 'major'},
{'track': 5, 'tempo': 1, 'key': 'A', 'mode': 'minor'},
):
print(group)
print('Versus')
for group in group_on(['tempo', 'key', 'mode'],
{'track': 1, 'tempo': 1, 'key': 'A', 'mode': 'minor'},
{'track': 2, 'tempo': 1, 'key': 'A', 'mode': 'major'},
{'track': 3, 'tempo': 1, 'key': 'A', 'mode': 'minor'},
{'track': 4, 'tempo': 1, 'key': 'A', 'mode': 'major'},
{'track': 5, 'tempo': 1, 'key': 'A', 'mode': 'minor'},
):
for pair in group_to_pairs(group):
print(pair)
答案 2 :(得分:0)
在itertools
中找到有用的内容,不确定是否是您想要的:
from itertools import product, combinations
all_collections = [collection_a, collection_b, collection_c] # d, e, f, ...
for collections in combinations(all_collections, 2): # Pick 2 (or any number) collections from all collections
for tracks in product(*collections): # Cartesian product of collections or equivalent to for track1 in collection1: for track2 in collection2: ...
if True: # check if all tracks are matched
print(*tracks) # or append them to another collection