我有几个列表,每个列表包含几个城市。我需要检查任何两个随机元素是否属于同一个列表。
简单示例:
list1 = ['London', 'Manchester', 'Liverpool', 'Edimburgh']
list2 = ['Dublin', 'Cork', 'Galway']
list3 = ['Berlin', 'Munich', 'Frankfurt', 'Paris', 'Milan', 'Rome', 'Madrid', 'Barcelona', 'Lisbon', ...]
list4 = ['Washington', 'New York', 'San Francisco', 'LA', 'Boston', ...]
预期结果:
> in_same_group('London', 'Liverpool')
> True
>
> in_same_group('Berlin', 'Washington')
> False
该功能经常被调用,因此速度至关重要。最大的列表最多可包含1000个元素。
最有效的方法是什么?
这是我到目前为止所尝试的,但它太慢了:
def in_same_group(city1, city2):
same_group = False
for this_list in [list1, list2, list3...]:
if city1 in this_list and city2 in this_list:
return True
return same_group
答案 0 :(得分:21)
这里是Horia的proposal和我原来的混合物。您可以将城市定义为dict
作为键,将索引集定义为值:
list1 = ['London', 'Manchester', 'Liverpool', 'Edimburgh']
list2 = ['Dublin', 'Cork', 'Galway', 'Paris', 'Rome']
list3 = ['Berlin', 'Munich', 'Frankfurt', 'Paris', 'Milan', 'Rome', 'Madrid', 'Barcelona', 'Lisbon']
list4 = ['Washington', 'New York', 'San Francisco', 'LA', 'Boston']
# Note that 'Paris' and 'Rome' are both in list2 and list3
groups = [list1, list2, list3, list4]
indices = {}
for i, group in enumerate(groups):
for city in group:
indices.setdefault(city, set()).add(i)
结构紧凑,看起来像这样:
print(indices)
#{'London': {0}, 'Manchester': {0}, 'Liverpool': {0}, 'Edimburgh': {0}, 'Dublin': {1}, 'Cork': {1}, 'Galway': {1}, 'Paris': {1, 2}, 'Rome': {1, 2}, 'Berlin': {2}, 'Munich': {2}, 'Frankfurt': {2}, 'Milan': {2}, 'Madrid': {2}, 'Barcelona': {2}, 'Lisbon': {2}, 'Washington': {3}, 'New York': {3}, 'San Francisco': {3}, 'LA': {3}, 'Boston': {3}}
对于任何城市对,您都可以通过set intersection得到一组常见索引:
def common_groups(city1, city2):
return indices.get(city1, set()) & indices.get(city2, set())
print(common_groups('London', 'Liverpool'))
# {0}
print(common_groups('London', 'Paris'))
# set()
print(common_groups('Cork', 'Paris'))
# {1}
print(common_groups('Rome', 'Paris'))
# {1, 2}
print(common_groups('Rome', 'Nowhere'))
# set()
Python中的空集是假的。
对于n
个城市,创建字典将为O(n)
,空间要求应为O(n)
,查找效果为O(1)
。作为奖励,查询不会返回布尔值,而是返回一组索引。
最后,由于设置了交叉点,如果您想检查三个或更多城市是否在同一组中,此方法也可以使用。
答案 1 :(得分:8)
一种方法是建立从城市到其组号的地图。所以你要构建类似的东西:
mapping = {
'London': 1,
...,
'Berlin': 3
...
}
然后您的in_same_group
功能可以是:
def in_same_group(item1, item2):
gr1 = mapping[item1]
gr2 = mapping[item2]
return gr1 == gr2
就速度而言,这是非常快的,因为它只是两个字典查找,在Python和一个比较中非常快,这又是非常快的。在大哦,函数是O(1)
。
但它假设元素只是一个组的一部分。在您提供的示例中似乎就是这种情况。
你做必须花费额外的时间和内存来实际构建地图。但它会在对in_same_group
的所有电话中摊销。 OTOH,无论你的方法如何,你都可能无法建立索引结构。
构建映射的代码是:
def build_mapping(groups):
mapping = {}
for i in range(0, len(groups)):
for g in groups[i]:
mapping[g] = i
return mapping
这不是最漂亮的代码,但它完成了工作。
答案 2 :(得分:5)
首先,使用集合,而不是列表(并使用集合列表而不是单独的变量)。
master_list = []
master_list.append(set(['London', 'Manchester', 'Liverpool', 'Edimburgh']))
master_list.append(set(['Dublin', 'Cork', 'Galway']))
master_list.append(set(['Berlin', 'Munich', 'Frankfurt', 'Paris', 'Milan', 'Rome', 'Madrid', 'Barcelona', 'Lisbon', ...]))
master_list.append(set(['Washington', 'New York', 'San Francisco', 'LA', 'Boston', ...]))
(根据您的使用情况,具有更有意义的键的dict可能比列表更合适。)
其次,构建一个将每个元素映射到其集合的字典:
# E.g., index['London'] == set(['London', 'Manchester', ...])
index = dict((item, s) for s in master_list for item in s)
现在,您只需检查两个项目是否属于同一组。
def in_same_group(i1, i2):
return index[i1] is index[i2]
答案 3 :(得分:2)
您可以遍历列表并确定是否在其中找到了两个搜索查询。然后,返回新形成的列表的布尔值。
def search(s1, s2):
list1 = ['London', 'Manchester', 'Liverpool', 'Edimburgh']
list2 = ['Dublin', 'Cork', 'Galway']
list3 = ['Berlin', 'Munich', 'Frankfurt', 'Paris', 'Milan', 'Rome', 'Madrid', 'Barcelona', 'Lisbon']
list4 = ['Washington', 'New York', 'San Francisco', 'LA', 'Boston']
return bool([i for i in [list1, list2, list3, list4] if s1 in i and s2 in i])
答案 4 :(得分:1)
如果你希望它真的很快,你应该改变你的数据结构,以获得一个集合字典,其中密钥将是一个城镇,集合将包含同一组中的所有城镇。这样您就可以确保in_same_group
只需要:
由于这些访问针对词典和集合进行了优化,因此研究应尽可能快地进行
代码可以是:
import collections
h = collections.defaultdict(set)
lists = [list1, list2, list3, list4]
for l in lists:
for town in l:
for other in l:
if town != other:
h[town].add(other)
该功能现在非常简单:
def in_same_group(t1, t2):
return t2 in h[t1]
答案 5 :(得分:0)
import pandas as pd
list1 = ['London', 'Manchester', 'Liverpool', 'Edimburgh']
list2 = ['Dublin', 'Cork', 'Galway']
list3 = ['Berlin', 'Munich', 'Frankfurt', 'Paris', 'Milan', 'Rome', 'Madrid', 'Barcelona', 'Lisbon']
list4 = ['Washington', 'New York', 'San Francisco', 'LA', 'Boston']
----------
a = pd.Series(list(list1))
b = pd.Series(list(list2))
c = pd.Series(list(list3))
d = pd.Series(list(list4))
lists = [a,b,c,d]
----------
for i in lists:
if (i.isin(['London']).any()) and (i.isin(['Manchester']).any()) == True:
print('Same Group')
else:
print('Different Group')
相同组
不同的小组
不同的小组
不同的小组
答案 6 :(得分:0)
在对提案进行了一些速度测试之后,我意识到关于所提出的解决方案的一个弱点,如果我没有弄错的话,那就是完整的循环将始终用于映射。通过先前跳过循环以防结果已知,可以提高速度。
此外,如果一个列表比其他列表大得多,则有可能加快速度。
我认为以下内容可能更快,以防其中一个列表比其他列表大得多。假设list3比其他的大得多。
list1 = ['London', 'Manchester', 'Liverpool', 'Edimburgh']
list2 = ['Dublin', 'Cork', 'Galway']
list3 = ['Berlin', 'Munich', 'Frankfurt', 'Paris', 'Milan', 'Rome', 'Madrid', 'Barcelona', 'Lisbon', ...] #assuming list3 is much larger than all others
list4 = ['Washington', 'New York', 'San Francisco', 'LA', 'Boston', ...]
可能性是:
def in_same_group(city1, city2):
for group in [list1, list2, list4]: #note that list3, the largest, is skipped
if city1 in group and city2 not in group:
return False
return True #if this point is reached, both cities belong to the biggest group
答案 7 :(得分:0)
在数据库术语中,您具有一对多的关系。一个列表可以包含许多名称,但每个名称只能出现在一个列表中。
试试这个:
In [20]: city_lists = {city_name:list1 for city_name in list1}
In [21]: city_lists.update({city_name:list2 for city_name in list2})
In [22]: city_lists
Out[22]:
{'Cork': ['Dublin', 'Cork', 'Galway'],
'Dublin': ['Dublin', 'Cork', 'Galway'],
'Edimburgh': ['London', 'Manchester', 'Liverpool', 'Edimburgh'],
'Galway': ['Dublin', 'Cork', 'Galway'],
'Liverpool': ['London', 'Manchester', 'Liverpool', 'Edimburgh'],
'London': ['London', 'Manchester', 'Liverpool', 'Edimburgh'],
'Manchester': ['London', 'Manchester', 'Liverpool', 'Edimburgh']}
In [23]: city_lists['Cork'] is city_lists['Dublin']
Out[23]: True
In [24]: city_lists['Cork'] is city_lists['London']
Out[24]: False
这非常有效。如果使用http://pythontutor.com可视化代码,您将看到dict包含对原始列表的引用,但不会复制列表本身。