Cara efisien untuk memutar daftar dengan python

263

Apa cara paling efisien untuk memutar daftar dengan python? Saat ini saya punya sesuatu seperti ini:

>>> def rotate(l, n):
...     return l[n:] + l[:n]
... 
>>> l = [1,2,3,4]
>>> rotate(l,1)
[2, 3, 4, 1]
>>> rotate(l,2)
[3, 4, 1, 2]
>>> rotate(l,0)
[1, 2, 3, 4]
>>> rotate(l,-1)
[4, 1, 2, 3]

Apakah ada cara yang lebih baik?

dzhelil
sumber
12
Ini tidak benar-benar bergeser karena bahasa lain (Perl, Ruby) menggunakan istilah ini. Ini memutar. Mungkin pertanyaannya harus diperbarui?
Vincent Fourmond
@ Dzhelil Saya sangat suka solusi asli Anda karena tidak memperkenalkan mutasi
juanchito
2
numpy.roll
BoltzmannBrain
2
Saya pikir rotateitu kata yang tepat, bukan shift.
codeforester
2
The nyata jawaban yang benar, adalah Anda tidak harus memutar daftar di tempat pertama. Buat variabel "pointer" ke tempat logis di daftar tempat Anda menginginkan "head" atau "tail", dan ubah variabel itu alih-alih memindahkan salah satu item dalam daftar. Cari operator "modulo"% untuk cara efisien untuk "membungkus" pointer Anda di awal dan akhir daftar.
cnd

Jawaban:

280

A collections.dequedioptimalkan untuk menarik dan mendorong kedua ujungnya. Mereka bahkan memiliki rotate()metode khusus .

from collections import deque
items = deque([1, 2])
items.append(3)        # deque == [1, 2, 3]
items.rotate(1)        # The deque is now: [3, 1, 2]
items.rotate(-1)       # Returns deque to original state: [1, 2, 3]
item = items.popleft() # deque == [2, 3]
Ignacio Vazquez-Abrams
sumber
8
Untuk pembaca yang akan datang: collections.deque rotate()lebih cepat daripada mengiris menurut wiki.python.org/moin/TimeComplexity
Geoff
2
Namun perlu diperhatikan, menggunakan deque.rotatemembutuhkan konversi tipe ke dequeobjek terlebih dahulu, yang lebih lambat dari l.append(l.pop(0)). Jadi, jika Anda memiliki objek deque untuk memulainya, pasti itu yang tercepat. Kalau tidak, gunakan l.append(l.pop(0)).
Purrell
8
Untuk menguraikan, deque.rotateadalah O (k) tetapi ketik konversi dari daftar ke deque adalah O (n) . Jadi jika Anda mulai dengan daftar, menggunakan deque.rotate adalah O (n) + O (k) = O (n). l.append(l.pop(0))di sisi lain adalah O (1).
Purrell
3
@Purrell, muncul item depan adalah O (n). Di wiki.python.org/moin/TimeComplexity terdaftar sebagai O (k), dan k adalah jumlah elemen dalam daftar yang mengikuti item yang muncul, karena struktur data menggeser semua elemen berikut ke bagian depan daftar. Hanya elemen terakhir yang dapat muncul dalam waktu O (1) karena alasan ini.
Kirk Boyer
88

Bagaimana kalau hanya menggunakan pop(0)?

list.pop([i])

Hapus item pada posisi yang diberikan dalam daftar, dan kembalikan. Jika tidak ada indeks yang ditentukan, a.pop()hapus dan kembalikan item terakhir dalam daftar. (Tanda kurung siku di idalam tanda tangan metode menunjukkan bahwa parameter bersifat opsional, bukan Anda harus mengetik tanda kurung siku pada posisi itu. Anda akan sering melihat notasi ini di Referensi Pustaka Python.)

