Saya memiliki pertanyaan ini pada tes Algoritma kemarin, dan saya tidak tahu jawabannya. Itu membuatku benar-benar gila, karena nilainya sekitar 40 poin. Saya pikir sebagian besar kelas tidak menyelesaikannya dengan benar, karena saya belum menemukan solusi dalam 24 jam terakhir.
Diberikan string biner sembarang dengan panjang n, temukan tiga yang berjarak secara merata dalam string jika ada. Tulis algoritma yang memecahkan ini dalam waktu O (n * log (n)).
Jadi string seperti ini memiliki tiga yang "merata": 11100000, 0100100100
sunting: Ini adalah nomor acak, jadi itu harus bisa berfungsi untuk nomor apa pun. Contoh yang saya berikan adalah untuk menggambarkan properti "berjarak sama". Jadi 1001011 adalah angka yang valid. Dengan 1, 4, dan 7 menjadi yang berjarak sama.
Jawaban:
Akhirnya! Menindaklanjuti petunjuk dalam jawaban sdcvvc , kami memilikinya: algoritma O (n log n) untuk masalahnya! Sederhana juga, setelah Anda memahaminya. Mereka yang menebak FFT benar.
Masalahnya: kita diberi string biner dengan
S
panjang n , dan kami ingin menemukan tiga 1 spasi secara merata di dalamnya. Misalnya,S
mungkin110110010
, di mana n = 9. Ini telah merata spasi 1s di posisi 2, 5, dan 8.Pindai
S
kiri ke kanan, dan buat daftarL
posisi 1. Untuk yang diS=110110010
atas, kita memiliki daftar L = [1, 2, 4, 5, 8]. Langkah ini adalah O (n). Masalahnya adalah sekarang untuk menemukan deret aritmetika panjang 3 diL
, yaitu untuk menemukan yang berbeda a, b, c diL
sehingga ba = cb , atau ekuivalen a + c = 2b . Untuk contoh di atas, kami ingin menemukan progresi (2, 5, 8).Membuat polinomial
p
dengan istilah x k untuk setiap k diL
. Untuk contoh di atas, kita membuat polinomial p (x) = (x + x 2 + x 4 + x 5 + x 8 ) . Langkah ini adalah O (n).Temukan polinomial
q
= p 2 , menggunakan Fast Fourier Transform . Untuk contoh di atas, kita mendapatkan polinomial q (x) = x 16 + 2x 13 + 2x 12 + 3x 10 + 4x 9 + x 8 + 2x 7 + 4x 6 + 2x 5 + x 4 + 2x 3 + x 2 . Langkah ini adalah O (n log n).Abaikan semua istilah kecuali yang terkait dengan x 2k untuk beberapa k in
L
. Untuk contoh di atas, kita mendapatkan istilah x 16 , 3x 10 , x 8 , x 4 , x 2 . Langkah ini O (n), jika Anda memilih untuk melakukannya sama sekali.Berikut titik penting: koefisien setiap x 2b untuk b di
L
adalah tepat jumlah pasangan (a, c) dalamL
sehingga a + c = 2b . [CLRS, Kel. 30.1-7] Satu pasangan seperti itu adalah (b, b) selalu (jadi koefisiennya paling sedikit 1), tetapi jika ada pasangan lain (a, c) , maka koefisiennya setidaknya 3, dari (a, c ) dan (c, a) . Untuk contoh di atas, kita memiliki koefisien x 10 menjadi 3 justru karena AP (2,5,8). (Koefisien ini x 2bakan selalu menjadi angka ganjil, karena alasan di atas. Dan semua koefisien lain dalam q akan selalu genap.)Jadi, algoritmanya adalah untuk melihat koefisien dari istilah-istilah ini x 2b , dan melihat apakah ada di antara mereka yang lebih besar dari 1. Jika tidak ada, maka tidak ada spasi 1s yang merata. Jika ada adalah sebuah b di
L
mana koefisien x 2b lebih besar dari 1, maka kita tahu bahwa ada beberapa pasangan (a, c) - selain (b, b) - untuk yang a + c = 2b . Untuk menemukan pasangan yang sebenarnya, kita cukup mencoba masing - masing a dalamL
( c yang sesuai adalah 2b-a ) dan melihat apakah ada 1 pada posisi 2b-a inS
. Langkah ini adalah O (n).Itu saja, semuanya.
Orang mungkin bertanya: apakah kita perlu menggunakan FFT? Banyak jawaban, seperti beta , flybywire , dan rsp , menyarankan bahwa pendekatan yang memeriksa setiap pasangan 1s dan melihat apakah ada 1 pada posisi "ketiga", mungkin bekerja di O (n log n), berdasarkan intuisi bahwa jika ada terlalu banyak 1, kita akan menemukan triple dengan mudah, dan jika ada terlalu sedikit 1, memeriksa semua pasangan membutuhkan sedikit waktu. Sayangnya, sementara intuisi ini benar dan pendekatan sederhana adalah lebih baik dari O (n 2 ), tidak secara signifikan lebih baik. Seperti dalam jawaban sdcvvc , kita dapat mengambil "set Cantor-like" dari string dengan panjang n = 3 k, dengan 1s pada posisi yang representasi ternernya hanya memiliki 0s dan 2s (no 1s) di dalamnya. String semacam itu memiliki 2 k = n (log 2) / (log 3) ≈ n 0,63 di dalamnya dan tidak ada spasi 1s secara merata, jadi memeriksa semua pasangan adalah urutan kuadrat dari jumlah 1s di dalamnya: itu 4 k ≈ n 1.26 yang sayangnya secara asimptot jauh lebih besar dari (n log n). Faktanya, kasus terburuk bahkan lebih buruk: Leo Moser pada tahun 1953 membangun (secara efektif) string seperti itu yang memiliki n 1-c / √ (log n) 1s di dalamnya tetapi tidak ada spasi 1s secara merata, yang berarti bahwa pada string seperti itu, sederhana pendekatan akan mengambil Θ (n 2-2c / √ (log n) )- hanya kecil sedikit lebih baik dari Θ (n 2 ) , mengejutkan!
Tentang jumlah maksimum 1s dalam string dengan panjang n tanpa 3 yang berjarak secara merata (yang kita lihat di atas setidaknya n 0,63 dari konstruksi yang mudah digunakan seperti Cantor, dan setidaknya n 1-c / √ (log n) dengan Konstruksi Moser) - ini adalah OEIS A003002 . Dapat juga dihitung langsung dari OEIS A065825 sebagai k sedemikian sehingga A065825 (k) ≤ n <A065825 (k + 1). Saya menulis sebuah program untuk menemukan ini, dan ternyata algoritma serakah tidak memberikan string terpanjang seperti itu. Misalnya, untuk n = 9, kita bisa mendapatkan 5 1s (110100011) tetapi serakah hanya memberikan 4 (110110000), untuk n= 26, kita bisa mendapatkan 11 1s (110010100010000000000000000000010101.010) tetapi yang serakah hanya memberi 8 (110110000110110000000000000000000000), dan untuk n = 74 kita bisa mendapatkan 221s. Mereka setuju di beberapa tempat sampai 50 (misalnya semua 38 hingga 50), meskipun. Seperti yang dikatakan referensi OEIS, tampaknya Jaroslaw Wroblewski tertarik dengan pertanyaan ini, dan ia mengelola sebuah situs web di perangkat non-rata-rata ini . Jumlah pastinya hanya diketahui hingga 194.
sumber
Masalah Anda disebut AVERAGE dalam makalah ini (1999):
Wikipedia :
Ini cukup untuk menyelesaikan masalah Anda :).
Yang sangat penting adalah bahwa O (n log n) adalah kompleksitas dalam hal jumlah nol dan satu, bukan jumlah yang (yang dapat diberikan sebagai array, seperti [1,5,9,15]). Memeriksa apakah suatu himpunan memiliki perkembangan aritmatika, ketentuan angka 1, sulit, dan menurut makalah itu pada tahun 1999 tidak ada algoritma yang lebih cepat daripada O (n 2 ) yang diketahui, dan diduga bahwa itu tidak ada. Semua orang yang tidak memperhitungkan ini berusaha memecahkan masalah terbuka.
Info menarik lainnya, kebanyakan tidak menarik:
Batas bawah:
Batas bawah yang mudah adalah set Cantor-like (angka 1..3 ^ n-1 tidak mengandung 1 dalam ekspansi terner mereka) - densitasnya adalah n ^ (log_3 2) (sekitar 0,631). Jadi setiap pengecekan jika set tidak terlalu besar, dan kemudian memeriksa semua pasangan tidak cukup untuk mendapatkan O (n log n). Anda harus menyelidiki urutan lebih pintar. Batas bawah yang lebih baik dikutip di sini - ini n 1-c / (log (n)) ^ (1/2) . Ini berarti set Cantor tidak optimal.
Batas atas - algoritme lama saya:
Diketahui bahwa untuk n besar, subset dari {1,2, ..., n} tidak mengandung progresi aritmatika memiliki paling banyak elemen n / (log n) ^ (1/20). Makalah Pada tiga kali lipat dalam perkembangan aritmatika membuktikan lebih: set tidak dapat mengandung lebih dari n * 2 28 * (log log n / log n) 1/2 elemen. Jadi Anda bisa memeriksa apakah ikatan itu tercapai dan jika tidak, periksa pasangan secara naif. Ini adalah algoritma O (n 2 * log log n / log n), lebih cepat dari O (n 2 ). Sayangnya "Di triples ..." ada di Springer - tetapi halaman pertama tersedia, dan eksposisi Ben Green tersedia di sini , halaman 28, teorema 24.
Ngomong-ngomong, makalahnya berasal dari tahun 1999 - tahun yang sama dengan yang pertama saya sebutkan, jadi mungkin itu sebabnya yang pertama tidak menyebutkan hasil itu.
sumber
Ini bukan solusi, tetapi garis pemikiran yang serupa dengan apa yang dipikirkan Olexiy
Saya bermain-main dengan membuat urutan dengan jumlah maksimum, dan semuanya cukup menarik, saya mendapatkan hingga 125 digit dan di sini adalah 3 angka pertama yang ditemukan dengan mencoba memasukkan bit '1' sebanyak mungkin:
Perhatikan mereka semua fraktal (tidak terlalu mengejutkan mengingat kendala). Mungkin ada sesuatu dalam berpikir mundur, mungkin jika string bukan fraktal dengan karakteristik, maka harus memiliki pola yang berulang?
Berkat beta untuk istilah yang lebih baik untuk menggambarkan angka-angka ini.
Pembaruan: Sayangnya sepertinya pola rusak ketika memulai dengan string awal yang cukup besar, seperti: 10000000000001:
sumber
Saya menduga bahwa pendekatan sederhana yang terlihat seperti O (n ^ 2) sebenarnya akan menghasilkan sesuatu yang lebih baik, seperti O (n ln (n)). Urutan yang membutuhkan waktu paling lama untuk diuji (untuk setiap n yang diberikan) adalah yang tidak mengandung trio, dan yang memberikan batasan ketat pada jumlah 1 yang dapat berada dalam urutan.
Saya telah mengajukan beberapa argumen yang melambaikan tangan, tetapi saya belum dapat menemukan bukti yang rapi. Saya akan mengambil bacokan dalam kegelapan: jawabannya adalah ide yang sangat pintar bahwa profesor telah tahu begitu lama sehingga tampak jelas, tetapi itu terlalu sulit bagi para siswa. (Entah itu atau Anda tidur melalui ceramah yang membahasnya.)
sumber
Revisi: 2009-10-17 23:00
Saya sudah menjalankan ini pada sejumlah besar (seperti, string 20 juta) dan saya sekarang percaya algoritma ini bukan O (n logn). Meskipun demikian, ini adalah implementasi yang cukup keren dan berisi sejumlah optimasi yang membuatnya berjalan sangat cepat. Ini mengevaluasi semua pengaturan string biner 24 atau lebih sedikit digit dalam waktu kurang dari 25 detik.
Saya telah memperbarui kode untuk memasukkan
0 <= L < M < U <= X-1
pengamatan dari hari ini sebelumnya.Asli
Ini, dalam konsepnya, mirip dengan pertanyaan lain yang saya jawab . Kode itu juga melihat tiga nilai dalam satu seri dan menentukan apakah triplet memenuhi suatu syarat. Berikut adalah kode C # yang diadaptasi dari itu:
Perbedaan utama adalah:
Kode ini menghasilkan kumpulan data untuk menemukan input tersulit untuk dipecahkan untuk algoritma ini.
Kode untuk pertanyaan sebelumnya menghasilkan semua solusi menggunakan generator python. Kode ini hanya menampilkan yang paling sulit untuk setiap panjang pola.
Kode ini memeriksa jarak dari elemen tengah ke sisi kiri dan kanannya. Kode python menguji apakah jumlah di atas atau di bawah 0.
Kode saat ini bekerja dari tengah menuju tepi untuk menemukan seorang kandidat. Kode dalam masalah sebelumnya bekerja dari tepi ke tengah. Perubahan terakhir ini memberikan peningkatan kinerja yang besar.
Berdasarkan pengamatan pada akhir artikel ini, kode mencari pasangan genap angka genap untuk menemukan L dan U, menjaga M tetap. Ini mengurangi jumlah pencarian dengan informasi pra-komputasi. Dengan demikian, kode ini menggunakan dua tingkat tipuan dalam loop utama FindCandidate dan membutuhkan dua panggilan ke FindCandidate untuk setiap elemen tengah: sekali untuk angka genap dan sekali untuk yang aneh.
Gagasan umum adalah bekerja pada indeks, bukan representasi data mentah. Menghitung larik di mana 1 muncul memungkinkan algoritma untuk berjalan dalam waktu yang proporsional dengan jumlah 1 dalam data daripada dalam waktu yang proporsional dengan panjang data. Ini adalah transformasi standar: buat struktur data yang memungkinkan operasi lebih cepat sambil menjaga masalah tetap sama.
Hasilnya kedaluwarsa: dihapus.
Sunting: 2009-10-16 18:48
Pada data yx, yang diberi kepercayaan pada respons lain sebagai perwakilan dari data sulit untuk dihitung, saya mendapatkan hasil ini ... Saya menghapus ini. Mereka ketinggalan zaman.
Saya akan menunjukkan bahwa data ini bukan yang paling sulit untuk algoritma saya, jadi saya pikir asumsi bahwa fraktal yx adalah yang paling sulit untuk dipecahkan adalah salah. Kasus terburuk untuk algoritma tertentu, saya harapkan, akan tergantung pada algoritma itu sendiri dan kemungkinan tidak akan konsisten di berbagai algoritma.
Sunting: 2009-10-17 13:30
Pengamatan lebih lanjut tentang ini.
Pertama, konversikan string 0 dan 1 menjadi array indeks untuk setiap posisi 1. Katakan panjang array itu adalah X. Lalu tujuannya adalah menemukan
seperti yang
atau
Karena A [L] dan A [U] jumlah ke bilangan genap, mereka tidak bisa (genap, ganjil) atau (ganjil, genap). Pencarian untuk pertandingan dapat ditingkatkan dengan memecah A [] menjadi kelompok yang genap dan genap dan mencari pasangan pada kelompok [M] di kelompok calon yang genap dan genap.
Namun, ini lebih merupakan optimasi kinerja daripada peningkatan algoritmik, saya pikir. Jumlah perbandingan harus turun, tetapi urutan algoritme harus sama.
Sunting 2009-10-18 00:45
Namun optimasi lain terjadi pada saya, dengan cara yang sama seperti memisahkan kandidat menjadi genap dan ganjil. Karena ketiga indeks harus ditambahkan ke kelipatan 3 (a, a + x, a + 2x - mod 3 adalah 0, terlepas dari a dan x), Anda dapat memisahkan L, M, dan U ke dalam nilai mod 3 mereka. :
Bahkan, Anda bisa menggabungkan ini dengan pengamatan even / odd dan memisahkannya ke dalam nilai mod 6 mereka:
dan seterusnya. Ini akan memberikan optimasi kinerja lebih lanjut tetapi bukan peningkatan algoritmik.
sumber
Belum dapat menemukan solusi :(, tetapi punya beberapa ide.
Bagaimana jika kita mulai dari masalah terbalik: membuat urutan dengan jumlah maksimum 1s dan TANPA trio yang berjarak sama rata. Jika Anda dapat membuktikan jumlah maksimum 1s adalah o (n), maka Anda dapat meningkatkan perkiraan Anda dengan mengulangi hanya melalui daftar 1s saja.
sumber
Ini dapat membantu ....
Masalah ini berkurang menjadi sebagai berikut:
Sebagai contoh, diberi urutan
[ 3, 5, 1, 3, 6, 5, 2, 2, 3, 5, 6, 4 ]
, kita akan menemukan urutan[ 3, 6, 5, 2, 2]
dengan awalan[ 3, 6 ]
dengan jumlah awalan9
dan akhiran[ 5, 2, 2 ]
dengan jumlah akhiran dari9
.Pengurangannya adalah sebagai berikut:
Misalnya, diberi urutan
[ 0, 1, 1, 0, 0, 1, 0, 0, 0, 1 0 ]
, kita akan menemukan pengurangan[ 1, 3, 4]
. Dari reduksi ini, kami menghitung urutan yang berdekatan[ 1, 3, 4]
, awalan[ 1, 3]
dengan jumlah4
, dan akhiran[ 4 ]
dengan jumlah4
.Pengurangan ini dapat dihitung dalam
O(n)
.Sayangnya, saya tidak yakin ke mana harus pergi dari sini.
sumber
Untuk jenis masalah sederhana (yaitu Anda mencari tiga "1" dengan hanya (yaitu nol atau lebih) "0" di antara itu), Ini cukup sederhana: Anda bisa membagi urutan di setiap "1" dan mencari dua urutan yang berdekatan setelah panjang yang sama (tentu saja urutan kedua bukan yang terakhir). Jelas, ini bisa dilakukan dalam O (n) waktu.
Untuk versi yang lebih kompleks (yaitu Anda mencari indeks i dan celah g > 0 sedemikian rupa
s[i]==s[i+g]==s[i+2*g]=="1"
), saya tidak yakin, jika ada solusi O (n log n) , karena mungkin ada O (n²) kembar tiga yang memiliki properti ini (pikirkan string semua yang ada, ada sekitar n² / 2 kembar tiga seperti itu). Tentu saja, Anda hanya mencari satu di antaranya, tetapi saat ini saya tidak tahu, bagaimana cara menemukannya ...sumber
Pertanyaan yang menyenangkan, tetapi begitu Anda menyadari bahwa pola aktual antara dua '1 tidak masalah, algoritme menjadi:
Dalam kode, JTest fashion, (Perhatikan kode ini tidak ditulis untuk menjadi paling efisien dan saya menambahkan beberapa println untuk melihat apa yang terjadi.)
sumber
Saya memikirkan pendekatan membagi dan menaklukkan yang mungkin berhasil.
Pertama, dalam preprocessing Anda harus memasukkan semua angka kurang dari setengah ukuran input Anda ( n / 3) ke dalam daftar.
Diberikan string:
0000010101000100
(perhatikan bahwa contoh khusus ini valid)Masukkan semua bilangan prima (dan 1) dari 1 hingga (16/2) ke dalam daftar: {1, 2, 3, 4, 5, 6, 7}
Kemudian bagi menjadi dua:
100000101 01000100
Terus lakukan ini sampai Anda mendapatkan string ukuran 1. Untuk semua string ukuran-satu dengan 1 di dalamnya, tambahkan indeks string ke daftar kemungkinan; jika tidak, kembalikan -1 untuk kegagalan.
Anda juga harus mengembalikan daftar jarak spasi yang masih mungkin, terkait dengan setiap indeks awal. (Mulailah dengan daftar yang Anda buat di atas dan hapus angka saat Anda pergi) Di sini, daftar kosong berarti Anda hanya berurusan dengan satu 1 dan sehingga spasi apa pun mungkin pada saat ini; kalau tidak, daftar mencakup jarak yang harus dikesampingkan.
Jadi lanjutkan dengan contoh di atas:
1000 0101 0100 0100
10 00 01 01 01 00 01 00
1 0 0 0 0 1 0 1 0 1 0 0 0 1 0 0
Pada langkah menggabungkan pertama, kami memiliki delapan set dua sekarang. Pada yang pertama, kita memiliki kemungkinan satu set, tetapi kita belajar bahwa spasi dengan 1 tidak mungkin karena nol lainnya ada di sana. Jadi kita mengembalikan 0 (untuk indeks) dan {2,3,4,5,7} untuk fakta bahwa spasi dengan 1 tidak mungkin. Yang kedua, kita tidak punya apa-apa dan jadi -1. Yang ketiga kami memiliki kecocokan tanpa spasi dihilangkan dalam indeks 5, jadi kembalikan 5, {1,2,3,4,5,7}. Pada pasangan keempat kita kembalikan 7, {1,2,3,4,5,7}. Di urutan kelima, kembalikan 9, {1,2,3,4,5,7}. Di keenam, kembali -1. Di ketujuh, kembalikan 13, {1,2,3,4,5,7}. Di kedelapan, kembalikan -1.
Menggabungkan lagi menjadi empat set empat, kami memiliki:
1000
: Return (0, {4,5,6,7})0101
: Return (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6 , 7})0100
: Kembali (9, {3,4,5,6,7})0100
: Kembali (13, {3,4,5,6,7})Menggabungkan ke dalam set delapan:
10000101
: Return (0, {5,7}), (5, {2,3,4,5,6,7}), (7, {1,2,3,4,5,6,7})01000100
: Return (9, {4,7}), (13, {3,4,5,6,7})Menggabungkan ke dalam satu set enam belas:
10000101 01000100
Seiring kemajuan kami, kami terus memeriksa semua kemungkinan sejauh ini. Hingga langkah ini kami telah meninggalkan hal-hal yang melampaui akhir dari string, tetapi sekarang kami dapat memeriksa semua kemungkinan.
Pada dasarnya, kami memeriksa 1 pertama dengan jarak 5 dan 7, dan menemukan bahwa mereka tidak berbaris ke 1. (Perhatikan bahwa setiap cek adalah KONSTAN, bukan waktu linier) Kemudian kita periksa yang kedua (indeks 5) dengan jarak 2, 3, 4, 5, 6, dan 7-- atau kita mau, tetapi kita bisa berhenti pada 2 karena yang benar-benar cocok.
Fiuh! Itu algoritma yang agak panjang.
Saya tidak tahu 100% apakah itu O (n log n) karena langkah terakhir, tetapi semua yang ada pasti O (n log n) sejauh yang saya tahu. Saya akan kembali ke sini nanti dan mencoba untuk memperbaiki langkah terakhir.
EDIT: Mengubah jawaban saya untuk mencerminkan komentar Welbog. Maaf atas kesalahannya. Saya akan menulis beberapa pseudocode nanti, ketika saya mendapat lebih banyak waktu untuk menguraikan apa yang saya tulis lagi. ;-)
sumber
100010001
? Jika saya memahami pendekatan Anda dengan benar, itu tidak akan dapat mencocokkannya karena jawaban yang benar(0,{4})
tidak mungkin dihitung. Mengingat bahwa Anda memerlukan non-primes dalam daftar Anda, mudah untuk menghasilkan string patologis yang mengembang daftar kemungkinan yang perlu Anda periksa ke lebih tinggi dari O (n log (n)), saya pikir.Saya akan memberikan tebakan kasar saya di sini, dan biarkan mereka yang lebih baik dengan kompleksitas menghitung untuk membantu saya tentang bagaimana harga algoritma saya dalam O-notasi bijaksana
Saya tidak tahu bagaimana cara menghitung kompleksitas untuk ini, adakah yang bisa membantu?
edit: tambahkan beberapa kode untuk menggambarkan ide saya
edit2: mencoba mengkompilasi kode saya dan menemukan beberapa kesalahan besar, diperbaiki
sumber
Saya datang dengan sesuatu seperti ini:
Ini terinspirasi oleh andycjw.
Mengenai kerumitan ini mungkin O (nlogn) seperti pada setiap rekursi kita membaginya dengan dua.
Semoga ini bisa membantu.
sumber
Oke, saya akan coba lagi masalah ini. Saya pikir saya bisa membuktikan algoritma O (n log (n)) yang mirip dengan yang sudah dibahas dengan menggunakan pohon biner seimbang untuk menyimpan jarak antara 1's. Pendekatan ini terinspirasi oleh pengamatan Hakim tentang mengurangi masalah menjadi daftar jarak antara 1 itu.
Bisakah kita memindai string input untuk membangun pohon biner seimbang di sekitar posisi 1 sehingga setiap node menyimpan posisi 1 dan setiap tepi diberi label dengan jarak ke 1 yang berdekatan untuk setiap simpul anak. Sebagai contoh:
Ini dapat dilakukan dalam O (n log (n)) karena, untuk string berukuran n, setiap penyisipan mengambil O (log (n)) dalam kasus terburuk.
Maka masalahnya adalah untuk mencari pohon untuk menemukan apakah, pada simpul mana pun, ada jalur dari simpul itu melalui anak kiri yang memiliki jarak yang sama dengan jalur melalui anak kanan. Ini dapat dilakukan secara rekursif pada setiap subtree. Saat menggabungkan dua subtree dalam pencarian, kita harus membandingkan jarak dari jalur di subtree kiri dengan jarak dari jalur di kanan. Karena jumlah jalur dalam subtree akan sebanding dengan log (n), dan jumlah node adalah n, saya percaya ini dapat dilakukan dalam waktu O (n log (n)) waktu.
Apakah saya melewatkan sesuatu?
sumber
Ini sepertinya masalah yang menyenangkan jadi saya memutuskan untuk mencoba.
Saya membuat asumsi bahwa 111000001 akan menemukan 3 yang pertama dan menjadi sukses. Pada dasarnya jumlah nol yang mengikuti angka 1 adalah hal yang penting, karena 0111000 sama dengan 111000 menurut definisi Anda. Setelah Anda menemukan dua kasus 1, 1 berikutnya ditemukan menyelesaikan trilogi.
Ini dia dengan Python:
Ini adalah percobaan pertama, jadi saya yakin ini bisa ditulis dengan cara yang lebih bersih. Harap sebutkan kasus di mana metode ini gagal di bawah.
sumber
Saya berasumsi alasan ini nlog (n) disebabkan oleh hal berikut:
Jadi, Anda memiliki n, log (n), dan 1 ... O (nlogn)
Sunting: Ups, salahku. Otak saya telah menetapkan bahwa n / 2 adalah logn ... yang jelas tidak (menggandakan nomor pada item masih menggandakan jumlah iterasi pada loop dalam). Ini masih di n ^ 2, tidak menyelesaikan masalah. Yah, setidaknya saya harus menulis beberapa kode :)
Implementasi di Tcl
sumber
Saya pikir saya telah menemukan cara untuk menyelesaikan masalah, tetapi saya tidak dapat membuat bukti formal. Solusi yang saya buat ditulis dalam Java, dan menggunakan penghitung 'n' untuk menghitung berapa banyak daftar / array yang diaksesnya. Jadi n harus kurang dari atau sama dengan stringLength * log (stringLength) jika sudah benar. Saya mencobanya untuk angka 0 hingga 2 ^ 22, dan berhasil.
Dimulai dengan mengulangi string input dan membuat daftar semua indeks yang menyimpan satu. Ini hanya O (n).
Kemudian dari daftar indeks itu memilih INDEX pertama, dan INDEX kedua yang lebih besar dari yang pertama. Kedua indeks ini harus memiliki satu, karena mereka berada dalam daftar indeks. Dari sanaIndex ketiga dapat dihitung. Jika inputString [thirdIndex] adalah 1 maka terhenti.
}
catatan tambahan: penghitung n tidak bertambah ketika iterasi atas string input untuk membangun daftar indeks. Operasi ini O (n), jadi tidak akan berpengaruh pada kompleksitas algoritma.
sumber
O(n^2)
algoritma.Salah satu jalan masuk ke masalah adalah memikirkan faktor dan bergeser.
Dengan menggeser, Anda membandingkan string yang dan nol dengan versi bergeser itu sendiri. Anda kemudian mengambil yang cocok. Ambil contoh ini bergeser dua:
1 yang dihasilkan (bitwise ANDed), harus mewakili semua 1 yang ditempatkan secara merata oleh dua. Contoh yang sama digeser oleh tiga:
Dalam hal ini tidak ada 1 yang secara merata berjarak tiga terpisah.
Jadi, apa artinya ini? Nah, Anda hanya perlu menguji shift yang merupakan bilangan prima. Misalnya katakan Anda memiliki dua 1 yang enam terpisah. Anda hanya perlu menguji shift 'dua' dan shift 'tiga' (karena ini membagi enam). Sebagai contoh:
Jadi, satu-satunya shift yang perlu Anda periksa adalah 2,3,5,7,11,13, dll. Hingga yang paling dekat dengan akar kuadrat dari ukuran string angka.
Hampir dipecahkan?
Saya pikir saya lebih dekat ke solusi. Pada dasarnya:
Saya pikir petunjuk terbesar untuk jawabannya, adalah bahwa algoritma pengurutan tercepat, adalah O (n * log (n)).
SALAH
Langkah 1 salah seperti yang ditunjukkan oleh seorang rekan. Jika kita memiliki 1 di posisi 2,12 dan 102. Kemudian mengambil modulus 10, mereka semua akan memiliki sisa yang sama, namun tidak sama-sama berjarak! Maaf.
sumber
Berikut adalah beberapa pemikiran yang, meskipun telah berusaha sebaik mungkin, tampaknya tidak akan membungkus diri mereka menjadi sebuah busur. Namun, mereka mungkin menjadi titik awal yang berguna untuk analisis seseorang.
Pertimbangkan solusi yang diusulkan sebagai berikut, yang merupakan pendekatan yang disarankan beberapa orang, termasuk saya dalam versi sebelumnya dari jawaban ini.
:)
Sekarang pertimbangkan string input string seperti berikut ini, yang tidak akan memiliki solusi:
Secara umum, ini adalah gabungan string k dari bentuk j 0 diikuti oleh 1 untuk j dari nol hingga k-1.
Perhatikan bahwa panjang substring adalah 1, 2, 3, dll. Jadi, ukuran masalah n memiliki substring panjang 1 hingga k sedemikian sehingga n = k (k + 1) / 2.
Perhatikan bahwa k juga melacak angka 1 yang harus kita pertimbangkan. Ingatlah bahwa setiap kali kita melihat angka 1, kita perlu mempertimbangkan semua angka 1 yang terlihat sejauh ini. Jadi ketika kita melihat 1 yang kedua, kita hanya mempertimbangkan yang pertama, ketika kita melihat yang ketiga, kita mempertimbangkan kembali yang pertama, ketika kita melihat yang keempat, kita perlu mempertimbangkan yang pertama, dan seterusnya. Pada akhir algoritma, kami telah mempertimbangkan k (k-1) / 2 pasang 1. Sebut itu hal.
Hubungan antara n dan p adalah n = p + k.
Proses melalui string membutuhkan O (n) waktu. Setiap kali 1 ditemukan, perbandingan maksimum (k-1) dilakukan. Karena n = k (k + 1) / 2, n> k ** 2, jadi sqrt (n)> k. Ini memberi kita O (n sqrt (n)) atau O (n ** 3/2). Namun perlu dicatat bahwa itu mungkin bukan ikatan yang benar-benar ketat, karena jumlah perbandingan berubah dari 1 hingga maksimum k, itu bukan k sepanjang waktu. Tapi saya tidak yakin bagaimana menjelaskannya dalam matematika.
Masih bukan O (n log (n)). Juga, saya tidak dapat membuktikan input tersebut adalah kasus terburuk, meskipun saya curiga mereka adalah input. Saya pikir pengemasan yang lebih padat dari 1 ke depan menghasilkan pengemasan yang lebih jarang pada akhirnya.
Karena seseorang mungkin masih menganggapnya berguna, inilah kode saya untuk solusi itu di Perl:
sumber
Saat memindai 1s, tambahkan posisinya ke Daftar. Saat menambahkan 1 detik dan berturut-turut, bandingkan mereka dengan setiap posisi dalam daftar sejauh ini. Spasi sama dengan currentOne (tengah) - priorOne (kiri). Bit sisi kanan adalah currentOne + spacing. Jika 1, akhirnya.
Daftar yang tumbuh terbalik dengan ruang di antara mereka. Sederhananya, jika Anda memiliki banyak 0s antara 1s (seperti dalam kasus terburuk), daftar 1s Anda yang dikenal akan tumbuh cukup lambat.
sumber
Saya pikir saya akan menambahkan satu komentar sebelum memposting solusi naif ke-22 untuk masalah ini. Untuk solusi naif, kita tidak perlu menunjukkan bahwa jumlah 1 dalam string paling banyak adalah O (log (n)), tetapi lebih banyak O (sqrt (n * log (n)).
Pemecah:
Ini pada dasarnya agak mirip dengan ide dan implementasi flybywire, meskipun melihat ke depan, bukan ke belakang.
Pembangun Tali Serakah:
(Dalam pembelaan saya, saya masih dalam tahap pemahaman 'belajar python')
Juga, output yang berpotensi berguna dari bangunan serakah string, ada lompatan yang agak konsisten setelah memukul kekuatan 2 dalam jumlah 1 ... yang saya tidak mau menunggu untuk menyaksikan memukul 2096.
sumber
Saya akan mencoba menyajikan pendekatan matematika. Ini lebih merupakan awal daripada akhir, sehingga bantuan, komentar, atau bahkan kontradiksi - akan sangat dihargai. Namun, jika pendekatan ini terbukti - algoritme adalah pencarian langsung di string.
Dengan jumlah ruang
k
dan string yang tetapS
, pencarian untuk k-spaced-triplet membutuhkan waktuO(n)
- Kami hanya menguji untuk setiap0<=i<=(n-2k)
ifS[i]==S[i+k]==S[i+2k]
. Tes mengambilO(1)
dan kami melakukannya din-k
manak
konstanta, sehingga dibutuhkanO(n-k)=O(n)
.Mari kita asumsikan ada Proporsi Terbalik antara jumlah
1
's dan ruang maksimum yang perlu kita cari. Artinya, Jika ada banyak1
, harus ada triplet dan itu harus cukup padat; Jika hanya ada beberapa1
, Triplet (jika ada) bisa sangat jarang. Dengan kata lain, saya dapat membuktikan bahwa jika saya memiliki cukup1
, triplet seperti itu harus ada - dan semakin banyak yang1
saya miliki, triplet yang lebih padat harus ditemukan. Ini bisa dijelaskan dengan prinsip Pigeonhole - Harapan untuk menguraikan hal ini nanti.Katakanlah ada batas atas
k
pada kemungkinan jumlah ruang yang harus saya cari. Sekarang, untuk setiap1
terletak diS[i]
kita perlu memeriksa1
diS[i-1]
danS[i+1]
,S[i-2]
danS[i+2]
, ...S[i-k]
danS[i+k]
. Ini berlakuO((k^2-k)/2)=O(k^2)
untuk masing-masing1
inS
-karena Formula Penjumlahan Seri Gauss . Perhatikan bahwa ini berbeda dari bagian 1 - Saya memilikik
sebagai batas atas untuk jumlah ruang, bukan sebagai ruang konstan.Kita perlu membuktikan
O(n*log(n))
. Artinya, kita perlu menunjukkan yangk*(number of 1's)
proporsionallog(n)
.Jika kita bisa melakukan itu, algoritma sepele - untuk setiap
1
diS
yang indeksi
, hanya mencari1
's dari setiap sisi hingga jarakk
. Jika dua ditemukan dalam jarak yang sama, kembalii
dank
. Sekali lagi, bagian yang sulit adalah menemukank
dan membuktikan kebenaran.Saya akan sangat menghargai komentar Anda di sini - Saya telah berusaha menemukan hubungan antara
k
dan jumlah1
di papan tulis saya, sejauh ini tidak berhasil.sumber
Anggapan:
Salah, berbicara tentang log (n) jumlah batas atas yang
EDIT:
Sekarang saya menemukan bahwa menggunakan nomor Cantor (jika benar), densitas pada set adalah (2/3) ^ Log_3 (n) (fungsi yang aneh) dan saya setuju, log (n) / n kepadatan adalah untuk kuat.
Jika ini adalah batas atas, ada algoritme yang memecahkan masalah ini dalam setidaknya O (n * (3/2) ^ (log (n) / log (3))) kompleksitas waktu dan O ((3/2) ^ ( log (n) / log (3))) kompleksitas ruang. (periksa jawaban Justice untuk algorhitm)
Ini masih jauh lebih baik daripada O (n ^ 2)
Fungsi ini ((3/2) ^ (log (n) / log (3))) sangat mirip dengan n * log (n) pada pandangan pertama.
Bagaimana saya mendapatkan formula ini?
Memutar nomor Cantors pada string.
Misalkan panjang string adalah 3 ^ p == n
Pada setiap langkah dalam menghasilkan string Cantor Anda menyimpan 2/3 dari jumlah yang ada sebelumnya. Terapkan ini p kali.
Itu berarti (n * ((2/3) ^ p)) -> (((3 ^ p)) * ((2/3) ^ p)) yang tersisa dan setelah penyederhanaan 2 ^ p. Ini berarti 2 ^ p dalam 3 ^ p string -> (3/2) ^ p. Pengganti p = log (n) / log (3) dan dapatkan
((3/2) ^ (log (n) / log (3)))
sumber
Bagaimana dengan solusi O (n) sederhana, dengan ruang O (n ^ 2)? (Menggunakan asumsi bahwa semua operator bitwise bekerja di O (1).)
Algoritma pada dasarnya bekerja dalam empat tahap:
Tahap 1: Untuk setiap bit di nomor asli Anda, cari tahu seberapa jauh jaraknya, tetapi pertimbangkan hanya satu arah. (Saya menganggap semua bit dalam arah bit yang paling tidak signifikan.)
Tahap 2: Membalik urutan bit dalam input;
Tahap 3: Jalankan kembali langkah 1 pada input yang terbalik.
Tahap 4: Bandingkan hasil dari Tahap 1 dan Tahap 3. Jika ada bit yang sama spasi di atas DAN di bawah ini, kita harus memiliki hit.
Ingatlah bahwa tidak ada langkah dalam algoritma di atas yang membutuhkan waktu lebih lama dari O (n). ^ _ ^
Sebagai manfaat tambahan, algoritma ini akan menemukan SEMUA yang sama spasi dari nomor SETIAP. Jadi misalnya jika Anda mendapatkan hasil "0x0005" maka ada yang sama spasi di KEDUA 1 dan 3 unit jauhnya
Saya tidak benar-benar mencoba mengoptimalkan kode di bawah ini, tetapi kode C # yang dapat dikompilasi sepertinya berfungsi.
Seseorang mungkin akan berkomentar bahwa untuk jumlah yang cukup besar, operasi bitwise tidak dapat dilakukan dalam O (1). Anda benar. Namun, saya menduga bahwa setiap solusi yang menggunakan penjumlahan, pengurangan, penggandaan, atau pembagian (yang tidak dapat dilakukan dengan bergeser) juga akan memiliki masalah itu.
sumber
Di bawah ini solusinya. Mungkin ada beberapa kesalahan kecil di sana-sini, tetapi idenya bagus.
Sunting: Ini bukan n * log (n)
KODE PSEUDO:
Kode C #:
Bagaimana itu bekerja:
sumber
Jelas kita perlu setidaknya memeriksa tandan triplet pada waktu yang bersamaan, jadi kita perlu memampatkan cek itu entah bagaimana. Saya memiliki algoritma kandidat, tetapi menganalisis kompleksitas waktu berada di luar batas kemampuan * waktu saya.
Bangun sebuah pohon di mana setiap simpul memiliki tiga anak dan setiap simpul berisi jumlah total 1 pada daunnya. Bangun daftar tertaut di atas angka 1 juga. Tetapkan setiap node biaya yang diizinkan sebanding dengan kisaran yang dicakupnya. Selama waktu yang kami habiskan di setiap node sesuai anggaran, kami akan memiliki algoritma O (n lg n).
-
Mulai dari root. Jika kuadrat dari jumlah total 1 di bawahnya kurang dari biaya yang diizinkan, terapkan algoritma naif. Kalau tidak kambuh pada anak-anaknya.
Sekarang kami telah kembali sesuai anggaran, atau kami tahu bahwa tidak ada kembar tiga yang sah yang sepenuhnya terkandung dalam salah satu anak. Karena itu kita harus memeriksa triplet antar-simpul.
Sekarang segalanya menjadi sangat berantakan. Kami pada dasarnya ingin mengulang pada potensi set anak sambil membatasi jangkauan. Segera setelah rentang dibatasi sehingga algoritma naif akan berjalan di bawah anggaran, Anda melakukannya. Nikmati menerapkan ini, karena saya jamin itu akan membosankan. Ada selusin kasus.
-
Alasan saya berpikir bahwa algoritma akan bekerja adalah karena urutan tanpa kembar tiga yang valid tampaknya berganti-ganti antara tandan 1 dan banyak 0. Ini secara efektif membagi ruang pencarian terdekat, dan pohon mengemulasi pemisahan itu.
Waktu menjalankan algoritma tidak jelas sama sekali. Itu bergantung pada sifat non-sepele dari urutan. Jika 1 benar-benar jarang maka algoritma naif akan bekerja sesuai anggaran. Jika angka 1 padat, maka kecocokan harus segera ditemukan. Tetapi jika kerapatan 'tepat' (mis. Dekat ~ n ^ 0,63, yang dapat Anda capai dengan mengatur semua bit pada posisi tanpa angka '2' pada basis 3), saya tidak tahu apakah ini akan bekerja. Anda harus membuktikan bahwa efek pemisahan cukup kuat.
sumber
Tidak ada jawaban teoritis di sini, tetapi saya menulis program Java cepat untuk mengeksplorasi perilaku running-time sebagai fungsi dari k dan n, di mana n adalah total panjang bit dan k adalah jumlah 1. Saya dengan beberapa penjawab yang mengatakan bahwa algoritma "biasa" yang memeriksa semua pasangan posisi bit dan mencari bit ke-3, meskipun itu akan membutuhkan O (k ^ 2) dalam kasus terburuk, di kenyataan karena kasus terburuk membutuhkan bitstring yang jarang, adalah O (n ln n).
Pokoknya inilah programnya, di bawah ini. Ini adalah program gaya Monte-Carlo yang menjalankan sejumlah besar percobaan NTRIALS untuk n konstan, dan secara acak menghasilkan bitet untuk rentang nilai-k menggunakan proses Bernoulli dengan kepadatan yang dibatasi antara batas yang dapat ditentukan, dan mencatat waktu berjalan menemukan atau gagal menemukan triplet yang jaraknya sama, waktu diukur dalam langkah-langkah TIDAK dalam waktu CPU. Saya menjalankannya untuk n = 64, 256, 1024, 4096, 16384 * (masih berjalan), pertama uji coba dengan 500000 uji coba untuk melihat nilai k mana yang mengambil waktu berjalan paling lama, kemudian tes lain dengan uji 50.000.000 dengan uji menyempit- kepadatan fokus untuk melihat seperti apa nilai-nilai itu. Waktu berjalan terpanjang bisa terjadi dengan kepadatan sangat jarang (misalnya untuk n = 4096 puncak waktu berjalan berada dalam kisaran k = 16-64, dengan puncak lembut untuk runtime rata-rata pada 4212 langkah @ k = 31, runtime maksimum memuncak pada 5101 langkah @ k = 58). Sepertinya akan mengambil nilai N yang sangat besar untuk langkah kasus O (k ^ 2) terburuk untuk menjadi lebih besar daripada langkah O (n) di mana Anda memindai bitstring untuk menemukan indeks posisi 1.
sumber
Saya mengalami masalah dengan skenario terburuk dengan jutaan digit. Fuzzing dari
/dev/urandom
dasarnya memberi Anda O (n), tapi saya tahu kasus terburuk lebih buruk dari itu. Aku tidak tahu seberapa parahnya. Untuk yang keciln
, itu sepele untuk menemukan input di sekitar3*n*log(n)
, tetapi sangat sulit untuk membedakan mereka dari beberapa urutan pertumbuhan lain untuk masalah khusus ini.Adakah yang bisa mengerjakan input kasus terburuk menghasilkan string dengan panjang lebih besar dari katakanlah, seratus ribu?
sumber
Adaptasi dari algoritma Rabin-Karp dapat dilakukan untuk Anda. Kompleksitasnya adalah 0 (n) sehingga dapat membantu Anda.
Lihatlah http://en.wikipedia.org/wiki/Rabin-Karp_string_search_algorithm
sumber
Mungkinkah ini solusi? Saya ', tidak yakin apakah itu O (nlogn) tetapi menurut saya itu lebih baik daripada O (n²) karena satu-satunya cara untuk tidak menemukan triple adalah distribusi bilangan prima.
Ada ruang untuk perbaikan, yang kedua ditemukan 1 bisa menjadi yang pertama berikutnya 1. Juga tidak ada pengecekan kesalahan.
sumber
Saya pikir algoritma ini memiliki kompleksitas O (n log n) (C ++, DevStudio 2k5). Sekarang, saya tidak tahu detail tentang bagaimana menganalisis suatu algoritma untuk menentukan kerumitannya, jadi saya telah menambahkan beberapa informasi pengumpulan metrik ke kode. Kode menghitung jumlah tes yang dilakukan pada urutan 1 dan 0 untuk setiap input yang diberikan (mudah-mudahan, saya belum membuat banyak algoritma). Kita dapat membandingkan jumlah tes aktual terhadap nilai O dan melihat apakah ada korelasi.
Program ini menghasilkan jumlah pengujian untuk setiap panjang string hingga 32 karakter. Inilah hasilnya:
Saya telah menambahkan nilai 'n log n' juga. Plot ini menggunakan alat grafik pilihan Anda untuk melihat korelasi antara dua hasil. Apakah analisis ini mencakup semua nilai n? Saya tidak tahu
sumber