it-roy-ru.com

Самый быстрый способ проверить, существует ли значение в списке

Какой самый быстрый способ узнать, существует ли значение в списке (список с миллионами значений в нем) и каков его индекс?

Я знаю, что все значения в списке уникальны, как в этом примере.

Первый метод, который я пробую, - (3,8 с в моем реальном коде):

a = [4,2,3,1,5,6]

if a.count(7) == 1:
    b=a.index(7)
    "Do something with variable b"

Второй метод, который я пробую, это (в 2 раза быстрее: 1,9 с для моего реального кода):

a = [4,2,3,1,5,6]

try:
    b=a.index(7)
except ValueError:
    "Do nothing"
else:
    "Do something with variable b"

Предлагаемые методы от пользователя переполнения стека (2,74 с для моего реального кода):

a = [4,2,3,1,5,6]
if 7 in a:
    a.index(7)

В моем реальном коде первый метод занимает 3,81 секунды, а второй - 1,88 секунды. Это хорошее улучшение, но:

Я новичок в Python/scripting, и есть ли более быстрый способ сделать то же самое и сэкономить больше времени на обработку?

Более конкретное объяснение для моего приложения:

В Blender API я могу получить доступ к списку частиц:

particles = [1, 2, 3, 4, etc.]

Оттуда я могу получить доступ к местоположению частицы:

particles[x].location = [x,y,z]

И для каждой частицы я проверяю, существует ли сосед, выполняя поиск каждой локации частицы следующим образом:

if [x+1,y,z] in particles.location
    "Find the identity of this neighbour particle in x:the particle's index
    in the array"
    particles.index([x+1,y,z])
661
Jean-Francois Gallant
7 in a

Самый простой и быстрый способ сделать это.

Вы также можете рассмотреть возможность использования set, но создание этого набора из вашего списка может занять больше времени, чем сэкономит более быстрое тестирование членства. Единственный способ быть уверенным - это хорошо оценивать. (это также зависит от того, какие операции вам нужны)

1298
Rafe Kettler

Как утверждают другие, in может быть очень медленным для больших списков. Вот некоторые сравнения характеристик для in, set и bisect. Обратите внимание на время (в секундах) в логарифмическом масштабе.

enter image description here

Код для тестирования:

import random
import bisect
import matplotlib.pyplot as plt
import math
import time

def method_in(a,b,c):
    start_time = time.time()
    for i,x in enumerate(a):
        if x in b:
            c[i] = 1
    return(time.time()-start_time)   

def method_set_in(a,b,c):
    start_time = time.time()
    s = set(b)
    for i,x in enumerate(a):
        if x in s:
            c[i] = 1
    return(time.time()-start_time)

def method_bisect(a,b,c):
    start_time = time.time()
    b.sort()
    for i,x in enumerate(a):
        index = bisect.bisect_left(b,x)
        if index < len(a):
            if x == b[index]:
                c[i] = 1
    return(time.time()-start_time)

def profile():
    time_method_in = []
    time_method_set_in = []
    time_method_bisect = []

    Nls = [x for x in range(1000,20000,1000)]
    for N in Nls:
        a = [x for x in range(0,N)]
        random.shuffle(a)
        b = [x for x in range(0,N)]
        random.shuffle(b)
        c = [0 for x in range(0,N)]

        time_method_in.append(math.log(method_in(a,b,c)))
        time_method_set_in.append(math.log(method_set_in(a,b,c)))
        time_method_bisect.append(math.log(method_bisect(a,b,c)))

    plt.plot(Nls,time_method_in,marker='o',color='r',linestyle='-',label='in')
    plt.plot(Nls,time_method_set_in,marker='o',color='b',linestyle='-',label='set')
    plt.plot(Nls,time_method_bisect,marker='o',color='g',linestyle='-',label='bisect')
    plt.xlabel('list size', fontsize=18)
    plt.ylabel('log(time)', fontsize=18)
    plt.legend(loc = 'upper left')
    plt.show()
155
xslittlegrass
def check_availability(element, collection: iter):
    return element in collection

Использование

check_availability('a', [1,2,3,4,'a','b','c'])

Я считаю, что это самый быстрый способ узнать, находится ли выбранное значение в массиве.

32
Tiago Moutinho

Вы можете поместить свои элементы в set . Набор поисков очень эффективен.

Пытаться:

s = set(a)
if 7 in s:
  # do stuff

edit В комментарии вы говорите, что хотите получить индекс элемента. К сожалению, наборы не имеют понятия положения элемента. Альтернативой является предварительная сортировка списка и последующее использование бинарный поиск каждый раз, когда вам нужно найти элемент.

29
NPE
a = [4,2,3,1,5,6]

