Apakah ada algoritma yang menemukan urutan berikutnya dari ukuran tiga dalam waktu ?

21

Saya ingin membuktikan atau menyangkal keberadaan suatu algoritma yang, mengingat array dari bilangan bulat, menemukan tiga indeks dan sedemikian rupa sehingga dan (atau menemukan bahwa tidak ada triple seperti itu) dalam waktu linier.SEBUAHsaya,jksaya<j<kSEBUAH[saya]<SEBUAH[j]<SEBUAH[k]

Ini bukan pertanyaan pekerjaan rumah; Saya melihatnya di forum pemrograman yang dibingkai sebagai "cobalah untuk mengimplementasikan algoritma seperti itu." Saya curiga tidak mungkin setelah berbagai percobaan. Intuisi saya mengatakan demikian, tetapi itu tidak berarti apa-apa.

Saya ingin membuktikannya secara formal. Bagaimana Anda melakukannya? Saya idealnya ingin melihat bukti yang disusun langkah demi langkah, dan kemudian jika Anda cenderung, beberapa penjelasan tentang bagaimana cara membuktikan / menyangkal pertanyaan sederhana seperti ini secara umum. Jika itu membantu, beberapa contoh:

[1,5,2,0,3] → (1,2,3)
[5,6,1,2,3] → (1,2,3)
[1,5,2,3] → (1,2,3)
[5,6,1,2,7] → (1,2,7)
[5,6,1,2,7,8] → (1,2,7)
[1,2,999,3] → (1,2,999)
[999,1,2,3] → (1,2,3)
[11,12,8,9,5,6,3,4,1,2,3] → (1,2,3)
[1,5,2,0,-5,-2,-1] → (-5,-2,-1)

Saya menduga bahwa orang bisa iterate atas , dan setiap kali ada (saat ini kami , yaitu), kami membuat tiga baru dan mendorongnya ke array. Kami terus melangkah dan membandingkan setiap tiga hingga satu dari tiga kali lipat kami selesai. Jadi seperti , ! Tapi saya pikir ini lebih kompleks daripada sekadar karena jumlah tiga kali lipat pada triple array kami akan dalam kasus terburuk sesuai dengan ukuran daftar input.SEBUAHsaya<jj[1,5,2,0,-5,-2,-1] → 1..2.. -5.. -2.. -1[1,5,2,0,-5,-2,3,-1] → 1..2.. -5.. -2.. 3O(n)

Christopher Done
sumber
Perhatikan bahwa dalam kasus terburuk (array yang diurutkan) Anda bahkan memiliki tiga kali lipat yang sesuai. Harap pertimbangkan untuk memberikan algoritma yang Anda usulkan sebagai kode semu; Saya pikir penjelasan Anda tidak lengkap. Θ(n3)
Raphael

Jawaban:

14

Ini adalah variasi dari masalah kenaikan terpanjang meningkat ; ini adalah solusi yang disajikan di Wikipedia menggunakan dua array bantu dan P :MP

  • - menyimpan posisi k dengan nilai terkecil A [ k ] sedemikian rupa sehingga ada peningkatan panjang j yang berakhir pada A [ k ] pada rentang k i (perhatikan kita memiliki j k i di sini, karena j mewakili panjang dari kenaikan berikutnya, dan k mewakili posisi penghentiannya.Tentu saja, kita tidak akan pernah memiliki peningkatan selanjutnya dari panjang 13 yang berakhir pada posisi 11M[j]kA[k]jA[k]kijkijk1311. menurut definisi).ki
  • - menyimpan posisi pendahulu A [ k ] dalam peningkatan terpanjang yang berakhir pada A [ k ] .P[k]A[k]A[k]

    Selain itu, algoritma menyimpan variabel mewakili panjang kenaikan paling lama yang ditemukan sejauh ini.L

