Jumlah terbesar yang dapat dibagi oleh n

16

Saya menanyakan pertanyaan ini pada StackOverflow , tapi saya pikir di sini adalah tempat yang lebih tepat.

Ini adalah masalah dari kursus Pengantar algoritma :

Anda memiliki array dengan bilangan bulat positif (array tidak perlu diurutkan atau elemen unik). Sarankan algoritma untuk menemukan jumlah elemen terbesar yang dapat dibagi oleh .anO(n)n

Contoh: . Jawabannya adalah 56 (dengan elemen 6 , 13 , 4 , 8 , 25 )a=[6,1,13,4,9,8,25],n=7566,13,4,8,25

Ini relatif mudah untuk menemukannya di O(n2) menggunakan pemrograman dinamis dan menyimpan jumlah terbesar dengan sisa 0,1,2,...,n1 .

Juga, jika kita membatasi perhatian pada urutan elemen yang berdekatan, mudah untuk menemukan urutan yang optimal dalam waktu O(n) , dengan menyimpan jumlah parsial modulo n : misalkan S[i]=a[0]+a[1]++a[i] , untuk setiap sisa ringat indeks terbesar jsehingga , dan kemudian untuk setiap Anda menganggap manaS[j]r(modn)iS[j]S[i]jadalah indeks yang sesuai dengan .r=S[i]modn

Tetapi apakah ada solusi O(n) -waktu untuk kasus umum? Setiap saran akan dihargai! Saya menganggap ini ada hubungannya dengan aljabar linier tapi saya tidak yakin apa tepatnya.

Atau, dapatkah ini dilakukan dalam waktu O(nlogn) ?

delta-terminator
sumber
2
1. Anda telah memposting pertanyaan yang sama persis di Stack Overflow. Tolong jangan posting pertanyaan yang sama di beberapa situs . Kami tidak ingin banyak salinan beredar di beberapa situs SE. Jika Anda tidak mendapatkan jawaban yang dapat diterima, tidak masalah untuk menandai pertanyaan Anda untuk migrasi ke situs lain, tapi tolong jangan posting ulang hal yang sama di tempat lain. 2. Dapatkah Anda memberikan referensi / kutipan / tautan ke buku teks atau kursus di mana ini muncul? Seberapa yakin Anda bahwa ada solusi waktu ? O(n)
DW
5
Apakah tantangan di universitas Anda masih terbuka? Akan sangat membantu untuk melihat tautan ke kursus, pertanyaan yang tepat dan jika benar-benar dan orang-orang yang menyiapkannya akan menjelaskan / mempublikasikan jawaban mereka, itu akan luar biasa. O(n)
Evil
Relatif mudah untuk menemukannya di O (n2) O (n2) menggunakan pemrograman dinamis dan menyimpan jumlah terbesar dengan sisanya 0,1,2, ..., n − 10,1,2, ..., n − 1. Bisakah Anda jelaskan sedikit ini? Saya bisa mengerti bagaimana ini akan menjadi n-kuadrat jika kita hanya mempertimbangkan elemen yang berdekatan, tetapi dengan elemen yang tidak berdekatan juga, bukankah itu akan eksponensial dalam urutan?
Nithish Inpursuit Ofhappiness

Jawaban:

4

