Да, возможно, одно решение, о котором я мог думать, требует создания словаря, объединяющего f(value)
и фактических имен в качестве словаря. Здесь я буду использовать iteration_utilities.groupedby
, но это легко сделать, используя collections.defaultdict
(я покажу это в нижней части ответа).
>>> from iteration_utilities import groupedby
>>> equivalent = groupedby(names, f)
>>> equivalent
{'A': ['Anabel', 'Alison', 'Avery', 'Abigail', 'Aimee', 'Alice'],
'B': ['Bethany', 'Beatrice'],
'C': ['Claudia', 'Carolyn'],
'D': ['Diane', 'Dana']}
Затем перебирать комбинации (отсортированные) ключей в этом словаре, а затем использовать itertools.product
делать итерации над всеми именами для каждого префикса:
import itertools
for comb in itertools.combinations(sorted(equivalent), 3):
for uniquecomb in itertools.product(*[equivalent[i] for i in comb]):
print(uniquecomb)
sorted
используется, потому что в противном случае порядок появления не будет детерминированным между прогонами.
Чтобы показать, что это намного быстрее, я использовал следующие настройки:
def unique_combs(names, f):
equivalent = groupedby(names, f)
for comb in itertools.combinations(sorted(equivalent), 3):
for uniquecomb in itertools.product(*[equivalent[i] for i in comb]):
pass
def unique_combs_original(names, f):
for i in itertools.combinations(names, 3):
if ((f(i[0]) != f(i[1])) and
(f(i[0]) != f(i[2])) and
(f(i[1]) != f(i[2]))):
pass
names = ["Anabel", "Alison", "Avery", "Abigail", "Aimee", "Alice",
"Bethany", "Beatrice",
"Claudia", "Carolyn",
"Diane", "Dana"]
f = lambda x : x[0]
%timeit unique_combs(names, f) # 10000 loops, best of 3: 59.4 µs per loop
%timeit unique_combs_original(names, f) # 1000 loops, best of 3: 417 µs per loop
Но масштабируется намного лучше, если есть много чтобы быть отвергнутыми комбинации:
names = names * 10 # more duplicates
%timeit unique_combs(names, f) # 100 loops, best of 3: 9.74 ms per loop
%timeit unique_combs_original(names, f) # 1 loop, best of 3: 577 ms per loop
Я упомянул defaultdict
вместо groupedby
, для полноты он может быть создан как thi s:
from collections import defaultdict
>>> names = ["Anabel", "Alison", "Avery", "Abigail", "Aimee", "Alice",
... "Bethany", "Beatrice",
... "Claudia", "Carolyn",
... "Diane", "Dana"]
>>> equivalent = defaultdict(list)
>>> for name in names:
... equivalent[f(name)].append(name)
>>> equivalent
defaultdict(list,
{'A': ['Anabel', 'Alison', 'Avery', 'Abigail', 'Aimee', 'Alice'],
'B': ['Bethany', 'Beatrice'],
'C': ['Claudia', 'Carolyn'],
'D': ['Diane', 'Dana']})
Спасибо за ответ! Это увлекательно, но достаточно, что я незнаком с тем, что я не могу действительно распаковать то, что здесь происходит. – Jacob
Просто nitpicking: на самом деле это не однострочный шрифт, если вы пишете его по двум строкам :-) Однако очень умное использование 'itertools.groupby', если вход уже отсортирован. – MSeifert
Также это немного медленнее, чем мое решение - однако я не могу точно понять, почему, учитывая, что фактическая обработка идентична (просто «groupby» заменяет словарь, который я использовал). – MSeifert