Algoritma ini berjalan dalam waktu terburuk . Masalah Anda adalah kasus khusus yang memungkinkan Anda untuk kembali ketika L = 3 yang mendorong runtime ke O ( n ) karena pencarian biner hanya berjalan pada panjang array paling banyak dua, yang karena itu dalam waktu O ( 1 ) sebagai lawan dari Θ ( log n ) dalam kasus umum.Θ(nlogn)L.=3HAI(n)HAI(1)Θ(logn)

Pertimbangkan kode pseudo yang dimodifikasi:

 L = 0
 for i = 1, 2, ... n:
    binary search for the largest positive j ≤ L
      such that X[M[j]] < X[i] (or set j = 0 if no such value exists)
    P[i] = M[j]
    if j == L or X[i] < X[M[j+1]]:
       M[j+1] = i
       L = max(L, j+1)
   if L==3 : return true; // you can break here, and return true.
return false; // because L is smaller than 3.

sumber
@ SaeedAmiri saya melihat komentar tetapi saya belum punya waktu untuk memeriksanya (saya memposting pertanyaan sebelum tidur). Saya curiga dari tautan Anda bahwa case khusus kami L = 3 akan membantu tetapi tidak memiliki kesempatan untuk memahami detailnya. Saya saat ini sedang bekerja dan waktu terbatas. Yakinlah bahwa saya menghargai jawaban Anda. Dangkal saya akan berterima kasih untuk itu tanpa sepenuhnya memahami setiap baris di dalamnya.
Christopher Done
@ SaeedAmiri: Saya setuju Anda mengharapkan lebih banyak "mengisi celah" di sini, tetapi Anda masih harus memberikan argumen sudut bukti (setidaknya sketsa) setidaknya. Mengenai OP, dia tampaknya berbasis di Italia jadi mungkin tertidur cepat antara komentar dan jawaban Anda (dan kemungkinan dia sedang sibuk dengan timur sekarang).
Raphael
@ChristopherDone, saya tidak ingin mengecewakan Anda, maaf ini kesalahan saya, Anda pasti benar.
+1: Ini menggeneralisasi dengan baik, hanya membuat satu operan dan merupakan ruang. HAI(1)
Aryabhata
OK, kelihatannya bagus. Butuh beberapa saat untuk grok perilaku algoritma urutan meningkat terpanjang umum. Setelah itu, panjang maks == 3 perubahan baik-baik saja. Terima kasih!
Christopher Done
11

Catatan tentang metodologi

Saya berpikir sedikit tentang masalah ini, dan menemukan solusi. Ketika saya membaca jawaban Saeed Amiri , saya menyadari bahwa apa yang saya temukan adalah versi khusus dari algoritma pencarian hasil terpanjang standar untuk urutan panjang 3. Saya memposting cara saya datang dengan solusi, karena saya pikir itu adalah contoh menarik dari pemecahan masalah.

Versi dua elemen

Mari kita mulai dari yang kecil: alih-alih mencari tiga indeks di mana elemen-elemennya berurutan, mari kita cari dua: sedemikian rupa sehingga A [ i ] < A [ j ] .saya<jSEBUAH[saya]<SEBUAH[j]

Jika menurun (yaitu i < j , A [ i ] A [ j ] , atau setara dengan i , A [ i ] A [ i + 1 ] ), maka tidak ada indeks seperti itu. Kalau tidak, ada indeks i sedemikian rupa sehingga A [ i ] < A [ i + 1 ] .SEBUAHsaya<j,SEBUAH[saya]SEBUAH[j]saya,SEBUAH[saya]SEBUAH[saya+1]sayaSEBUAH[saya]<SEBUAH[saya+1]

Kasus ini sangat sederhana; kami akan mencoba menyamaratakannya. Ini menunjukkan bahwa masalah seperti yang disebutkan tidak dapat dipecahkan: indeks yang diminta tidak selalu ada. Jadi kami lebih suka bertanya apakah algoritma mengembalikan indeks yang valid, jika ada, atau mengklaim dengan benar bahwa tidak ada indeks seperti itu.

Datang dengan algoritme

