Somma più grande divisibile per n


16

Ho fatto questa domanda su StackOverflow , ma penso che qui sia un posto più appropriato.

Questo è un problema dall'introduzione al corso sugli algoritmi :

Hai una matrice a con n numeri interi positivi (la matrice non deve essere ordinata o gli elementi univoci). Suggerisci un algoritmo O(n) per trovare la somma più grande di elementi che è divisibile per n .

Esempio: a=[6,1,13,4,9,8,25],n=7 . La risposta è 56 (con elementi 6,13,4,8,25 )

E 'relativamente facile trovare in utilizzando la programmazione dinamica e memorizzare più grande somma con resto 0 , 1 , 2 , . . . , n - 1 .O(n2)0,1,2,...,n-1

Inoltre, se limitiamo l'attenzione a una sequenza contigua di elementi, è facile trovare tale sequenza ottimale in tempo, memorizzando somme parziali modulo n : let S [ i ] = a [ 0 ] + a [ 1 ] + + a [ i ] , per ogni resto r ricordare l'indice più grande j tale che S [ j ] rO(n)nS[i]=a[0]+a[1]++a[i]rj , quindi per ogni i consideri S [ j ] - S [ i ] dove j è l'indice corrispondente a r = S [ i ] mod n .S[j]r(modn)iS[j]S[i]jr=S[i]modn

Ma esiste una soluzione -time per il caso generale? Ogni suggerimento sarà apprezzato! Ritengo che questo abbia qualcosa a che fare con l'algebra lineare, ma non sono sicuro di cosa esattamente.O(n)

In alternativa, questo può essere fatto in tempo?O(nlogn)


2
1. Hai pubblicato la stessa identica domanda su Stack Overflow. Si prega di non pubblicare la stessa domanda su più siti . Non vogliamo più copie fluttuanti su più siti SE. Se non hai ricevuto una risposta accettabile, è OK contrassegnare la tua domanda per la migrazione su un altro sito, ma per favore non ripubblicare la stessa cosa altrove. 2. Puoi fornire un riferimento / citazione / link al libro di testo o al corso in cui è apparso? Quanto sei sicuro che esista una soluzione -time? O(n)
DW

5
La sfida nella tua università è ancora aperta? Sarebbe davvero utile vedere il link al corso, la domanda esatta e se è davvero e le persone che lo hanno preparato spiegheranno / pubblicheranno la loro risposta sarebbe fantastico. O(n)
Evil

È relativamente facile trovarlo in O (n2) O (n2) usando la programmazione dinamica e memorizzando la somma più grande con il resto 0,1,2, ..., n − 10,1,2, ..., n − 1. Potresti per favore elaborarlo un po '? Posso capire come questo sarebbe n-quadrato se consideriamo solo elementi contigui, ma anche con elementi non contigui, non sarebbe esponenziale in ordine?
Nithish Inpursuit Ofhappiness,

Risposte:


4