Jamgold
sumber
16
Tapi bukankah akan membebani O (k) untuk menghapus setiap elemen dalam daftar di mana k adalah jumlah elemen yang tersisa. Jadi total waktu akan menjadi O (n ^ 2) wiki.python.org/moin/TimeComplexity
Pramod
5
Ini tidak benar-benar menjawab pertanyaan. Pertanyaannya bukan tentang mengembalikan barang secara berurutan, melainkan tentang membuat daftar baru dengan urutan yang berbeda.
user650261
5
tidak, jawaban atas pertanyaan menggunakan pop adalah l.append(l.pop(0). Yang jika saya tidak salah adalah O (1).
Purrell
4
list.pop secara internal memanggil list_ass_slice yang menggunakan memmove untuk memindahkan semua item dengan sangat cepat, tetapi masih O (n). Lihat github.com/python/cpython/blob/master/Objects/listobject.c dan wiki.python.org/moin/TimeComplexity . Satu-satunya item yang dapat dihapus dari daftar python dalam waktu konstan adalah yang terakhir.
DRayX
2
Diturunkan. Dari docs.python.org/3/tutorial/… Juga dimungkinkan untuk menggunakan daftar sebagai antrian, di mana elemen pertama yang ditambahkan adalah elemen pertama yang diambil (“masuk pertama, keluar pertama”); namun, daftar tidak efisien untuk tujuan ini. Sementara menambahkan dan muncul dari akhir daftar cepat, melakukan memasukkan atau muncul dari awal daftar lambat (karena semua elemen lain harus digeser oleh satu).
SantaXL
59

Numpy dapat melakukan ini menggunakan rollperintah:

>>> import numpy
>>> a=numpy.arange(1,10) #Generate some data
>>> numpy.roll(a,1)
array([9, 1, 2, 3, 4, 5, 6, 7, 8])
>>> numpy.roll(a,-1)
array([2, 3, 4, 5, 6, 7, 8, 9, 1])
>>> numpy.roll(a,5)
array([5, 6, 7, 8, 9, 1, 2, 3, 4])
>>> numpy.roll(a,9)
array([1, 2, 3, 4, 5, 6, 7, 8, 9])
Richard
sumber
1
Apa yang saya sukai tentang SO adalah bahwa kadang-kadang turun jawaban memberi Anda dapat menemukan beberapa harta baru yang hebat seperti ini :)
noamgot
Ini, ketika saya mengujinya, sangat, sangat lambat
Peter Harrison
@PeterHarrison: Karena Anda tidak memberikan detail pengujian, sulit untuk mengetahui apa yang Anda maksudkan. Jawaban ini memberikan detail pengujian lengkap dan perbandingan waktu.
Richard
33

Itu tergantung pada apa yang Anda inginkan terjadi ketika Anda melakukan ini:

>>> shift([1,2,3], 14)

Anda mungkin ingin mengubah:

def shift(seq, n):
    return seq[n:]+seq[:n]

untuk:

def shift(seq, n):
    n = n % len(seq)
    return seq[n:] + seq[:n]
jcdyer
sumber
5
NB: Ini akan macet untuk daftar kosong.
meawoppl
n = n% len (seq) return = seq [-n:] + seq [: - n]
user3303020
Bisakah Anda menjelaskan mengapa n = n% len (seq)?
AerysS
16

Cara paling sederhana yang bisa saya pikirkan:

a.append(a.pop(0))
Terima kasih
sumber
3
Ini adalah cara tercepat untuk daftar. collections.dequelebih cepat tetapi untuk sebagian besar kasus panjang daftar pada satu iterasi, atau kasus iterasi ganda, a.append(a.pop(0))akan lebih cepat daripada konversi tipe menjadi deque
Purrell
@runDOSrun jawaban sempurna untuk pertanyaan ini yang sayangnya ditutup sebagai duplikat. Mungkin Anda akan memilih untuk membukanya kembali?
Serigala
15

Jika Anda hanya ingin mengulangi set elemen ini daripada membangun struktur data terpisah, pertimbangkan untuk menggunakan iterator untuk membuat ekspresi generator:

def shift(l,n):
    return itertools.islice(itertools.cycle(l),n,n+len(l))

>>> list(shift([1,2,3],1))
[2, 3, 1]
Phil H
sumber
11

Ini juga tergantung pada apakah Anda ingin menggeser daftar di tempatnya (memutasikannya), atau jika Anda ingin fungsi mengembalikan daftar baru. Karena, menurut pengujian saya, sesuatu seperti ini setidaknya dua puluh kali lebih cepat daripada implementasi Anda yang menambahkan dua daftar:

def shiftInPlace(l, n):
    n = n % len(l)
    head = l[:n]
    l[:n] = []
    l.extend(head)
    return l

Bahkan, bahkan menambahkan a l = l[:]ke atas itu untuk beroperasi pada salinan daftar yang disahkan masih dua kali lebih cepat.