Berikut adalah beberapa ide acak:

  • Algoritma pemrograman dinamis dapat dibalik untuk mencari jumlah terkecil, bukan jumlah terbesar. Anda akhirnya mencari jumlah kongruen ke sisa jumlah seluruh array, bukan satu kongruen ke nol. Jika kami memproses elemen dalam urutan yang meningkat, ini terkadang memungkinkan algoritma dinamis untuk mengakhiri sebelum memproses seluruh array.

    Biaya akan menjadi jika kita memproses elemen . Ada tidak lebih rendah terikat pada algoritma ini karena kita tidak harus menyortir semua elemen. Hanya membutuhkan waktu untuk mendapatkan elemen terkecil .O(nk)kΩ(nlogn)O(nlogk)k

  • Jika kita peduli dengan himpunan dengan ukuran terbesar, alih-alih himpunan dengan jumlah terbesar, kita mungkin dapat menggunakan perkalian polinomial berbasis transformasi fourier cepat untuk menyelesaikan masalah dalam waktu. Mirip dengan apa yang dilakukan di 3SUM ketika rentang domain terbatas. (Catatan: gunakan kuadrat berulang untuk melakukan pencarian biner, kalau tidak Anda akan mendapatkan mana adalah jumlah elemen yang dihilangkan.)O(n(logn)2(loglogn))O(nk(logn)(loglogn))k

  • Ketika adalah gabungan, dan hampir semua sisa adalah kelipatan dari salah satu faktor , waktu yang signifikan dapat dihemat dengan memfokuskan pada sisanya yang bukan merupakan kelipatan dari faktor itu.nn

  • Ketika sisa radalah sangat umum, atau hanya ada beberapa sisa yang ada, melacak 'slot terbuka berikutnya jika Anda mulai dari sini dan terus maju dengan r' informasi dapat menghemat banyak pemindaian untuk lompatan-ke-tempat-terbuka waktu.

  • Anda dapat mencukur faktor log dengan hanya melacak jangkauan dan menggunakan topeng bit (dalam algoritma dinamis terbalik), lalu mundur setelah Anda mencapai target yang tersisa.

  • Algoritma pemrograman dinamis sangat memungkinkan untuk dijalankan secara paralel. Dengan prosesor untuk setiap slot buffer, Anda dapat beralih ke . Sebagai alternatif, dengan menggunakan luas , dan membagi dan menaklukkan agregasi alih-alih agregasi iteratif, biaya kedalaman rangkaian bisa sampai ke .O(n)O(n2)O(log2n)

  • (Meta) Saya sangat curiga bahwa masalah yang Anda berikan adalah jumlah yang berdekatan . Jika Anda ditautkan dengan masalah aktual, akan mudah untuk memverifikasi itu. Kalau tidak, saya sangat terkejut dengan betapa sulitnya masalah ini, mengingat bahwa itu diberikan dalam kursus yang disebut "Pengantar Algoritma". Tapi mungkin Anda membahas trik di kelas yang membuatnya sepele.

Craig Gidney
sumber
Untuk poin satu. Itu tidak tertulis dalam spesifikasi masalah, jadi Anda tidak dapat mengasumsikan itu. Juga, masalahnya bukan mengatakan bahwa Anda tidak dapat mengubah array atau membuat yang baru, Anda memang bisa. Satu-satunya hal yang perlu Anda lakukan adalah menemukan angka-angka yang dijumlahkan memberi Anda jumlah terbesar yang dapat dibagi dengan dalam O ( n ) kompleksitas waktu (biasanya diasumsikan hanya kompleksitas waktu). nO(n)
nbro
2
@ EvilJS Subset dengan jumlah terbesar dengan sisa 0 sama dengan set lengkap setelah menghapus subset dengan jumlah terkecil dengan sisa kongruen dengan jumlah set lengkap. Mencari kongruen jumlah terkecil ke lebih nyaman daripada mencari kongruen jumlah terbesar ke r 2 karena memungkinkan Anda untuk mengakhiri segera setelah Anda menemukan solusi (saat memproses elemen dalam urutan yang meningkat) daripada harus melanjutkan. r1r2
Craig Gidney
-1

Algoritme yang saya usulkan berjalan sebagai berikut:

Jumlah dapat dibagi oleh n jika Anda hanya menambahkan jumlah yang merupakan kelipatan dari n.

Sebelum Anda mulai, Anda membuat hashmap dengan kunci int as dan daftar indeks sebagai nilai. Anda juga membuat daftar hasil yang berisi indeks.

Anda kemudian mengulang array dan menambahkan setiap indeks yang mod n adalah nol ke daftar hasil Anda. Untuk setiap indeks lain Anda melakukan hal berikut:

