Как проверить, присутствует ли значение в любом из заданных наборов

Скажем, у меня разные наборы (они должны быть разными, я не могу присоединиться к ним в зависимости от вида данных, с которыми я работаю):

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)

И вам не нужно беспокоиться о производительности здесь. Да, он создает временный набор "на лету", но поскольку он не хранится, происходит сбор мусора.