Berbagai implementasi dengan sejumlah waktu di http://gist.github.com/288272

keturn
sumber
3
Alih-alih l[:n] = []saya akan pergi untuk del l[:n]. Hanya sebuah alternatif.
tzot
1
Oh, ya, del tua yang baik. Saya sering lupa tentang del; operasi daftar itu pernyataan, bukan metode. Apakah py3k mengubah kekhasan itu, atau apakah kita masih mendapatkannya?
keturn
2
@keturn: delmasih merupakan pernyataan di Py3. Namun x.__delitem__(y) <==> del x[y], jadi jika Anda lebih suka menggunakan metode, l.__delitem__(slice(n))ini juga setara dan berfungsi di 2 & 3.
martineau
9

Hanya beberapa catatan tentang waktu:

Jika Anda memulai dengan daftar, l.append(l.pop(0))adalah metode tercepat yang dapat Anda gunakan. Ini dapat ditunjukkan dengan kompleksitas waktu saja:

  • deque.rotate adalah O (k) (k = jumlah elemen)
  • konversi daftar ke deque adalah O (n)
  • list.append dan list.pop keduanya O (1)

Jadi, jika Anda memulai dengan dequeobjek, Anda bisa deque.rotate()dengan biaya O (k). Tetapi, jika titik awal adalah daftar, kompleksitas waktu penggunaan deque.rotate()adalah O (n). l.append(l.pop(0)lebih cepat di O (1).

Hanya demi ilustrasi, berikut adalah beberapa contoh waktu pada iterasi 1M:

Metode yang memerlukan konversi jenis:

  • deque.rotatedengan objek deque: 0,12380790710449219 detik (tercepat)
  • deque.rotatedengan konversi tipe: 6.853878974914551 detik
  • np.rolldengan nparray: 6.0491721630096436 detik
  • np.rolldengan konversi tipe: 27.558452129364014 detik

Daftar metode yang disebutkan di sini:

  • l.append(l.pop(0)): 0,32483696937561035 detik (tercepat)
  • " shiftInPlace": 4,819645881652832 detik
  • ...

Kode waktu yang digunakan adalah di bawah ini.


collections.deque

Menunjukkan bahwa membuat deques dari daftar adalah O (n):

from collections import deque
import big_o

def create_deque_from_list(l):
     return deque(l)

best, others = big_o.big_o(create_deque_from_list, lambda n: big_o.datagen.integers(n, -100, 100))
print best

# --> Linear: time = -2.6E-05 + 1.8E-08*n

Jika Anda perlu membuat objek deque:

Iterasi 1M @ 6,853878974914551 detik

setup_deque_rotate_with_create_deque = """
from collections import deque
import random
l = [random.random() for i in range(1000)]
"""

test_deque_rotate_with_create_deque = """
dl = deque(l)
dl.rotate(-1)
"""
timeit.timeit(test_deque_rotate_with_create_deque, setup_deque_rotate_with_create_deque)

Jika Anda sudah memiliki objek deque:

Iterasi 1M @ 0,12380790710449219 detik

setup_deque_rotate_alone = """
from collections import deque
import random
l = [random.random() for i in range(1000)]
dl = deque(l)
"""

test_deque_rotate_alone= """
dl.rotate(-1)
"""
timeit.timeit(test_deque_rotate_alone, setup_deque_rotate_alone)

np.roll

Jika Anda perlu membuat nparrays

Iterasi 1M @ 27.558452129364014 detik

setup_np_roll_with_create_npa = """
import numpy as np
import random
l = [random.random() for i in range(1000)]
"""

test_np_roll_with_create_npa = """
np.roll(l,-1) # implicit conversion of l to np.nparray
"""

Jika Anda sudah memiliki nparray:

Iterasi 1M @ 6.0491721630096436 detik

setup_np_roll_alone = """
import numpy as np
import random
l = [random.random() for i in range(1000)]
npa = np.array(l)
"""

test_roll_alone = """
np.roll(npa,-1)
"""
timeit.timeit(test_roll_alone, setup_np_roll_alone)

"Bergeser di tempat"

Tidak memerlukan konversi jenis

Iterasi 1M @ 4,819645881652832 detik

setup_shift_in_place="""
import random
l = [random.random() for i in range(1000)]
def shiftInPlace(l, n):
    n = n % len(l)
    head = l[:n]
    l[:n] = []
    l.extend(head)
    return l
"""

test_shift_in_place="""
shiftInPlace(l,-1)
"""

timeit.timeit(test_shift_in_place, setup_shift_in_place)

l.append (l.pop (0))

Tidak memerlukan konversi jenis

Iterasi 1M @ 0.32483696937561035

setup_append_pop="""
import random
l = [random.random() for i in range(1000)]
"""

test_append_pop="""
l.append(l.pop(0))
"""
timeit.timeit(test_append_pop, setup_append_pop)
Purrell
sumber
2
sementara list.pop () adalah operasi waktu-konstan, list.pop (0) tidak . Ini berjalan dalam waktu linier sehubungan dengan panjang daftar. Anda dapat mengujinya dengan mengubah pengaturan waktu Anda:l = [random.random() for i in range(100000)]
emu
1
list.pop bukan operasi waktu yang konstan. list.pop berjalan dalam waktu O (k) di mana k adalah jumlah elemen yang melewati elemen yang dihapus, jadi list.pop (0) adalah O (n). Secara internal, list.pop menggunakan list_ass_slice yang menggunakan memmove untuk memindahkan item lebih cepat daripada yang pernah Anda lakukan dengan python, tetapi untuk daftar panjang itu masih sangat memakan waktu. Lihat github.com/python/cpython/blob/master/Objects/listobject.c dan wiki.python.org/moin/TimeComplexity
DRayX
Terima kasih atas waktunya (dan komentar @emu). Jadi dapatkah kita mengatakan bahwa l.append(l.pop(0))ini adalah yang terbaik untuk menggeser daftar pendek (sekitar 7 elemen) satu per satu?
Serigala
Sekali lagi, menyangkut l.append(l.pop(0))sebagai jawaban: Pertanyaan ini ditutup sebagai duplikat. Mungkin Anda akan memilih untuk membukanya kembali?
Wolf
8

Saya juga tertarik dengan ini dan membandingkan beberapa solusi yang disarankan dengan perfplot (proyek kecil saya).

Ternyata itu

for _ in range(n):
    data.append(data.pop(0))

adalah jauh metode tercepat untuk pergeseran kecil n.

Untuk yang lebih besar n,

data[n:] + data[:n]

tidak buruk.

Pada dasarnya, perfplot melakukan pergeseran untuk meningkatkan array besar dan mengukur waktu. Inilah hasilnya:

shift = 1:

masukkan deskripsi gambar di sini

shift = 100:

masukkan deskripsi gambar di sini


Kode untuk mereproduksi plot:

import numpy
import perfplot
import collections


shift = 100


def list_append(data):
    return data[shift:] + data[:shift]


def shift_concatenate(data):
    return numpy.concatenate([data[shift:], data[:shift]])


def roll(data):
    return numpy.roll(data, -shift)


def collections_deque(data):
    items = collections.deque(data)
    items.rotate(-shift)
    return items


def pop_append(data):
    for _ in range(shift):
        data.append(data.pop(0))
    return data


perfplot.save(
    "shift100.png",
    setup=lambda n: numpy.random.rand(n).tolist(),
    kernels=[list_append, roll, shift_concatenate, collections_deque, pop_append],
    n_range=[2 ** k for k in range(7, 20)],
    logx=True,
    logy=True,
    xlabel="len(data)",
)
Nico Schlömer
sumber
Alat bagus yang Anda buat. Mengenai l.append(l.pop(0))jawaban: Pertanyaan ini ditutup sebagai duplikat. Mungkin Anda akan memilih untuk membukanya kembali?
Wolf
4

Mungkin ringbuffer lebih cocok. Ini bukan daftar, meskipun kemungkinan itu bisa berperilaku cukup seperti daftar untuk tujuan Anda.

Masalahnya adalah bahwa efisiensi pergeseran pada daftar adalah O (n), yang menjadi signifikan untuk daftar yang cukup besar.

Bergeser di ringbuffer hanya memperbarui lokasi kepala yaitu O (1)

John La Rooy
sumber
4

Untuk implementasi yang tidak berubah, Anda dapat menggunakan sesuatu seperti ini:

def shift(seq, n):
    shifted_seq = []
    for i in range(len(seq)):
        shifted_seq.append(seq[(i-n) % len(seq)])
    return shifted_seq

print shift([1, 2, 3, 4], 1)
Bittercoder
sumber
3

Jika efisiensi adalah tujuan Anda, (siklus? Memori?) Anda mungkin lebih baik melihat modul array: http://docs.python.org/library/array.html

Array tidak memiliki overhead daftar.

Sejauh daftar murni pergi, apa yang Anda miliki adalah tentang sebaik yang bisa Anda harapkan untuk dilakukan.

rekursif
sumber
3

Saya pikir Anda mencari ini:

a.insert(0, x)
redreamality
sumber
Saya tidak melihat hubungan antara pertanyaan dan jawaban Anda. Bisakah Anda jelaskan?
Wolf
2

Alternatif lain:

def move(arr, n):
    return [arr[(idx-n) % len(arr)] for idx,_ in enumerate(arr)]
damio
sumber
1

Saya menggunakan model biaya ini sebagai referensi:

http://scripts.mit.edu/~6.006/fall07/wiki/index.php?title=Python_Cost_Model

Metode Anda mengiris daftar dan menggabungkan dua sub-daftar adalah operasi waktu linier. Saya sarankan menggunakan pop, yang merupakan operasi waktu-konstan, misalnya:

def shift(list, n):
    for i in range(n)
        temp = list.pop()
        list.insert(0, temp)
herrfz
sumber
2
perbarui: anggap ini sebagai referensi yang lebih baik: wiki.python.org/moin/TimeComplexity , gunakan collections.dequeuepop dan appendleft, yang keduanya adalah O (1) ops. Dalam jawaban pertama saya di atas, masukkan adalah O (n).
herrfz
1
haruscollections.deque
herrfz
1

Saya tidak tahu apakah ini 'efisien', tetapi juga berfungsi:

x = [1,2,3,4]
x.insert(0,x.pop())

EDIT: Halo lagi, saya baru saja menemukan masalah besar dengan solusi ini! Pertimbangkan kode berikut:

class MyClass():
    def __init__(self):
        self.classlist = []

    def shift_classlist(self): # right-shift-operation
        self.classlist.insert(0, self.classlist.pop())

if __name__ == '__main__':
    otherlist = [1,2,3]
    x = MyClass()

    # this is where kind of a magic link is created...
    x.classlist = otherlist

    for ii in xrange(2): # just to do it 2 times
        print '\n\n\nbefore shift:'
        print '     x.classlist =', x.classlist
        print '     otherlist =', otherlist
        x.shift_classlist() 
        print 'after shift:'
        print '     x.classlist =', x.classlist
        print '     otherlist =', otherlist, '<-- SHOULD NOT HAVE BIN CHANGED!'

Metode shift_classlist () mengeksekusi kode yang sama dengan x.insert (0, x.pop ()) - solusi saya, otherlist adalah daftar indipendent dari kelas. Setelah meneruskan konten daftar lain ke daftar MyClass.classlist, memanggil shift_classlist () juga mengubah daftar daftar lain:

OUTPUT KONSOL:

before shift:
     x.classlist = [1, 2, 3]
     otherlist = [1, 2, 3]
after shift:
     x.classlist = [3, 1, 2]
     otherlist = [3, 1, 2] <-- SHOULD NOT HAVE BIN CHANGED!



before shift:
     x.classlist = [3, 1, 2]
     otherlist = [3, 1, 2]
after shift:
     x.classlist = [2, 3, 1]
     otherlist = [2, 3, 1] <-- SHOULD NOT HAVE BIN CHANGED!

Saya menggunakan Python 2.7. Saya tidak tahu apakah itu bug, tapi saya pikir lebih mungkin saya salah mengerti sesuatu di sini.

Apakah ada di antara kalian yang tahu mengapa ini terjadi?

wese3112
sumber
2
Itu terjadi karena x.classlist = otherlistmembuat x.classlistmerujuk ke daftar yang sama seperti otherlistdan kemudian ketika Anda memanggilnya x.shift_classlist()bermutasi daftar dan karena kedua nama merujuk ke objek daftar yang sama. Kedua nama tampaknya berubah karena mereka hanya alias untuk objek yang sama. Gunakan x.classlist = otherlist[:]sebagai gantinya untuk menetapkan salinan daftar.
Dan D.
Hai wow! Terima kasih banyak! Saya benar-benar tidak tahu itu dan Ini benar-benar baik untuk diketahui! :)
wese3112
1

