2017-01-06 2 views
3

Когда working on an AoC puzzle, я обнаружил, что я хотел, чтобы вычесть списки (с сохранением упорядочения):О (п) список вычитание

def bag_sub(list_big, sublist): 
    result = list_big[:] 
    for n in sublist: 
     result.remove(n) 
    return result 

Я не нравится, как list.remove вызова (который сам по себе O (п)) содержится внутри цикла, что кажется бесполезным неэффективным. Так что я попытался переписать его, чтобы избежать этого:

def bag_sub(list_big, sublist): 
    c = Counter(sublist) 
    result = [] 
    for k in list_big: 
     if k in c: 
      c -= Counter({k: 1}) 
     else: 
      result.append(k) 
    return result 
  1. Является ли это сейчас O (п), или же Counter.__isub__ использование еще винт вещи?

  2. Этот подход требует, чтобы элементы были хешируемыми, ограничение, которых оригинал не имел. Существует ли решение O (n), которое позволяет избежать этого дополнительного ограничения? Есть ли у Python лучший тип данных «bag», чем collections.Counter?

Можно предположить, sublist составляет половину длины list_big.

+0

Имеются ли в этих списках какой-либо конкретный порядок? Вы можете сделать это в O (n) детерминированном времени, если они оба отсортированы. – user2357112

+0

Я не уверен, что вы делаете с Counter Counter. Вы можете получить тот же результат более четко, преобразовывая подсписку в набор и просто проверяя членство. –

+0

@ DanielRoseman - Я думаю, что счетчик обрабатывает дубликаты ('bag_sub ([foo, foo], [foo]) -> [foo]') – mgilson

ответ

2

Это сейчас O (n), или используется использование Counter.__isub__?

Это можно было бы ожидать случая O (п), за исключением того, что когда Counter.__isub__ отбрасывает неположительные ценности, он проходит через каждый ключ сделать это. Вам лучше просто вычитать 1 из ключевого значения «обычный» способ и проверить c[k] вместо k in c. (c[k] 0 для k not in c, так что вам не нужен in чек.)

if c[k]: 
    c[k] -= 1 
else: 
    result.append(k) 

Есть ли (п) решение O, который позволяет избежать создания этого дополнительного ограничения?

Только в том случае, если входы сортируются, и в этом случае стандартный вариант слияния слияния может это сделать.

Есть ли у Python лучший тип данных «bag», чем collections.Counter?

collections.Counter это сумка Python.

-1
  1. Удаление элемента из списка длины N равно O (N), если список неупорядочен, потому что вы должны его найти.
  2. Удаление K элементов из списка длины N, следовательно, O (кН), если мы сосредоточимся на «разумных» случаях, когда к < < Н.

Так что я не вижу, как вы могли бы получить это до O (N).

Краткий способ написать это:

new_list = [x for x in list_big if x not in sublist] 

Но это все равно O (кН).

+2

Это не обрабатывает дубликаты, как это делает исходный код. – user2357112

3

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

def bag_sub(big_list, sublist): 
    sublist_counts = Counter(sublist) 
    result = [] 
    for item in big_list: 
     if sublist_counts[item] > 0: 
      sublist_counts[item] -= 1 
     else: 
      result.append(item) 
    return result 

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

Кроме того, если вам не нужно возвращать список, а затем рассмотрим функцию генератора ...

Это работает до тех пор, как все элементы в list_big и sublist можно хэшируются. Этот раствор равен O(N + M), где N и M - это длины list_big и sublist соответственно.

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

Обратите внимание, что Counter s также ведут себя очень похоже на defaultdict(int) так это прекрасно, чтобы искать пункт в счетчике, который не существует уже.

+0

Это не обрабатывает дубликаты так, как это делает исходный код. – user2357112

+1

@ user2357112 - Ах, я вижу сейчас. Я не понимал, что делает OP. Я исправил свое решение. – mgilson

+0

Вы ошибаетесь в вычитании счетчика. – wim

Смежные вопросы