python 循环中的计数对

Over counting pairs in python loop

我有一个字典列表,其中每个字典的形式为:

 {'A': a,'B': b}

我想遍历列表并为每个 (a,b) 对找到 (b,a) 对(如果存在)。

例如,如果对于列表 A = 13 和 B = 14 的给定条目,则原始对将为 (13,14)。我想搜索整个字典列表以找到这对 (14,13)。如果 (14,13) 出现多次,我也想记录下来。

我想计算列表中所有原始 (a,b) 对出现的次数,补码 (b,a) 出现的时间,如果出现的话是多少次。为此,当找到互补对时,我有两个 for 循环和一个计数器。

pairs_found = 0
for i, val in enumerate( list_of_dicts ):
    for j, vol in enumerate( list_of_dicts ):
        if val['A'] == vol['B']:
            if vol['A'] == val['B']:
                pairs_found += 1

这会生成一个 pairs_found 大于 list_of_dicts 的长度。我意识到这是因为相同的对会被多算。我不确定如何克服这种退化?

为清晰起见编辑

list_of_dicts = []

list_of_dicts[0] = {'A': 14, 'B', 23}
list_of_dicts[1] = {'A': 235, 'B', 98}
list_of_dicts[2] = {'A': 686, 'B', 999}
list_of_dicts[3] = {'A': 128, 'B', 123}

....

假设该列表有大约 100000 个条目。在该列表的某处,将有一个或多个条目,其形式为 {'A' 23, 'B': 14}。如果这是真的,那么我想要一个计数器将其值增加一个。我想对列表中的每个值都这样做。

您可以先创建一个列表,其中每个字典的值作为元组:

example_dict = [{"A": 1, "B": 2}, {"A": 4, "B": 3}, {"A": 5, "B": 1}, {"A": 2, "B": 1}]
dict_values = [tuple(x.values()) for x in example_dict]

然后创建第二个列表,其中每个元素的出现次数倒置:

occurrences = [dict_values.count(x[::-1]) for x in dict_values]

最后,创建一个以 dict_values 为键,occurrences 为值的字典:

dict(zip(dict_values, occurrences))

输出:

{(1, 2): 1, (2, 1): 1, (4, 3): 0, (5, 1): 0}

对于每个键,您都有反转键的数量。您还可以即时创建字典:

occurrences = {dict_values: dict_values.count(x[::-1]) for x in dict_values}

我仍然不能 100% 确定你想做什么,但这是我的 猜测:

pairs_found = 0
for i, dict1 in enumerate(list_of_dicts):
    for j, dict2 in enumerate(list_of_dicts[i+1:]):
        if dict1['A'] == dict2['B'] and dict1['B'] == dict2['A']:
            pairs_found += 1

注意第二个 for 循环中的切片。这避免了检查之前已经检查过的对(比较 D1 和 D2 就足够了;不需要比较 D2 和 D1)

这比 O(n**2) 更好,但可能仍有改进空间

这是我的建议:

  • 使用元组来表示您的对并将它们用作 dict/set 键。
  • 构建一组您会寻找的独特倒置对。
  • 使用字典存储一对出现倒置的次数

那么代码应该是这样的:

# Create a set of unique inverted pairs    
inverted_pairs_set = {(d['B'],d['A']) for d in list_of_dicts}
# Create a counter for original pairs
pairs_counter_dict = {(ip[1],ip[0]):0 for ip in inverted_pairs_set]
# Create list of pairs
pairs_list = [(d['A'],d['B']) for d in list_of_dicts]
# Count for each inverted pairs, how many times 
for p in pairs_list:
   if p in inverted_pairs_set:
      pairs_counter_dict[(p[1],p[0])] += 1

您可以创建一个计数器字典,其中包含所有字典中 'A''B' 键的值:

complements_cnt = {(dct['A'], dct['B']): 0 for dct in list_of_dicts}

然后您需要做的就是再次遍历您的字典并增加 "complements":

的值
for dct in list_of_dicts:
    try:
        complements_cnt[(dct['B'], dct['A'])] += 1
    except KeyError:   # in case there is no complement there is nothing to increase
        pass

例如 list_of_dicts:

list_of_dicts = [{'A': 1, 'B': 2}, {'A': 2, 'B': 1}, {'A': 1, 'B': 2}]

这给出:

{(1, 2): 1, (2, 1): 2}   

这基本上是说 {'A': 1, 'B': 2} 有一个补码(第二个)而 {'A': 2, 'B': 1} 有两个(第一个和最后一个)。

解决方案是 O(n) 即使对于 100000 个词典也应该相当快。

注意:这与@debzsud 的回答非常相似。在我发布答案之前我还没有看到它。 :(