Metode berikut adalah O (n) pada tempatnya dengan memori bantu konstan:

def rotate(arr, shift):
  pivot = shift % len(arr)
  dst = 0
  src = pivot
  while (dst != src):
    arr[dst], arr[src] = arr[src], arr[dst]
    dst += 1
    src += 1
    if src == len(arr):
      src = pivot
    elif dst == pivot:
      pivot = src

Perhatikan bahwa dalam python, pendekatan ini sangat tidak efisien dibandingkan dengan yang lain karena tidak dapat mengambil keuntungan dari implementasi asli dari salah satu bagian.

DRAYX
sumber
baik, sebenarnya Anda bisa menggunakan list.pop dan list.append. Itu bukan kesalahan bahasa yang Anda tulis fungsi 12 baris yang O (n), ketika Anda bisa saja menulis "l.append (l.pop (0))" yang merupakan waktu konstan.
Purrell
l.append (l.pop (0)) adalah O (n) (l.pop (0) harus menggeser setiap elemen), jadi jika Anda ingin menggeser nilai m, kompleksitas sebenarnya adalah O (n * m). Kompleksitas algoritma yang saya sediakan adalah O (n) terlepas dari jumlah shift. Dalam praktiknya, ini lambat karena begitu banyak logika dilakukan dalam ops python, bukan C (list.pop diimplementasikan dalam c, lihat github.com/python/cpython/blob/master/Objects/listobject.c ).
DRayX
1