Saya akan menggunakan istilah berikutnya untuk berarti ekstrak dari array terdiri dari indeks yang mungkin tidak berurutan ( ( A [ i 1 ] , , A [ i m ] ) dengan i 1 < < i m ), dan jalankan berarti elemen berurutan dari A ( ( A [ i ] , A [ i + 1 ] , ... , A [SEBUAH(SEBUAH[saya1],...,SEBUAH[sayam])saya1<<sayamSEBUAH ).(SEBUAH[saya],SEBUAH[saya+1],...,SEBUAH[saya+m-1])

Kami hanya melihat bahwa indeks yang diminta tidak selalu ada. Strategi kami adalah belajar ketika indeks tidak ada. Kami akan melakukan ini dengan mengandaikan kami berusaha menemukan indeks dan melihat bagaimana kesalahan pencarian kami. Maka kasus-kasus di mana pencarian tidak salah akan menyediakan algoritma untuk menemukan indeks.

4,3,2,1,0

Dengan dua indeks, kita bisa menemukan indeks berurutan. Dengan tiga indeks, kita mungkin tidak dapat menemukan dan k = j + 1 . Namun, kita dapat mempertimbangkan kasus ketika ada serangkaian tiga elemen yang benar-benar meningkat ( A [ i ] < A [ i + 1 ] < A [ i + 2 ] ) yang harus dipecahkan, karena mudah untuk mengenali proses tersebut, dan lihat bagaimana kondisi ini mungkin tidak terpenuhi. Misalkan urutan tidak memiliki peningkatan panjang menjalankan 3.j=saya+1k=j+1SEBUAH[saya]<SEBUAH[saya+1]<SEBUAH[saya+2]

4,3,2,1,2,3,2,1,0

Urutan hanya memiliki peningkatan ketat menjalankan panjang 2 (yang saya akan memanggil pasangan yang diperintahkan pendek), dipisahkan oleh penurunan jangka panjang setidaknya 2. Dalam rangka untuk menjalankan peningkatan ketat untuk menjadi bagian dari urutan 3 elemen yang meningkat, harus ada elemen sebelumnya i sehingga A [ i ] < A [ j ] atau elemen selanjutnya k sedemikian sehingga A [ j + 1 ] < A [ kSEBUAH[j]<SEBUAH[j+1]sayaSEBUAH[saya]<SEBUAH[j]k .SEBUAH[j+1]<SEBUAH[k]

4,3,2,2,5,1,5,0,5,1,0

Suatu kasus ketika atau k tidak ada adalah ketika setiap pasangan yang dipesan seluruhnya lebih rendah dari yang berikutnya. Ini belum semuanya: ketika pasangan saling terkait, kita perlu membandingkannya dengan lebih halus.sayak

3,2,1,3,5,2,5,1,5,0,5, -0,5,1,25, -0,25 3,2,1,2,5,1,5,0,5,2,1,0

Paling kiri elemen dari subsequence meningkat perlu datang lebih awal dan menjadi kecil. Elemen j berikutnya harus lebih besar, tetapi sekecil mungkin untuk dapat menemukan elemen ketiga yang lebih besar k . Elemen pertama i tidak selalu elemen terkecil dalam urutan, dan itu tidak selalu yang pertama ada elemen berikutnya yang lebih besar, baik - kadang-kadang ada lebih rendah 2 elemen berikutnya, dan kadang-kadang ada yang lebih baik cocok untuk minimum yang sudah ditemukan.sayajksaya

2.1,3,2,1,2,5,1,5,0,5,2,1,0 1,2,0,2,5,1,5,0,5

saya(saya,j)ksaya(saya,j)(saya,j)saya>jSEBUAH[saya]<SEBUAH[saya]sayasaya(saya,j)jSEBUAH[j]<SEBUAH[j](saya,j)

Pernyataan algoritma

Diberikan dalam sintaksis Python, tetapi berhati-hatilah karena saya belum mengujinya.

def subsequence3(A):
    """Return the indices of a subsequence of length 3, or None if there is none."""
    index1 = None; value1 = None
    index2 = None; value2 = None
    for i in range(0,len(A)):
        if index1 == None or A[i] < value1:
            index1 = i; value1 = A[i]
        else if A[i] == value1: pass
        else if index2 == None:
            index2 = (index1, i); value2 = (value1, A[i])
        else if A[i] < value2[1]:
            index2[1] = i; value2[1] = A[i]
        else if A[i] > value2[1]:
            return (index2[0], index2[1], i)
    return None

Sketsa bukti

index1adalah indeks minimum bagian array yang telah dilalui (jika terjadi beberapa kali, kami mempertahankan kejadian pertama), atau Nonesebelum memproses elemen pertama. index2menyimpan indeks peningkatan urutan panjang 2 di bagian array yang sudah dilintasi yang memiliki elemen terbesar terendah, atau Nonejika urutan seperti itu tidak ada.

Ketika return (index2[0], index2[1], i)berjalan, kami memiliki value2[0] < value[1](ini adalah invarian dari value2) dan value[1] < A[i](jelas dari konteksnya). Jika loop berakhir tanpa menerapkan pengembalian awal, baik value1 == None, dalam hal ini tidak ada penambahan panjang 2 panjang apalagi 3, atau value1berisi peningkatan panjang 2 panjang yang memiliki elemen terbesar terendah. Dalam kasus terakhir, kami selanjutnya memiliki invarian bahwa tidak ada peningkatan setelah panjang 3 berakhir lebih awal dari value1; Oleh karena itu elemen terakhir dari setiap urutan tersebut, ditambahkan ke value2, akan membentuk urutan berikutnya yang meningkat dari panjang 3: karena kami juga memiliki invarian yang value2bukan bagian dari peningkatan berikutnya dari panjang 3 yang terkandung dalam bagian array yang sudah dilalui, ada tidak ada urutan seperti itu di seluruh array.

Membuktikan invarian yang disebutkan di atas dibiarkan sebagai latihan untuk pembaca.

Kompleksitas

HAI(1)HAI(1)HAI(n)

Bukti formal

Ditinggalkan sebagai latihan untuk pembaca.

Gilles 'SANGAT berhenti menjadi jahat'
sumber
8

HAI(n)HAI(n)

Pertama, lintasi larik kiri ke kanan dengan mempertahankan tumpukan dan larik bantu yang memberi tahu Anda untuk setiap elemen, indeks elemen yang lebih besar darinya dan di sebelah kanannya.

-1

Setiap kali Anda mempertimbangkan elemen baru dalam array, jika elemen itu lebih besar dari elemen atas stack, Anda melepaskannya dari stack, dan mengatur elemen array aux yang sesuai dengan bagian atas untuk membuat indeks elemen baru di bawah pertimbangan.

Lanjutkan mengeluarkan elemen dari tumpukan dan mengatur indeks yang sesuai, sementara elemen saat ini lebih besar. Setelah bagian atas memiliki elemen yang tidak lebih kecil (atau menjadi kosong), dorong elemen saat ini ke tumpukan, dan lanjutkan ke elemen berikutnya dari array, ulangi langkah di atas.

Buat pass lain (dan array aux lain), tetapi ke kanan ke kiri.

-1

HAI(n)

k-saya

Kode palsu untuk pass pertama mungkin terlihat seperti ini:

Stack <Pair<Elem, Index>> greats;
Elem auxArr[inputArr.Length];

for (Index i = 0; i < inputArr.Length; i++) {

    while (!greats.IsEmpty() && inputArr[i] > greats.PeekTop().Elem) {
        Pair top = greats.Pop();
        auxArr[top.Index] = i;
    }

    Pair p;
    p.Elem = inputArr[i];
    p.Index = i;

    greats.Push(p);
}
Aryabhata
sumber
“Karena kamu menganggap setiap elemen dari array hanya sejumlah kali yang konstan, ini adalah waktu O (n).” Oh, kumuh. Entah bagaimana saya telah mengesampingkan beberapa lintasan konstan, membuangnya sebagai bukan O (n). Sangat bodoh. Saya berterima kasih atas penjelasan Anda, dan saya akan berusaha sekali lagi untuk menyelesaikannya.
Christopher Done