index = dict((y,x) for x,y in enumerate(a))
try:
   a_index = index[7]
except KeyError:
   print "Not found"
else:
   print "found"

Это будет хорошей идеей, только если а не изменится, и, таким образом, мы можем выполнить часть dict () один раз, а затем использовать ее повторно. Если что-то изменилось, пожалуйста, предоставьте более подробную информацию о том, что вы делаете.

15
Winston Ewert

Похоже, что ваше приложение может получить преимущество от использования структуры данных Bloom Filter.

Короче говоря, просмотр фильтра Блума может очень быстро сказать вам, если значение ОПРЕДЕЛЕННО НЕ присутствует в наборе. В противном случае вы можете выполнить поиск медленнее, чтобы получить индекс значения, которое МОЖЕТ БЫТЬ в списке. Поэтому, если ваше приложение имеет тенденцию получать результат "не найден" гораздо чаще, чем результат "найден", вы можете ускорить процесс, добавив фильтр Блума.

Для получения более подробной информации Википедия предоставляет хороший обзор того, как работают фильтры Bloom, а веб-поиск "библиотеки фильтров Python Bloom" предоставит как минимум пару полезных реализаций.

6
matt2000

Имейте в виду, что оператор in проверяет не только равенство (==), но и идентичность (is), логика in для lists --- примерно эквивалентно следующее (на самом деле оно написано на C, а не Python хотя, по крайней мере, в CPython):

for element in s:
    if element is target:
        # fast check for identity implies equality
        return True
    if element == target:
        # slower check for actual equality
        return True
return False

В большинстве случаев эта деталь не имеет значения, но в некоторых случаях она может удивить новичка Python, например, numpy.NAN имеет необычное свойство быть не равным себе :

>>> import numpy
>>> numpy.NAN == numpy.NAN
False
>>> numpy.NAN is numpy.NAN
True
>>> numpy.NAN in [numpy.NAN]
True

Чтобы различать эти необычные случаи, вы можете использовать any(), например:

>>> lst = [numpy.NAN, 1 , 2]
>>> any(element == numpy.NAN for element in lst)
False
>>> any(element is numpy.NAN for element in lst)
True 

Обратите внимание, что логика in для lists с any() будет:

any(element is target or element == target for element in lst)

Тем не менее, я должен подчеркнуть, что это случай Edge, и в подавляющем большинстве случаев оператор in очень оптимизирован и, разумеется, именно то, что вы хотите (либо с list, либо с set).

5
Chris_Rands

Это не код, а алгоритм очень быстрого поиска.

Если ваш список и значение, которое вы ищете, все числа, это довольно просто. Если строки: посмотрите внизу:

  • -Позвольте "n" быть длиной вашего списка
  • -Дополнительный шаг: если вам нужен индекс элемента: добавьте второй список в список с текущим индексом элементов (от 0 до n-1) - см. Позже
  • Заказать свой список или его копию (.sort ())
  • Переберите:
    • Сравните ваш номер с n/2-м элементом списка
      • Если больше, повторите цикл между индексами n/2-n
      • Если меньше, повторите цикл между индексами 0-n/2
      • Если то же самое: вы нашли это
  • Продолжайте сужать список, пока не найдете его или не получите только 2 числа (ниже и выше того, которое вы ищете)
  • Это позволит найти любой элемент в не более 19 шагов для списка из 1.000.0 (log (2) n, если быть точным)

Если вам также нужна исходная позиция вашего номера, найдите ее во втором столбце индекса.

Если ваш список не состоит из чисел, метод все еще работает и будет самым быстрым, но вам может потребоваться определить функцию, которая может сравнивать/упорядочивать строки.

Конечно, это требует вложений метода sorted (), но если вы продолжаете использовать один и тот же список для проверки, это может стоить того.

2
Adam

Или используйте __contains__:

sequence.__contains__(value)

Демонстрация:

>>> l=[1,2,3]
>>> l.__contains__(3)
True
>>> 
2
U9-Forward
present = False
searchItem = 'd'
myList = ['a', 'b', 'c', 'd', 'e']
if searchItem in myList:
   present = True
   print('present = ', present)
else:
   print('present = ', present)
1
Hafizur Rahman

Для меня это было 0,030 с (реальное), 0,026 с (пользователь) и 0,004 с (sys).

try:
print("Started")
x = ["a", "b", "c", "d", "e", "f"]

i = 0

while i < len(x):
    i += 1
    if x[i] == "e":
        print("Found")
except IndexError:
    pass
1
Tabin1000

Код для проверки наличия двух элементов в массиве, произведение которых равно k:

n = len(arr1)
for i in arr1:
    if k%i==0:
        print(i)
0
ravi tanwar