Saya punya hal serupa. Misalnya, untuk beralih dua ...

def Shift(*args):
    return args[len(args)-2:]+args[:len(args)-2]
EyoelD
sumber
1

Saya pikir Anda punya cara yang paling efisien

def shift(l,n):
    n = n % len(l)  
    return l[-U:] + l[:-U]
john ktejik
sumber
0

Apa gunanya? Seringkali, kita tidak benar-benar membutuhkan array yang sepenuhnya bergeser - kita hanya perlu mengakses beberapa elemen dalam array yang digeser.

Mendapatkan irisan Python adalah runtime O (k) di mana k adalah slice, jadi rotasi irisan adalah runtime N. Perintah rotasi deque juga O (k). Bisakah kita berbuat lebih baik?

Pertimbangkan sebuah array yang sangat besar (katakanlah, begitu besar itu akan lambat untuk mengirisnya). Solusi alternatif adalah meninggalkan array asli sendirian dan hanya menghitung indeks item yang akan ada dalam indeks yang diinginkan setelah perubahan jenis.

Mengakses elemen yang bergeser dengan demikian menjadi O (1).

def get_shifted_element(original_list, shift_to_left, index_in_shifted):
    # back calculate the original index by reversing the left shift
    idx_original = (index_in_shifted + shift_to_left) % len(original_list)
    return original_list[idx_original]

