>>> k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]
>>> import itertools
>>> k.sort()
>>> list(k for k,_ in itertools.groupby(k))
[[1, 2], [3], [4], [5, 6, 2]]
itertools
часто предлагает самые быстрые и мощные решения для такого рода проблем, и стоит хорошо , с которыми стоит познакомиться! -)
Редактировать : как я упоминаю в комментарии, обычные усилия по оптимизации сфокусированы на больших входах (подход big-O), потому что это намного проще, чем дает хорошую отдачу от усилий. Но иногда (по существу для «трагически критических узких мест» в глубоких внутренних циклах кода, которые раздвигают границы пределов производительности), может потребоваться более детальная детализация, обеспечивающая распределение вероятностей, решая, какие показатели производительности следует оптимизировать (возможно, верхнюю границу или 90-й центиль является более важным, чем среднее значение или медиана, в зависимости от приложений), выполняя, возможно, эвристические проверки в начале, чтобы выбрать различные алгоритмы в зависимости от характеристик входных данных и т. д.
Тщательные измерения «точечной» производительности (код A против кода B для конкретного входа) являются частью этого чрезвычайно дорогостоящего процесса, и здесь помогает стандартный модуль библиотеки timeit
. Тем не менее, проще использовать его в командной строке. Например, вот небольшой модуль для демонстрации общего подхода к этой проблеме, сохраните его как nodup.py
:
import itertools
k = [[1, 2], [4], [5, 6, 2], [1, 2], [3], [4]]
def doset(k, map=map, list=list, set=set, tuple=tuple):
return map(list, set(map(tuple, k)))
def dosort(k, sorted=sorted, xrange=xrange, len=len):
ks = sorted(k)
return [ks[i] for i in xrange(len(ks)) if i == 0 or ks[i] != ks[i-1]]
def dogroupby(k, sorted=sorted, groupby=itertools.groupby, list=list):
ks = sorted(k)
return [i for i, _ in itertools.groupby(ks)]
def donewk(k):
newk = []
for i in k:
if i not in newk:
newk.append(i)
return newk
# sanity check that all functions compute the same result and don't alter k
if __name__ == '__main__':
savek = list(k)
for f in doset, dosort, dogroupby, donewk:
resk = f(k)
assert k == savek
print '%10s %s' % (f.__name__, sorted(resk))
Обратите внимание на проверку работоспособности (выполняемую, когда вы просто выполняете python nodup.py
) и базовую технику подъема (создание постоянных глобальных имен локально для каждой функции для скорости), чтобы поставить вещи в равное положение.
Теперь мы можем запускать проверки в крошечном списке примеров:
$ python -mtimeit -s'import nodup' 'nodup.doset(nodup.k)'
100000 loops, best of 3: 11.7 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dosort(nodup.k)'
100000 loops, best of 3: 9.68 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dogroupby(nodup.k)'
100000 loops, best of 3: 8.74 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.donewk(nodup.k)'
100000 loops, best of 3: 4.44 usec per loop
подтверждение того, что квадратичный подход имеет достаточно малые константы, чтобы сделать его привлекательным для крошечных списков с несколькими дублированными значениями. С кратким списком без дубликатов:
$ python -mtimeit -s'import nodup' 'nodup.donewk([[i] for i in range(12)])'
10000 loops, best of 3: 25.4 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dogroupby([[i] for i in range(12)])'
10000 loops, best of 3: 23.7 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.doset([[i] for i in range(12)])'
10000 loops, best of 3: 31.3 usec per loop
$ python -mtimeit -s'import nodup' 'nodup.dosort([[i] for i in range(12)])'
10000 loops, best of 3: 25 usec per loop
Квадратичный подход не плох, но сортировка и группировка лучше. И т. Д.
Если (как предполагает навязчивая идея производительности) эта операция находится в основном внутреннем цикле вашего приложения "раздвигающие границы", стоит попробовать тот же набор тестов на других репрезентативных входных выборках, возможно, обнаружив некоторую простую меру, которая может эвристически позволяет вам выбрать тот или иной подход (но, конечно, мера должна быть быстрой).
Также стоит подумать о сохранении другого представления для k
- почему это должен быть список списков, а не набор кортежей? Если задача удаления дубликатов встречается часто, а профилирование показывает, что она является узким местом в производительности программы, постоянное хранение набора кортежей и получение списка списков из нее только, если и где это необходимо, может быть быстрее, например, в целом.