Python: Какой самый быстрый способ архивировать справа налево, и нет ли встроенного для этого? - PullRequest
5 голосов
/ 04 ноября 2011

Дано 2 последовательности различной длины:

In [931]: a = [1,2,3]
In [932]: b = [4,5,6,7]

Это то, что я хочу

In [933]: c = zip(reversed(a),reversed(b))
In [934]: [x for x in reversed(c)]
Out[934]: [(1, 5), (2, 6), (3, 7)]

Но мне не нравится идея использовать реверсированный для всех моих входных параметров, и я также не хочу повторно реализовывать свою собственную функцию zip.

Итак:

  1. Есть ли более быстрый / более эффективный способ сделать это?
  2. Есть ли более простой способ сделать это?

Ответы [ 6 ]

6 голосов
/ 04 ноября 2011

Это быстрее и понятнее, чем другие опубликованные решения:

s = zip(reversed(a), reversed(b))
s.reverse()                       # in-place

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

3 голосов
/ 04 ноября 2011

Следующее будет работать, даже если вы не знаете, что длиннее:

>>> zip(a[-len(b):], b[-len(a):])
[(1, 5), (2, 6), (3, 7)]

Вот пример того, что происходит с меньшим списком:

>>> range(5)[-10:]
[0, 1, 2, 3, 4]

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

2 голосов
/ 04 ноября 2011

Я бы предложил:

>>> a = [1,2,3]
>>> b = [4,5,6,7]
>>> k = min(len(a),len(b))
>>> zip(a[-k:], b[-k:])
[(1, 5), (2, 6), (3, 7)]
1 голос
/ 05 ноября 2011

В следующем решении копирование любого из списков ввода не производится.

def rzip(a,b):
    m = min(len(a),len(b))
    for i in xrange(m):
        yield a[-m+i],b[-m+i]


for a,b in (([1,2,3,4,5,6,7],[8,9,10]),
            ([1,2,3],[8,9,10]),
            ([1,2,3,4],[1,2,3,4,5,6,7,8,9,10])):
    print list(rzip(a,b))

m - длина самого короткого списка, -m + 0 всегда преобразуется в индекс 0 для этого кратчайшего списка,
в то время как -m + 0 преобразуется в индекс> = 0 для самого длинного списка

результат:

[(5, 8), (6, 9), (7, 10)]
[(1, 8), (2, 9), (3, 10)]
[(1, 7), (2, 8), (3, 9), (4, 10)]

Несмотря на то, что функция rzip () не является однострочной, я думаю, что это просто, и я уверен, что это более быстрое решение

.

Редактировать

Сначала в функции rzip () выше (и eyquem1 в следующем коде) я подумал, что создание списка заархивированных элементов путем запуска из правильной позиции в каждом списке слева направо быть хорошей процедурой тоже. В следующем коде eyquem1bis - это функция rzip () , слегка улучшенная за счет предварительного расчета начальных позиций.

Но меня заинтересовало утверждение Раймонда Хеттингера о том, что исключительное использование zip () и итератора reversed () является более ясным и быстрым решением. Поэтому я подумал, что использование других итераторов слева направо будет лучше, чем моя функция rzip () , и я использовал iter () и islice () для записи функции eyquem2 и eyquem3 в следующем коде.

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

import random
from time import clock

lima,limb = 20000 , 50000

a = list(range(lima))
b = list(range(limb))
random.shuffle(a)
random.shuffle(b)


A,B,C,D,E,F,G,H,I = [],[],[],[],[],[],[],[],[],