my_list = [1, 2, 3, 4, 5]

print get_shifted_element(my_list, 1, 2) ----> outputs 4

print get_shifted_element(my_list, -2, 3) -----> outputs 2 
Nick Lee
sumber
0

Fungsi berikut menyalin daftar yang dikirim ke templist, sehingga fungsi pop tidak mempengaruhi daftar asli:

def shift(lst, n, toreverse=False):
    templist = []
    for i in lst: templist.append(i)
    if toreverse:
        for i in range(n):  templist = [templist.pop()]+templist
    else:
        for i in range(n):  templist = templist+[templist.pop(0)]
    return templist

Pengujian:

lst = [1,2,3,4,5]
print("lst=", lst)
print("shift by 1:", shift(lst,1))
print("lst=", lst)
print("shift by 7:", shift(lst,7))
print("lst=", lst)
print("shift by 1 reverse:", shift(lst,1, True))
print("lst=", lst)
print("shift by 7 reverse:", shift(lst,7, True))
print("lst=", lst)

Keluaran:

lst= [1, 2, 3, 4, 5]
shift by 1: [2, 3, 4, 5, 1]
lst= [1, 2, 3, 4, 5]
shift by 7: [3, 4, 5, 1, 2]
lst= [1, 2, 3, 4, 5]
shift by 1 reverse: [5, 1, 2, 3, 4]
lst= [1, 2, 3, 4, 5]
shift by 7 reverse: [4, 5, 1, 2, 3]
lst= [1, 2, 3, 4, 5]
juga
sumber
0

