Как проверить, присутствует ли значение в любом из заданных наборов
Скажем, у меня разные наборы (они должны быть разными, я не могу присоединиться к ним в зависимости от вида данных, с которыми я работаю):
r = set([1,2,3])
s = set([4,5,6])
t = set([7,8,9])
Каков наилучший способ проверить, присутствует ли данная переменная в любом из них?
Я использую:
if myvar in r \
or myvar in s \
or myvar in t:
Но мне интересно, можно ли это каким-то образом уменьшить, используя set
свойства, такие как union
.
Следующие работы, но я не нахожу способ определить несколько объединений:
if myvar in r.union(s)
or myvar in t:
И мне также интересно, повлияет ли этот союз на производительность, так как я предполагаю, что временная set
будет создана на лету.
Ответы
Ответ 1
Просто используйте любой:
if any(myvar in x for x in (r,s,t))
set lookups 0(1)
, поэтому создание объединения для проверки того, является ли переменная в любом наборе, совершенно ненужным, а не просто проверять использование in
с помощью any
, которая будет коротко замыкаться, как только будет найдено совпадение не создавать новый набор.
И мне также интересно, повлияет ли этот союз на производительность как-то
Да, конечно, объединение наборов влияет на производительность, это добавляет сложности, вы создаете новый набор каждый раз, когда O(len(r)+len(s)+len(t))
, чтобы вы могли попрощаться с реальной точкой использования наборов, которые являются эффективными поисками.
Итак, суть в том, что вы хотите сохранить эффективные поисковые запросы, которые вам придется объединить в один раз, и сохранить их в памяти, создавая новую переменную, а затем использовать ее для поиска myvar
, поэтому первоначальное создание будет 0(n)
и поиск будет 0(1)
после этого.
Если вы не каждый раз хотите выполнить поиск сначала, создав объединение, у вас будет линейное решение длиной r+s+t -> set.union(*(r, s, t))
, а не в худшем случае три постоянных (в среднем) поиска. Это также означает всегда добавление или удаление любых элементов из нового объединенного набора, которые добавляются/удаляются из r,s
или t
.
Некоторые реалистичные тайминги на моделях с умеренно большими размерами показывают точно разницу:
In [1]: r = set(range(10000))
In [2]: s = set(range(10001,20000))
In [3]: t = set(range(20001,30000))
In [4]: timeit any(29000 in st for st in (r,s,t))
1000000 loops, best of 3: 869 ns per loop
In [5]: timeit 29000 in r | s | t
1000 loops, best of 3: 956 µs per loop
In [6]: timeit 29000 in reduce(lambda x,y :x.union(y),[r,s,t])
1000 loops, best of 3: 961 µs per loop
In [7]: timeit 29000 in r.union(s).union(t)
1000 loops, best of 3: 953 µs per loop
Сроки объединения показывают, что почти все время тратится на профсоюзные вызовы:
In [8]: timeit r.union(s).union(t)
1000 loops, best of 3: 952 µs per loop
Использование больших наборов и получение элемента в последнем наборе:
In [15]: r = set(range(1000000))
In [16]: s = set(range(1000001,2000000))
In [17]: t = set(range(2000001,3000000))
In [18]: timeit any(2999999 in st for st in (r,s,t))
1000000 loops, best of 3: 878 ns per loop
In [19]: timeit 2999999 in reduce(lambda x,y :x.union(y),[r,s,t])
1 loops, best of 3: 161 ms per loop
In [20]: timeit 2999999 in r | s | t
10 loops, best of 3: 157 ms per loop
Существует буквально никакой разницы, независимо от того, насколько большие элементы получают использование any
, но по мере роста размеров набора так же работает время, используя union.
Единственный способ сделать это быстрее - придерживаться or
, но мы берем разницу в несколько сотен наносекунд, что является затратами на создание выражения генератора и вызова функции:
In [22]: timeit 2999999 in r or 2999999 in s or 2999999 in t
10000000 loops, best of 3: 152 ns per loop
Наборы union set.union(* (r, s, t)) также являются самыми быстрыми, поскольку вы не создаете промежуточные множества:
In [47]: timeit 2999999 in set.union(*(r,s,t))
10 loops, best of 3: 108 ms per loop
In [49]: r | s | t == set.union(*(r,s,t))
Out[49]: True
Ответ 2
Вы можете использовать встроенный any:
r = set([1,2,3])
s = set([4,5,6])
t = set([7,8,9])
if any(myvar in x for x in [r,s,t]):
print "I'm in one of them"
any
будет короткое замыкание на первом условии, которое возвращает True
, чтобы вы могли обойти создание потенциально огромного union
или проверку потенциально большого количества наборов для включения.
И мне также интересно, повлияет ли этот союз на производительность, так как я предполагаю, что временный набор будет создан на лету.
В соответствии с wiki.python.com s|t
O(len(s)+len(t))
, тогда как поисковые запросы O(1)
.
Для n
устанавливает с l
элементами каждый, делая union
итеративно, чтобы построить набор, приведет к:
a.union(b).union(c).union(d) .... .union(n)
Что эквивалентно O(l+l)
для a.union(b)
и O(2l+2l+l)
a.union(b).union(c)
и т.д., сумма которых составляет до O(n*(n+1)/2)*l)
.
O(n^2*l)
является квадратичным и исключает преимущество использования наборов.
Поиск в n наборах с помощью any
будет выполняться в O(n)
Ответ 3
Вы можете использовать reduce
, чтобы применить функцию двух аргументов кумулятивно к элементам итерабельности:
>>> r = set([1,2,3])
>>> s = set([4,5,6])
>>> t = set([7,8,9])
>>>
>>> reduce(lambda x,y :x.union(y),[r,s,t])
set([1, 2, 3, 4, 5, 6, 7, 8, 9])
И для проверки членства в любом из них вы можете использовать выражение-генератор в any
, что более эффективно здесь, потому что python использует хеш-таблица для хранения наборов, и проверка корабля-члена имеет O (1) в таких структурах данных, как словари или frozenset
. Также для проверки членства во всех ваших установках используйте all
.
if any(i in item for item in [r,s,t]):
#do stuff
Но в этом случае (Не для больших наборов) использование оператора or
выполняется быстрее.
value in r|s|t
Это показатель для всех способов:
~$ python -m timeit "r = set([1,2,3]);s = set([4,5,6]);t = set([7,8,9]);3 in reduce(lambda x,y :x.union(y),[r,s,t])"
1000000 loops, best of 3: 1.55 usec per loop
~$ python -m timeit "r = set([1,2,3]);s = set([4,5,6]);t = set([7,8,9]);3 in r|s|t"
1000000 loops, best of 3: 1.11 usec per loop
~$ python -m timeit "r = set([1,2,3]);s = set([4,5,6]);t = set([7,8,9]);any(3 in item for item in [r,s,t])"
1000000 loops, best of 3: 1.24 usec per loop
~$ python -m timeit "r = set([1,2,3]);s = set([4,5,6]);t = set([7,8,9]);3 in r.union(s).union(t)"
1000000 loops, best of 3: 1.19 usec per loop
Обратите внимание, что как @Padraic Cunningham упоминал, что для больших наборов с использованием any
очень эффективен!
Ответ 4
|
является оператором объединения sets
в python. Вы можете определить объединение над несколькими наборами, используя |
как:
>>> r = set([1,2,3])
>>> s = set([4,5,6])
>>> t = set([7,8,9])
>>> r | s | t
set([1, 2, 3, 4, 5, 6, 7, 8, 9])
Ответ 5
Вы можете просто сделать
if myvar in r.union(s).union(t)
И вам не нужно беспокоиться о производительности здесь. Да, он создает временный набор "на лету", но поскольку он не хранится, происходит сбор мусора.