Anda kurangi nilai mod n dari indeks ini dari n. Hasil ini adalah kunci untuk hashmap Anda yang menyimpan indeks untuk elemen dengan nilai yang diperlukan. Sekarang, Anda menambahkan indeks ini ke daftar di hashmap dan melanjutkan.

Setelah Anda selesai mengulangi array Anda menghitung output. Anda melakukan ini dengan mengurutkan setiap daftar dalam hashmap sesuai dengan nilai yang ditunjukkan oleh indeks. Sekarang Anda menganggap setiap pasangan dalam hashmap yang menyimpulkan hingga n. Jadi jika n = 7 Anda mencari hashmap untuk 3 dan 4. Jika Anda mendapat entri di kedua Anda mengambil dua nilai terbesar menghapusnya dari daftar mereka dan menambahkannya ke daftar hasil Anda.

Rekomendasi terakhir: masih tidak menguji algoritma, menulis testcase terhadapnya menggunakan algoritma brute force.

Tobias Würfl
sumber
2
Serakah, linier, tidak bekerja. Anda menganggap hanya elemen yang dapat dibagi oleh n dan pasangan dibagi oleh n, bagaimana dengan tiga kali lipat dan lebih banyak lagi? Itu tidak menjamin jumlah subset maksimal pada kasus sepele. [2, 1, 8] -> jumlah maksimal adalah 9, tetapi algoritme Anda mengembalikan 3.
Evil
@EvilJS apa yang terjadi dengan sub- Anda algoritma? n2
delta-terminator
Terima kasih telah menunjukkan kesalahan ini kepada saya. Gagasan saya tentang peningkatan adalah membuat hashmap tumpukan daftar yang dipesan dengan meningkatkan nilai dan mulai mengumpulkan hanya setelah menyelesaikan pass melalui array.
Tobias Würfl
Maksud Anda array array, yang akan diurutkan, dan "hashmap" adalah% n? Anda masih perlu menyortirnya, dan jika Anda menyortirnya, mengambil nilai minimal / maksimal adalah ok, tetapi masih ada bagian yang tak terhindarkan untuk benar-benar memilih subset, yang dalam kasus terburuk tidak menguntungkan. Lagi pula jika Anda memiliki beberapa peningkatan, mungkin Anda dapat mengedit posting?
Evil
Ya itu ide yang cepat dengan tumpukan. Bahkan Anda hanya perlu daftar dalam hashmap yang Anda urutkan. Saya tidak yakin apakah sopan mengedit jawaban pertama saya. Bagaimanapun, saya membuat kesalahan dalam upaya pertama saya.
Tobias Würfl
-2

gunakan metode DP ini dari ( /programming/4487438/maximum-sum-of-non-consecutive-elements?rq=1 ):

Diberikan array A [0..n], misalkan M (i) menjadi solusi optimal menggunakan elemen dengan indeks 0..i. Kemudian M (-1) = 0 (digunakan dalam perulangan), M (0) = A [0], dan M (i) = maks (M (i - 1), M (i - 2) + A [i ]) untuk i = 1, ..., n. M (n) adalah solusi yang kita inginkan. Ini O (n) . Anda dapat menggunakan larik lain untuk menyimpan pilihan mana yang dibuat untuk setiap sub-masalah, dan memulihkan elemen yang sebenarnya dipilih.

Ubah rekursi menjadi M (i) = maks (M (i - 1), M (i - 2) + A [i]) sedemikian sehingga hanya disimpan jika dapat dibagi oleh N

Kepala peniti
sumber
2
Ini tidak berhasil - saya akan memberitahu Anda mengapa. (Petunjuk: Cobalah untuk menjalankannya pada array konstan 1.) Selain itu, dalam masalah ini kami mengizinkan elemen berurutan.
Yuval Filmus
1
Ini adalah solusi yang sangat baik, hanya untuk masalah yang sama sekali berbeda (dan jauh lebih mudah).
Evil