Jon Bentley dalam Pemrograman Mutiara (Kolom 2) menjelaskan algoritma yang elegan dan efisien untuk memutar nvektor elemen- xkiri oleh iposisi:

Mari kita lihat masalah sebagai mentransformasikan array abke dalam array ba, tetapi mari kita juga berasumsi bahwa kita memiliki fungsi yang membalikkan elemen-elemen dalam bagian tertentu dari array. Dimulai dengan ab, kita membalikkan auntuk mendapatkan , membalikkan untuk mendapatkan , dan kemudian membalikkan semuanya untuk mendapatkan , yang tepatnya . Ini menghasilkan kode berikut untuk rotasi:arbbarbr(arbr)rba

reverse(0, i-1)
reverse(i, n-1)
reverse(0, n-1)

Ini dapat diterjemahkan ke Python sebagai berikut:

def rotate(x, i):
    i %= len(x)
    x[:i] = reversed(x[:i])
    x[i:] = reversed(x[i:])
    x[:] = reversed(x)
    return x

Demo:

>>> def rotate(x, i):
...     i %= len(x)
...     x[:i] = reversed(x[:i])
...     x[i:] = reversed(x[i:])
...     x[:] = reversed(x)
...     return x
... 
>>> rotate(list('abcdefgh'), 1)
['b', 'c', 'd', 'e', 'f', 'g', 'h', 'a']
>>> rotate(list('abcdefgh'), 3)
['d', 'e', 'f', 'g', 'h', 'a', 'b', 'c']
>>> rotate(list('abcdefgh'), 8)
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']
>>> rotate(list('abcdefgh'), 9)
['b', 'c', 'd', 'e', 'f', 'g', 'h', 'a']
Eugene Yarmash
sumber
0

Untuk daftar X = ['a', 'b', 'c', 'd', 'e', 'f']dan nilai pergeseran yang diinginkan shift kurang dari panjang daftar , kita dapat mendefinisikan fungsi list_shift()seperti di bawah ini

def list_shift(my_list, shift):
    assert shift < len(my_list)
    return my_list[shift:] + my_list[:shift]

Contoh,

list_shift(X,1)mengembalikan ['b', 'c', 'd', 'e', 'f', 'a'] list_shift(X,3)pengembalian['d', 'e', 'f', 'a', 'b', 'c']

helcode
sumber
1
Persis seperti yang dimiliki OP. Anda baru saja mengubah nama dan menambahkan pernyataan.
RufusVS
Fungsi list_shiftdalam jawaban Anda identik dengan fungsi shiftdalam pertanyaan awal, jadi ini bukan jawaban untuk pertanyaan aktual: "Apakah ada cara yang lebih baik?"
RufusVS
0
def solution(A, K):
    if len(A) == 0:
        return A

    K = K % len(A)

    return A[-K:] + A[:-K]

# use case
A = [1, 2, 3, 4, 5, 6]
K = 3
print(solution(A, K))

Misalnya diberikan

A = [3, 8, 9, 7, 6]
K = 3

fungsi harus kembali [9, 7, 6, 3, 8]. Tiga rotasi dibuat:

[3, 8, 9, 7, 6] -> [6, 3, 8, 9, 7]
[6, 3, 8, 9, 7] -> [7, 6, 3, 8, 9]
[7, 6, 3, 8, 9] -> [9, 7, 6, 3, 8]

Sebagai contoh lain, diberikan

A = [0, 0, 0]
K = 1

fungsi harus kembali [0, 0, 0]

Diberikan

A = [1, 2, 3, 4]
K = 4

fungsi harus kembali [1, 2, 3, 4]

Rakesh Kumar
sumber
0

Saya sedang mencari solusi untuk masalah ini. Ini memecahkan tujuan dalam O (k).

def solution(self, list, k):
    r=len(list)-1
    i = 0
    while i<k:
        temp = list[0]
        list[0:r] = list[1:r+1]
        list[r] = temp
        i+=1
    return list
Ankit
sumber
-3

untuk fungsi yang sama dengan pergeseran dalam bahasa lain:

def shift(l):
    x = l[0]
    del(l[0])
    return x
David
sumber
1
-1: Ini melakukan sesuatu yang berbeda dari yang diminta, dan BTW juga setara denganL.pop(0)
6502