n = 10
for essays in range(100):

    te = clock()
    for rep in range(n):
        k = min(len(a),len(b)) 
        sam1 = list(zip(a[-k:], b[-k:]))
    tref= (clock()-te)/100
    A.append((clock()-te,
              'Sam Hocevar, deducted normal slicing and zip'
              '\nk = min(len(a),len(b))'
              '\nlist(zip(a[-k:], b[-k:]))'))


    te = clock()
    for rep in range(n):
        k = min(len(a),len(b)) 
        sam2 = list(zip(a[len(a)-k:], b[len(b)-k:]))
    tref= (clock()-te)/100
    B.append((clock()-te,
              'Sam Hocevar, exactly normal slicing and zip'
              '\nk = min(len(a),len(b))'
              '\nlist(zip(a[len(a)-k:], b[len(b)-k:]))'))


    te = clock()
    for rep in range(n):
        fj = list(zip(a[-len(b):], b[-len(a):]))
    C.append((clock()-te,
              'F.J. , deducted tricky slicing and zip'
              '\nlist(zip(a[-len(b):], b[-len(a):]))'))


    te = clock()
    for rep in range(n):
        m = min(len(a),len(b))
        sleep = list(zip(a[len(a)-m:],b[len(b)-m:]))
    D.append((clock()-te,
              'sleeplessnerd, exactly normal slicing and zip'
              '\nm = min(len(a),len(b))'
              '\nlist(zip(a[len(a)-m:],b[len(b)-m:]))'))


    te = clock()
    for rep in range(n):
        m = min(len(a),len(b))
        ey1 = [ (a[-m+i],b[-m+i]) for i in range(m) ]
    E.append((clock()-te,
              'eyquem 1, deducted normal slicing and listcomp'
              '\nm = min(len(a),len(b))'
              '\n[ (a[-m+i],b[-m+i]) for i in range(m) ]'))


    te = clock()
    for rep in range(n):
        m = min(len(a),len(b))
        x,y = len(a)-m , len(b)-m
        ey1bis = [ (a[x+i],b[y+i]) for i in range(m)]
    F.append((clock()-te,
              'eyquem 1 improved, exactly normal slicing and listcomp'
              '\nm = min(len(a),len(b)'
              '\nx,y = len(a)-m , len(b)-m'
              '\n[(a[x+i],b[y+i]) for i in range(m)]'))


    te = clock()
    for rep in range(n):
        ita = iter(a)
        itb = iter(b)
        if len(b)>len(a):
            for av in range(len(b)-len(a)):
                itb.__next__()
        else:
            for av in range(len(a)-len(b)):
                itb.__next__()
        ey2 = list(zip(ita,itb))
    G.append((clock()-te,
              'eyquem 2, use of zip and iterator iter'
              '\nlist(zip(ita,itb))'))


    from itertools import islice
    te = clock()
    for rep in range(n):
        if len(b)>len(a):
            ey3 = list(zip(iter(a) , islice(b,len(b)-len(a),None)))
        else:
            ey3 = list(zip(islice(a,len(a)-len(b),None),iter(b)))
    H.append((clock()-te,
              'eyquem 3, use of zip and iterators iter AND islice'
              '\nlist(zip(iter(a),islice(b,len(b)-len(a),None)))'))


    te = clock()
    for rep in range(n):
        ray = list(reversed(list(zip(reversed(a),reversed(b)))))
    I.append((clock()-te,
              'Raymond Hettinger, use of zip and iterator reversed'
              '\nlist(reversed(list(zip(reversed(a),reversed(b)))))'))


print( 'len(a) == %d\nlen(b) == %d\n' % (len(a),len(b)) )

tempi = [min(x) for x in (A,B,C,D,E,F,G,H,I)]
tempi.sort(reverse=True)
tref = tempi[0][0]/100
print( '\n\n'.join('%.2f %%     %s' % (t/tref,ch) for t,ch in tempi) )


print('\nsam1==sam2==fj==sleep==ey1==ey1bis==ey2==ey3==ray  is ',sam1==sam2==fj==sleep==ey1==ey1bis==ey2==ey3==ray)

Результат со списками очень разных длин:

len(a) == 20000
len(b) == 50000

100.00 %     Sam Hocevar, exactly normal slicing and zip
k = min(len(a),len(b))
list(zip(a[len(a)-k:], b[len(b)-k:]))

99.80 %     Sam Hocevar, deducted normal slicing and zip
k = min(len(a),len(b))
list(zip(a[-k:], b[-k:]))

98.02 %     sleeplessnerd, exactly normal slicing and zip
m = min(len(a),len(b))
list(zip(a[len(a)-m:],b[len(b)-m:]))

97.98 %     F.J. , deducted tricky slicing and zip
list(zip(a[-len(b):], b[-len(a):]))

82.30 %     eyquem 2, use of zip and iterator iter
list(zip(ita,itb))

69.61 %     eyquem 1, deducted normal slicing and listcomp
m = min(len(a),len(b))
[ (a[-m+i],b[-m+i]) for i in range(m) ]