Ecco alcune idee casuali:

  • L'algoritmo di programmazione dinamica può essere capovolto per cercare una somma più piccola invece di una più grande. Devi solo cercare una somma congruente con il resto della somma dell'intero array, anziché un congruente a zero. Se elaboriamo gli elementi in ordine crescente, questo a volte consente di terminare l'algoritmo dinamico prima di elaborare l'intero array.

    Il costo sarebbe se elaborassimo k elementi. C'è non un limite inferiore di Ω ( n log n ) su questo algoritmo, perché non abbiamo per ordinare tutti gli elementi. Ci vuole solo O ( n log k ) per ottenere i k elementi più piccoli.O(nk)kΩ(nlogn)O(nlogk)k

  • Se ci preoccupassimo del set con la dimensione maggiore, anziché del set con la somma maggiore, potremmo essere in grado di utilizzare la moltiplicazione polinomiale basata sulla trasformata di Fourier veloce per risolvere il problema in ora. Simile a ciò che viene fatto in 3SUM quando l'intervallo di domini è limitato. (Nota: usa la quadratura ripetuta per fare una ricerca binaria, altrimenti otterrai O ( n k ( log n ) ( log log n ) ) dove kO(n(logn)2(loglogn))O(nk(logn)(loglogn))k è il numero di elementi omessi).

  • Quando è composto e quasi tutti i resti sono un multiplo di uno dei fattori n , è possibile risparmiare tempo significativo concentrandosi sui resti che non sono un multiplo di quel fattore.nn

  • Quando un resto rè molto comune o sono presenti solo pochi resti, tenere traccia del 'prossimo slot aperto se si parte da qui e si continua ad avanzare di r' informazioni può salvare un sacco di scansioni per i salti in punti aperti tempo.

  • È possibile radere un fattore di registro solo monitorando la raggiungibilità e utilizzando le maschere di bit (nell'algoritmo dinamico capovolto), quindi tornare indietro una volta raggiunto il resto del target.

  • L'algoritmo di programmazione dinamica è molto suscettibile di essere eseguito in parallelo. Con un processore per ogni slot di buffer è possibile scendere a . In alternativa, usando O ( n 2 ) di larghezza, e dividere e conquistare l'aggregazione invece dell'aggregazione iterativa, il costo della profondità del circuito può arrivare fino a O ( log 2 n ) .O(n)O(n2)O(log2n)

  • (Meta) Sospetto fortemente che il problema che ti è stato dato riguardi somme contigue . Se si è collegati al problema reale, sarebbe facile verificarlo. Altrimenti sono molto sorpreso da quanto sia difficile questo problema, dato che è stato assegnato in un corso chiamato "Introduzione agli algoritmi". Ma forse hai coperto un trucco in classe che lo rende banale.


Per il punto uno. Non è scritto nelle specifiche del problema, quindi non puoi supporre che. Inoltre, il problema non sta nel dire che non puoi modificare l'array o crearne di nuovi, puoi davvero. L'unica cosa che devi fare è trovare i numeri che sommati insieme ti danno la somma più grande che è divisibile per nella complessità temporale O ( n ) (di solito si presume solo la complessità temporale). nO(n)
nbro,

2
r1r2

-1

Il mio algoritmo proposto è il seguente:

Una somma è divisibile per n se aggiungi solo riassunti che sono multipli di n.

Prima di iniziare crei una hashmap con un int come chiave e un elenco di indici come valore. È inoltre possibile creare un elenco di risultati contenente indici.

Quindi passare in rassegna l'array e aggiungere tutti gli indici che mod n è zero all'elenco dei risultati. Per ogni altro indice, procedi come segue:

Sottrai il valore mod n di questo indice da n. Questo risultato è la chiave per la tua hashmap che memorizza gli indici per gli elementi con il valore richiesto. Ora aggiungi questo indice all'elenco nella hashmap e vai avanti.

Dopo aver terminato il loop sull'array, si calcola l'output. Puoi farlo ordinando ogni elenco nella hashmap in base al valore a cui punta l'indice. Ora consideri ogni coppia nella hashmap sommando fino a n. Quindi se n = 7 cerchi l'hashmap per 3 e 4. Se hai una voce in entrambi prendi i due valori più grandi rimuovili dai loro elenchi e aggiungili al tuo elenco dei risultati.

Ultima raccomandazione: non ho ancora testato l'algoritmo, scrivete un testcase su di esso usando un algoritmo a forza bruta.


2
Avido, lineare, non funzionante. Consideri solo elementi che sono divisibili per n e coppie divisibili per n, che dire di triple e altro? Non garantisce la somma massima del sottoinsieme per casi banali. [2, 1, 8] -> la somma massima è 9, ma l'algoritmo restituisce 3.
Evil

n2

Grazie per avermi segnalato questo errore. La mia idea sul miglioramento sarebbe quella di creare una hashmap di pile di elenchi che viene ordinata aumentando il valore e iniziare ad accumularsi solo dopo aver completato un passaggio attraverso l'array.
Tobias Würfl

Intendi array di array, che verranno ordinati e "hashmap" è% n? Devi ancora ordinarli e, se li hai ordinati, prendere il valore minimo / massimo è ok, ma c'è ancora una parte inevitabile nella scelta effettiva del sottoinsieme, che nel peggiore dei casi non ne trae beneficio. Ad ogni modo se hai qualche miglioramento, forse potresti modificare il post?
Evil

Sì, è stata un'idea abbastanza veloce con le pile. In effetti hai solo bisogno di elenchi nella hashmap che ordini. Non ero sicuro che fosse educato modificare la mia prima risposta. Dopo tutto, ho fatto un errore nel mio primo tentativo.
Tobias Würfl

-2

utilizzare questo metodo DP da ( /programming/4487438/ma maximum - sum- of-non- consecutive - elements ? rq=1 ):

Dato un array A [0..n], sia M (i) la soluzione ottimale usando gli elementi con indici 0..i. Quindi M (-1) = 0 (utilizzato nella ricorrenza), M (0) = A [0] e M (i) = max (M (i - 1), M (i - 2) + A [i ]) per i = 1, ..., n. M (n) è la soluzione che vogliamo. Questo è O (n) . È possibile utilizzare un altro array per memorizzare la scelta effettuata per ciascun sottoproblema e quindi ripristinare gli elementi effettivi scelti.

Cambia la ricorsione in M ​​(i) = max (M (i - 1), M (i - 2) + A [i]) tale che sia memorizzato solo se è divisibile per N


2
Questo non funziona - ti farò capire perché. (Suggerimento: prova a eseguirlo sull'array costante 1). Inoltre, in questo problema consentiamo elementi consecutivi.
Yuval Filmus

1
Questa è un'ottima soluzione, solo per un problema completamente diverso (e molto più semplice).
Evil
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.