Seorang teman saya sedang mewawancarai untuk suatu pekerjaan. Salah satu pertanyaan wawancara membuat saya berpikir, hanya ingin umpan balik.
Ada 2 bilangan bulat non-negatif: i dan j. Dengan persamaan berikut, cari solusi (optimal) untuk beralih lebih dari i dan j sedemikian rupa sehingga output diurutkan.
2^i * 5^j
Jadi beberapa putaran pertama akan terlihat seperti ini:
2^0 * 5^0 = 1
2^1 * 5^0 = 2
2^2 * 5^0 = 4
2^0 * 5^1 = 5
2^3 * 5^0 = 8
2^1 * 5^1 = 10
2^4 * 5^0 = 16
2^2 * 5^1 = 20
2^0 * 5^2 = 25
Cobalah sekuat tenaga, saya tidak bisa melihat polanya. Pikiran Anda?
algorithm
optimization
hamming-numbers
smooth-numbers
Chris Eberle
sumber
sumber
2^2 < 5
tetapi2^3 > 5
pada saat itu Anda meningkatkan j. Saya pikir Anda dapat menghasilkan output dalam O (n) daripada O (nlgn). @ tom-zynch dua loop bersarang adalah O (n ^ 2). Pertanyaan ini sangat validJawaban:
Dijkstra mendapatkan solusi fasih dalam "A Disiplin Pemrograman". Dia menghubungkan masalah itu dengan Hamming. Inilah implementasi solusi Dijkstra saya.
sumber
di sini adalah cara yang lebih baik untuk melakukannya (lebih halus dari jawaban saya sebelumnya, yaitu):
bayangkan angka-angka ditempatkan dalam matriks:
apa yang perlu Anda lakukan adalah 'berjalan' matriks ini, mulai dari
(0,0)
. Anda juga perlu melacak apa kemungkinan langkah Anda selanjutnya. Ketika Anda mulai pada(0,0)
Anda hanya memiliki dua opsi: baik(0,1)
atau(1,0)
: karena nilai(0,1)
lebih kecil, Anda memilih itu. kemudian lakukan hal yang sama untuk pilihan Anda berikutnya(0,2)
atau(1,0)
. Sejauh ini, Anda memiliki daftar berikut:1, 2, 4
. Langkah Anda berikutnya adalah(1,0)
karena nilainya lebih kecil dari(0,3)
. Namun, Anda sekarang memiliki tiga pilihan untuk langkah selanjutnya: baik(0,3)
, atau(1,1)
, atau(2,0)
.Anda tidak perlu matriks untuk mendapatkan daftar, tetapi Anda perlu melacak semua pilihan Anda (yaitu ketika Anda mencapai 125+, Anda akan memiliki 4 pilihan).
sumber
j
memeriksa untuk setiap 1 outputj ~ n^0.5
untuk n th-nilai secara berurutan, karenan
nilai-nilai mengisi area padai x j
pesawat. Jadi algo ini adalahO(n^1.5)
waktu, denganO(n^0.5)
ruang. Tapi ada algo waktu linier dengan ruang yang saman^0.5
, dan algo-heap mini dari jawaban di bawah ini adalahO(n*log(n))
waktu dengann^0.5
ruang yang sama .Gunakan Min-heap.
Masukkan 1.
ekstrak-Min. Katakanlah Anda mendapatkan x.
Dorong 2x dan 5x ke tumpukan.
Ulang.
Alih-alih menyimpan x = 2 ^ i * 5 ^ j, Anda dapat menyimpan (i, j) dan menggunakan fungsi perbandingan khusus.
sumber
Solusi berbasis FIFO membutuhkan kapasitas penyimpanan yang lebih sedikit. Kode python
keluaran:
sumber
Ini sangat mudah dilakukan
O(n)
dalam bahasa fungsional. Daftarl
dari2^i*5^j
nomor dapat hanya didefinisikan sebagai1
kemudian2*l
dan5*l
bergabung. Berikut ini tampilannya di Haskell:The
merge
Fungsi memberikan nilai baru dalam waktu yang konstan. Begitu jugamap
dan karenanya juga begitul
.sumber
union
, karena menghapus duplikat.merge
, sebagai bagian darimergesort
, harus mempertahankan duplikat yang berasal dari kedua urutan inputnya. LihatData.List.Ordered
paket untuk hal-hal terkait.Data.List.Ordered.union
. Itu membuatnya menjadi satu baris:xs = 1 : union (map (2*) xs) (map (5*) xs)
[1, 2, 4, 5,...]
jadi termasuk5*4
.Data.List.Ordered.union
fungsinya. Jangan bingung denganData.List.union
.Anda harus melacak eksponen individu mereka, dan berapa jumlah mereka
jadi Anda mulai dengan
f(0,0) --> 1
sekarang Anda harus menambah salah satunya:jadi kita tahu 2 adalah yang berikutnya - kita juga tahu kita bisa menaikkan eksponen sampai jumlah surpases 5.
Anda terus bolak-balik seperti ini sampai Anda berada di jumlah putaran deisred Anda.
sumber
f(*,2)
hanya karena Anda menemukan ituf(a1,b+1)>f(a2,b)
. Pendekatan tambahan pada akhirnya akan menghasilkan jumlah pasangan yang tidak terbatas yang berdekatan dengan wilayah yang sudah Anda hasilkan.Menggunakan pemrograman dinamis, Anda dapat melakukan ini di O (n). Kebenaran dasarnya adalah bahwa tidak ada nilai i dan j yang dapat memberi kita 0, dan untuk mendapatkan 1 nilai keduanya harus 0;
Setiap kali Anda memanggil fungsi ini periksa apakah i dan j diatur, jika tidak nol, maka isi
TwoCount
danFiveCount
Jawaban C ++. Maaf untuk gaya pengkodean yang buruk, tapi saya sedang terburu-buru :(
Jelas Anda dapat menggunakan struktur data selain array untuk meningkatkan penyimpanan Anda secara dinamis, dll. Ini hanya sketsa untuk membuktikan bahwa itu berfungsi.
sumber
O(exp(sqrt(n)))
, untuk menghasilkann
nomor urut. Algoritma linear ada, misalnya seperti yang diberikan oleh ThomasAhle.O(n)
berartin
menjadi nilai terakhir, bukan jumlah barang yang dicetak, yang tidak benar. Saya tidak tahu bagaimana bahasa fungsional bekerja, atau bagaimana penggabungan bekerja dalam waktu yang konstan, tetapi jawabannya membuat saya merasa lebih baikMengapa tidak mencoba melihat ini dari arah lain. Gunakan penghitung untuk menguji kemungkinan jawaban terhadap formula asli. Maaf untuk kode semu.
sumber
O(4^sqrt(n))
karenanth
jumlah urutan kira-kira sebesar itu.Ini adalah entri yang relevan di OEIS.
Tampaknya mungkin untuk mendapatkan urutan yang diurutkan dengan menghasilkan beberapa istilah pertama, katakanlah
dan kemudian, mulai dari istilah kedua, mengalikan dengan 4 dan 5 untuk mendapatkan dua berikutnya
dan seterusnya...
Secara intuitif, ini tampaknya benar, tetapi tentu saja bukti tidak ada.
sumber
Anda tahu bahwa log_2 (5) = 2.32. Dari ini kita perhatikan bahwa 2 ^ 2 <5 dan 2 ^ 3> 5.
Sekarang lihatlah matriks jawaban yang memungkinkan:
Sekarang, untuk contoh ini, pilih angka dalam urutan. Akan ada pemesanan:
Perhatikan bahwa setiap baris dimulai 2 kolom di belakang baris yang memulainya. Misalnya, i = 0 j = 1 datang langsung setelah i = 2 j = 0.
Algoritma yang dapat kita peroleh dari pola ini adalah (asumsikan j> i):
CATATAN: Kode di sini membatasi nilai eksponen i dan j menjadi kurang dari 10. Anda dapat dengan mudah memperluas algoritma ini agar sesuai dengan batas arbitrer lainnya.
CATATAN: Waktu berjalan untuk algoritma ini adalah O (n) untuk n jawaban pertama.
CATATAN: Kompleksitas ruang untuk algoritma ini adalah O (1)
sumber
Implementasi saya didasarkan pada ide-ide berikut:
Contoh:
Kode di Jawa:
sumber
menghitung hasilnya dan memasukkannya ke dalam daftar yang disortir, bersama dengan nilai untuk
i
danj
sumber
2^n*5^n
tetapi tidak2^(n+1)*5^(n-1)
yang lebih kecil.i
danj
, bukan? Kalau tidak, Anda tidak akan pernah sampai ke status penyortiran, dan karenanya Anda tidak akan pernah mengembalikan nilai tunggal. Tetapi untuk batas apa pun yangn
Anda pilih, daftar Anda akan cacat.i
danj
.2^i*5^j
nilai - nilai, dan kemudian mengurutkannya. Jika Anda tidak memiliki "hasil" dalam jumlah terbatas, bagaimana Anda akan sampai pada langkah penyortiran?Algoritme yang diimplementasikan oleh user515430 oleh Edsger Dijkstra (http://www.cs.utexas.edu/users/EWD/ewd07xx/EWD792.PDF) mungkin secepat yang Anda bisa dapatkan. Saya memanggil setiap nomor yang merupakan bentuk
2^i * 5^j
"nomor khusus". Sekarang jawaban vlads akanO(i*j)
tetapi dengan algoritma ganda, satu untuk menghasilkan angka khususO(i*j)
dan satu untuk menyortirnya (sesuai dengan artikel yang ditautkan jugaO(i*j)
.Tapi mari kita periksa algoritma Dijkstra (lihat di bawah). Dalam hal ini
n
adalah jumlah angka khusus yang kami hasilkan, sehingga sama dengani*j
. Kami mengulang sekali,1 -> n
dan di setiap putaran kami melakukan tindakan konstan. Jadi algoritma ini jugaO(i*j)
. Dan dengan konstanta cepat yang sangat menyala juga.Implementasi saya di C ++ dengan GMP (pembungkus C ++), dan ketergantungan
boost::lexical_cast
, meskipun itu dapat dengan mudah dihapus (saya malas, dan siapa yang tidak menggunakan Boost?). Disusun dengang++ -O3 test.cpp -lgmpxx -o test
. Pada Q6600 Ubuntu 10.10time ./test 1000000
memberi1145ms
.sumber
Jika Anda menggambar matriks dengan i sebagai baris dan j sebagai kolom, Anda dapat melihat polanya. Mulailah dengan i = 0 dan kemudian hanya melintasi matriks dengan naik 2 baris dan kanan 1 kolom sampai Anda mencapai bagian atas matriks (j> = 0). Lalu lanjutkan +1, dll ...
Jadi untuk i = 7 Anda bepergian seperti ini:
Dan untuk i = 8:
Ini dia di Java naik ke i = 9. Mencetak posisi matriks (i, j) dan nilainya.
sumber
Intuisi saya :
Jika saya mengambil nilai awal sebagai 1 di mana i = 0, j = 0, maka saya dapat membuat angka berikutnya sebagai (2 ^ 1) (5 ^ 0), (2 ^ 2) (5 ^ 0), (2 ^ 0) * (5 ^ 1), ... yaitu 2,4,5 ..
Katakanlah kapan saja nomor saya adalah x. maka saya dapat membuat angka berikutnya dengan cara berikut:
Penjelasan :
Uji Coba
Mari kita mulai dengan x = 1.
Tiga angka berikutnya adalah 1 * 2, 1 * 4, 1 * 5 [2,4,5]; Rangkaian [1,2,4,5]
Sekarang x = 2
Tiga angka berikutnya adalah [4,8,10] {Karena 4 sudah terjadi, kita akan mengabaikannya} [8,10]; Rancangan [1,2,4,5,8,10]
Sekarang x = 4
Tiga angka berikutnya [8,16,20] {8 sudah terjadi abaikan saja} [16,20] Atur [1,2,4,5,8,10,16,20]
x = 5
Tiga angka berikutnya [10,20,25] {10,20} sudah jadi [25] ditambahkan Arr [1,2,4,5,8,10,16,20,25]
Kondisi Pengakhiran
Analisis
sumber
Hanya ingin tahu apa yang diharapkan minggu depan dan telah menemukan pertanyaan ini.
Saya pikir, idenya adalah 2 ^ i meningkat tidak dalam langkah besar sebesar 5 ^ j. Jadi tambahlah saya selama j-step berikutnya tidak akan lebih besar.
Contoh dalam C ++ (Qt adalah opsional):
Hasil:
sumber
Ini solusinya
Hasil:
sumber
Saya tahu saya mungkin salah tetapi ada heuristik yang sangat sederhana di sini karena tidak melibatkan banyak angka seperti 2,3,5. Kita tahu bahwa untuk setiap i, j2 ^ i * 5 ^ j urutan selanjutnya adalah 2 ^ (i-2) * 5 ^ (j + 1). Menjadi google q itu harus memiliki solusi sederhana.
Ini menghasilkan output sebagai:
sumber
Jika Anda mengikuti apa yang sebenarnya terjadi ketika kita menambah i atau j dalam ekspresi
2^i * 5^j
, Anda dapat mengalikan dengan 2 atau yang lain 5. Jika kami menyatakan kembali masalah sebagai - diberi nilai tertentu dari i dan j, bagaimana Anda menemukan berikutnya nilai yang lebih besar, solusinya menjadi jelas.Berikut adalah aturan yang dapat kami sebutkan secara intuitif:
i > 1
) dalam ekspresi, kita harus menggantinya dengan 5 untuk mendapatkan angka terbesar berikutnya. Jadi,i -= 2
danj += 1
.j > 0
), kita perlu menggantinya dengan tiga 2s. Jadij -= 1
dani += 3
.i += 1
.Berikut programnya di Ruby:
sumber
Jika kita diizinkan menggunakan Koleksi java maka kita dapat memiliki nomor ini dalam O (n ^ 2)
Sini powerLimit harus diinisialisasi dengan sangat hati-hati !! Tergantung pada berapa banyak angka yang Anda inginkan.
sumber
Inilah upaya saya dengan Scala:
Keluaran:
sumber