67.62 %     eyquem 1 improved, exactly normal slicing and listcomp
m = min(len(a),len(b)
x,y = len(a)-m , len(b)-m
[(a[x+i],b[y+i]) for i in range(m)]

61.23 %     eyquem 3, use of zip and iterators iter AND islice
list(zip(iter(a),islice(b,len(b)-len(a),None)))

60.92 %     Raymond Hettinger, use of zip and iterator reversed
list(reversed(list(zip(reversed(a),reversed(b)))))

sam1==sam2==fj==sleep==ey1==ey1bis==ey2==ey3==ray  is  True

Результат с двумя списками одинаковой длины:

len(a) == 49500
len(b) == 50000

100.00 %     Sam Hocevar, deducted normal slicing and zip
k = min(len(a),len(b))
list(zip(a[-k:], b[-k:]))

99.39 %     F.J. , deducted tricky slicing and zip
list(zip(a[-len(b):], b[-len(a):]))

99.12 %     sleeplessnerd, exactly normal slicing and zip
m = min(len(a),len(b))
list(zip(a[len(a)-m:],b[len(b)-m:]))

98.10 %     Sam Hocevar, exactly normal slicing and zip
k = min(len(a),len(b))
list(zip(a[len(a)-k:], b[len(b)-k:]))

69.91 %     eyquem 1, deducted normal slicing and listcomp
m = min(len(a),len(b))
[ (a[-m+i],b[-m+i]) for i in range(m) ]

66.54 %     eyquem 1 improved, exactly normal slicing and listcomp
m = min(len(a),len(b)
x,y = len(a)-m , len(b)-m
[(a[x+i],b[y+i]) for i in range(m)]

58.94 %     Raymond Hettinger, use of zip and iterator reversed
list(reversed(list(zip(reversed(a),reversed(b)))))

51.29 %     eyquem 2, use of zip and iterator iter
list(zip(ita,itb))

51.17 %     eyquem 3, use of zip and iterators iter AND islice
list(zip(iter(a),islice(b,len(b)-len(a),None)))

sam1==sam2==fj==sleep==ey1==ey1bis==ey2==ey3==ray  is  True

Я заключаю, что существует 3 группы решений:

  • решения Сэма Хочевара, FJ и sleeplessnerd имеют самые длинные исполнения.
    Они используют разные комбинации нарезки и функции zip ().
    Кстати, решение Сэма, одобренное в подавляющем большинстве случаев, самое длинное.

  • my function rzip () и rzip () - улучшенное выполнение за 68% времени, затраченного теми, кто находится в вышеуказанной группе.
    Они используют только понимание списка элементов, полученных по их индексам

  • мои растворы eyquem2 и eyquem3, а также раствор Рэймонда Хеттингера занимают менее 61% указанного времени.
    Они используют итераторы.

Время выполнения моих решений зависит от длины списка:
для списков аналогичной длины решения eyquem2 и eyquem3 имеют времена выполнения, разделенные на 2!
Но для очень разных списков eyquem2 неожиданно занимает целых 82% от эталонного времени.

Постоянное время исполнения около 60% контрольного времени решения Раймонда Хеттингера подтверждает его претензию.
Мое решение eyquem3 с iter () и islice () тоже не так уж и плохо, но не так ясно, как у Рэймонда Хеттингера, хотя последнее слишком мало Первый взгляд.

0 голосов
/ 04 ноября 2011

[Обновлено, чтобы добавить возможность обратного упорядочения (потому что я думал, что смогу использовать это однажды)]

Как генератор, вы можете попробовать,

def reversed_zip(*l, from_right=False):

    i = min(len(l) for l in l)
    i, j, k = (1, i+1, 1) if from_right else (i, 0, -1) 

    while i != j:
        yield tuple( x[-i] for x in l ) 
        i += k

Это не должно брать копии списков, и дает,

>>> a = [1,2,3]
>>> b = [4,5,6,7]

>>> list(reversed_zip(a, b))
[(1, 5), (2, 6), (3, 7)]

>>> list(reversed_zip(a, b, from_right=True))
[(3, 7), (2, 6), (1, 5)]
0 голосов
/ 04 ноября 2011
zip(a[len(a)-min(len(a), len(b)):], b[len(b)-min(len(a), len(b)):])

но выглядит не очень "питонно": P

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...