Misalkan kita diberi array n bilangan bulat yang mewakili harga saham dalam satu hari. Kita ingin mencari pair (buyDay, sellDay) , dengan buyDay ≤ sellDay , sehingga jika kita membeli saham pada buyDay dan menjualnya pada sellDay , kita akan memaksimalkan keuntungan kita.
Jelas ada solusi O (n 2 ) untuk algoritme dengan mencoba semua kemungkinan pasangan (buyDay, sellDay) dan mengambil yang terbaik dari semuanya. Namun, apakah ada algoritma yang lebih baik, mungkin yang berjalan dalam waktu O (n) ?
arrays
algorithm
big-o
time-complexity
Ajeet Ganga
sumber
sumber
Jawaban:
Saya suka masalah ini. Ini adalah pertanyaan wawancara klasik dan tergantung bagaimana Anda memikirkannya, Anda akan mendapatkan solusi yang lebih baik dan lebih baik. Tentu saja mungkin untuk melakukan ini lebih baik daripada waktu O (n 2 ), dan saya telah membuat daftar tiga cara berbeda yang dapat Anda pikirkan tentang masalah ini di sini. Semoga ini menjawab pertanyaan Anda!
Pertama, solusi bagi-dan-taklukkan. Mari kita lihat apakah kita bisa menyelesaikan ini dengan membagi input menjadi dua, menyelesaikan masalah di setiap sublarik, lalu menggabungkan keduanya. Ternyata kita sebenarnya bisa melakukan ini, dan bisa melakukannya dengan efisien! Intuisi adalah sebagai berikut. Jika kita memiliki satu hari, pilihan terbaik adalah membeli pada hari itu dan kemudian menjualnya kembali pada hari yang sama tanpa keuntungan. Jika tidak, pisahkan array menjadi dua bagian. Jika kita berpikir tentang apa jawaban yang optimal, itu harus ada di salah satu dari tiga tempat:
Kita bisa mendapatkan nilai untuk (1) dan (2) dengan menggunakan algoritma kita secara rekursif pada bagian pertama dan kedua. Untuk opsi (3), cara mendapatkan keuntungan tertinggi adalah membeli di titik terendah di paruh pertama dan menjual di titik terbesar di paruh kedua. Kita dapat menemukan nilai minimum dan maksimum dalam dua bagian hanya dengan melakukan pemindaian linier sederhana pada input dan menemukan dua nilai. Ini kemudian memberi kita algoritme dengan pengulangan berikut:
Menggunakan Teorema Master untuk menyelesaikan pengulangan, kita menemukan bahwa ini berjalan dalam waktu O (n lg n) dan akan menggunakan ruang O (lg n) untuk panggilan rekursif. Kami baru saja mengalahkan solusi O (n 2 ) yang naif !
Tapi tunggu! Kami bisa melakukan jauh lebih baik dari ini. Perhatikan bahwa satu-satunya alasan kita memiliki suku O (n) dalam pengulangan kita adalah karena kita harus memindai seluruh input mencoba menemukan nilai minimum dan maksimum di setiap setengahnya. Karena kita sudah menjelajahi setiap bagian secara rekursif, mungkin kita dapat melakukan lebih baik dengan meminta rekursi juga mengembalikan nilai minimum dan maksimum yang disimpan di setiap bagian! Dengan kata lain, rekursi kami mengembalikan tiga hal:
Dua nilai terakhir ini dapat dihitung secara rekursif menggunakan rekursi langsung yang dapat kita jalankan bersamaan dengan rekursi untuk menghitung (1):
Jika kita menggunakan pendekatan ini, relasi pengulangan kita sekarang
Menggunakan Teorema Utama di sini memberi kita runtime dari O (n) dengan ruang O (lg n), yang bahkan lebih baik daripada solusi awal kita!
Tapi tunggu sebentar - kita bisa melakukan lebih baik dari ini! Mari pikirkan untuk memecahkan masalah ini menggunakan pemrograman dinamis. Idenya adalah memikirkan masalah sebagai berikut. Misalkan kita mengetahui jawaban soal setelah melihat elemen k pertama. Bisakah kita menggunakan pengetahuan kita tentang elemen (k + 1) st, dikombinasikan dengan solusi awal kita, untuk menyelesaikan masalah elemen pertama (k + 1)? Jika demikian, kita bisa mendapatkan algoritma yang bagus dengan menyelesaikan masalah untuk elemen pertama, lalu dua yang pertama, lalu tiga yang pertama, dll. Sampai kita menghitungnya untuk n elemen pertama.
Mari kita pikirkan bagaimana melakukan ini. Jika kita hanya memiliki satu elemen, kita sudah tahu bahwa itu pasti pasangan beli / jual terbaik. Sekarang misalkan kita mengetahui jawaban terbaik untuk elemen k pertama dan melihat elemen (k + 1) st. Maka satu-satunya cara agar nilai ini dapat menciptakan solusi yang lebih baik daripada yang kita miliki untuk elemen k pertama adalah jika perbedaan antara elemen k terkecil dan elemen baru lebih besar daripada perbedaan terbesar yang telah kita hitung sejauh ini. Jadi misalkan saat kita melintasi elemen, kita melacak dua nilai - nilai minimum yang kita lihat sejauh ini, dan keuntungan maksimum yang bisa kita hasilkan hanya dengan k elemen pertama. Awalnya, nilai minimum yang kami lihat sejauh ini adalah elemen pertama, dan keuntungan maksimum adalah nol. Saat kita melihat elemen baru, pertama-tama kami memperbarui laba optimal kami dengan menghitung berapa banyak yang kami hasilkan dengan membeli pada harga terendah yang terlihat sejauh ini dan menjual dengan harga saat ini. Jika ini lebih baik dari nilai optimal yang telah kami hitung sejauh ini, kami memperbarui solusi optimal menjadi keuntungan baru ini. Selanjutnya, kami memperbarui elemen minimum yang terlihat sejauh ini menjadi minimum elemen terkecil saat ini dan elemen baru.
Karena pada setiap langkah kita hanya melakukan O (1) pekerjaan dan kita mengunjungi masing-masing n elemen tepat satu kali, ini membutuhkan waktu O (n) untuk diselesaikan! Selain itu, hanya menggunakan penyimpanan tambahan O (1). Sejauh ini sudah bagus!
Sebagai contoh, pada masukan Anda, berikut ini bagaimana algoritma ini dapat berjalan. Angka-angka di antara masing-masing nilai larik sesuai dengan nilai yang dipegang oleh algoritme pada saat itu. Anda tidak akan benar-benar menyimpan semua ini (ini akan membutuhkan O (n) memori!), Tetapi sangat membantu untuk melihat algoritme berkembang:
Jawaban: (5, 10)
Jawaban: (4, 12)
Jawaban: (1, 5)
Bisakah kita melakukannya lebih baik sekarang? Sayangnya, tidak dalam arti asimtotik. Jika kita menggunakan waktu kurang dari O (n), kita tidak dapat melihat semua angka pada input yang besar dan dengan demikian tidak dapat menjamin bahwa kita tidak akan melewatkan jawaban yang optimal (kita hanya dapat "menyembunyikan" di elemen yang kita gunakan. tidak melihat). Selain itu, kami tidak dapat menggunakan kurang dari O (1) spasi. Mungkin ada beberapa pengoptimalan pada faktor konstan yang tersembunyi dalam notasi O besar, tetapi sebaliknya kita tidak dapat berharap untuk menemukan opsi yang jauh lebih baik.
Secara keseluruhan, ini berarti kami memiliki algoritme berikut:
Semoga ini membantu!
EDIT : Jika Anda tertarik, saya telah membuat kode versi Python dari empat algoritme ini sehingga Anda dapat bermain-main dengannya dan menilai kinerja relatifnya. Berikut kodenya:
sumber
Ini adalah masalah urutan jumlah maksimum dengan sedikit tipuan. Jumlah maksimum masalah selanjutnya diberikan daftar bilangan bulat yang bisa positif atau negatif, temukan jumlah terbesar dari subset yang berdekatan dari daftar itu.
Anda dapat dengan mudah mengubah masalah ini menjadi masalah itu dengan mengambil untung atau rugi antara hari-hari berturut-turut. Jadi, Anda akan mengubah daftar harga saham, misalnya
[5, 6, 7, 4, 2]
menjadi daftar keuntungan / kerugian, misalnya[1, 1, -3, -2]
. Masalah penjumlahan selanjutnya cukup mudah dipecahkan: Temukan urutan dengan jumlah elemen terbesar dalam sebuah arraysumber
Saya tidak begitu yakin mengapa ini dianggap sebagai pertanyaan pemrograman dinamis. Saya telah melihat pertanyaan ini di buku teks dan panduan algoritma menggunakan runtime O (n log n) dan O (log n) untuk ruang (misalnya, Elemen Wawancara Pemrograman). Sepertinya masalah yang jauh lebih sederhana daripada yang orang bayangkan.
Ini bekerja dengan melacak keuntungan maksimal, harga beli minimum, dan akibatnya, harga beli / jual yang optimal. Saat melewati setiap elemen dalam larik, ia memeriksa untuk melihat apakah elemen yang diberikan lebih kecil dari harga beli minimum. Jika ya, indeks harga beli minimum, (
min
), diperbarui menjadi indeks elemen tersebut. Selain itu, untuk setiap elemen,becomeABillionaire
algoritme memeriksa apakaharr[i] - arr[min]
(perbedaan antara elemen saat ini dan harga beli minimum) lebih besar dari keuntungan saat ini. Jika ya, keuntungan diperbarui menjadi selisih itu dan beli ditetapkan kearr[min]
dan jual ditetapkan kearr[i]
.Berjalan dalam sekali jalan.
Penulis bersama: https://stackoverflow.com/users/599402/ephraim
sumber
Masalahnya identik dengan sub-urutan maksimum yang
saya selesaikan menggunakan pemrograman Dinamis. Melacak saat ini dan sebelumnya (Laba, tanggal beli & tanggal jual) Jika saat ini lebih tinggi dari sebelumnya maka ganti sebelumnya dengan saat ini.
sumber
inilah solusi My Java:
sumber
Saya telah menemukan solusi sederhana - kode lebih dari cukup jelas. Ini adalah salah satu pertanyaan pemrograman dinamis.
Kode tidak menangani pemeriksaan kesalahan dan kasus tepi. Ini hanya contoh untuk memberikan gambaran tentang logika dasar untuk menyelesaikan masalah.
sumber
Inilah solusi saya. memodifikasi algoritma sub-urutan maksimum. Memecahkan masalah di O (n). Saya pikir itu tidak bisa dilakukan lebih cepat.
sumber
Ini adalah masalah yang menarik, karena tampaknya sulit, tetapi pemikiran yang cermat menghasilkan solusi yang elegan dan sederhana.
Seperti yang telah dicatat, kekerasan dapat diselesaikan dalam waktu O (N ^ 2). Untuk setiap entri dalam larik (atau daftar), ulangi semua entri sebelumnya untuk mendapatkan nilai minimum atau maksimum tergantung pada apakah masalahnya adalah menemukan untung atau rugi terbesar.
Berikut adalah cara memikirkan solusi dalam O (N): setiap entri mewakili kemungkinan maks (atau min) baru. Kemudian, yang perlu kita lakukan adalah menyimpan min sebelumnya (atau maks), dan membandingkan perbedaannya dengan arus dan min sebelumnya (atau maks). Sangat mudah.
Ini kodenya, di Java sebagai pengujian JUnit:
Dalam kasus penghitungan kerugian terbesar, kami melacak nilai maksimum dalam daftar (harga beli) hingga entri saat ini. Kami kemudian menghitung perbedaan antara max dan entri saat ini. Jika max - current> maxLoss, maka kami tetap menggunakan diff sebagai maxLoss baru. Karena indeks maks dijamin kurang dari indeks saat ini, kami menjamin bahwa tanggal 'beli' kurang dari tanggal 'jual'.
Dalam kasus menghitung keuntungan terbesar, semuanya terbalik. Kami melacak min dalam daftar hingga entri saat ini. Kami menghitung perbedaan antara min dan entri saat ini (membalik urutan pengurangan). Jika saat ini - min> maxGain, maka kami menyimpan perbedaan ini sebagai maxGain baru. Sekali lagi, indeks 'beli' (min) datang sebelum indeks saat ini ('jual').
Kita hanya perlu melacak maxGain (atau maxLoss), dan indeks min atau max, tapi tidak keduanya, dan kita tidak perlu membandingkan indeks untuk memvalidasi bahwa 'beli' kurang dari 'jual', karena kita dapatkan ini secara alami.
sumber
Keuntungan penjualan tunggal maksimum, solusi O (n)
Berikut adalah proyek yang melakukan pengujian kompleksitas waktu pada pendekatan o (N) vs o (n ^ 2) pada kumpulan data acak pada 100k int. O (n ^ 2) membutuhkan waktu 2 detik, sedangkan O (n) membutuhkan 0,01 detik
https://github.com/gulakov/complexity.js
Ini adalah pendekatan yang lebih lambat, o (n ^ 2) yang berulang melalui sisa hari untuk setiap hari, putaran ganda.
sumber
Jawaban pilihan teratas tidak memungkinkan untuk kasus di mana keuntungan maksimum negatif dan harus dimodifikasi untuk memungkinkan kasus seperti itu. Seseorang dapat melakukannya dengan membatasi kisaran loop ke (len (a) - 1) dan mengubah cara penentuan keuntungan dengan menggeser indeks satu per satu.
Bandingkan versi fungsi ini dengan yang sebelumnya untuk array:
sumber
sumber
Kemungkinan untuk menentukan keuntungan maksimum mungkin dengan melacak elemen minimum sisi kiri dan maksimum sisi kanan dalam larik pada setiap indeks dalam larik. Ketika Anda kemudian mengulangi harga saham, untuk hari tertentu Anda akan mengetahui harga terendah hingga hari itu, dan Anda juga akan mengetahui harga maksimum setelah (dan termasuk) hari itu.
Sebagai contoh, mari kita definisikan a
min_arr
danmax_arr
, dengan array yang diberikanarr
. Indeksi
dalammin_arr
akan menjadi elemen minimum dalamarr
untuk semua indeks<= i
(kiri dan termasuk i). Indeksi
masukmax_arr
akan menjadi elemen maksimum dalamarr
untuk semua indeks>= i
(hak dari dan termasuk i). Kemudian, Anda dapat menemukan perbedaan maksimum antara elemen terkait dalammax_arr
dan `min_arr ':Ini harus berjalan dalam waktu O (n), tapi saya yakin ini menghabiskan banyak ruang.
sumber
Ini adalah perbedaan maksimum antara dua elemen dalam array dan ini adalah solusi saya:
O (N) kompleksitas waktu O (1) kompleksitas ruang
sumber
Setelah gagal dalam ujian pengkodean langsung untuk posisi insinyur solusi FB, saya harus menyelesaikannya dalam suasana sejuk yang tenang, jadi inilah 2 sen saya:
sumber
Satu-satunya jawaban yang benar-benar menjawab pertanyaan adalah salah satu dari @akash_magoon (dan dengan cara yang sangat sederhana!), Tetapi tidak mengembalikan objek persis seperti yang ditentukan dalam pertanyaan. Saya merefaktor sedikit dan mendapatkan jawaban saya di PHP mengembalikan apa yang ditanyakan:
sumber
Solusi yang rapi:
sumber
Program pada python3 ini dapat mengembalikan harga beli dan harga jual yang akan memaksimalkan keuntungan, dihitung dengan kompleksitas waktu O (n) dan kompleksitas ruang O (1) .
sumber
Inilah solusi saya
sumber
Untuk semua jawaban yang mencatat elemen minimum dan maksimum, solusi itu sebenarnya adalah solusi O (n ^ 2). Ini karena pada akhirnya harus diperiksa apakah maksimum terjadi setelah minimum atau tidak. Jika tidak, iterasi lebih lanjut diperlukan sampai kondisi tersebut terpenuhi, dan ini meninggalkan kasus terburuk O (n ^ 2). Dan jika Anda ingin melewatkan iterasi ekstra maka diperlukan lebih banyak ruang. Either way, tidak-tidak dibandingkan dengan solusi pemrograman dinamis
sumber