Größte durch n teilbare Summe

16

Ich habe diese Frage auf StackOverflow gestellt , aber ich denke, hier ist ein geeigneterer Ort.

Dies ist ein Problem aus dem Kurs Einführung in Algorithmen :

Sie haben ein Array a mit n positiven ganzen Zahlen (das Array muss nicht sortiert oder die Elemente eindeutig sein). Schlagen Sie einen O(n) -Algorithmus vor, um die größte durch teilbare Summe von Elementen zu finden n.

Beispiel: a=[6,1,13,4,9,8,25],n=7 . Die Antwort ist 56 (mit Elementen 6,13,4,8,25 )

Mit dynamischer Programmierung und Speichern der größten Summe mit dem Rest 0 , 1 , 2 , ist es relativ einfach, sie in zu finden . . . , N - 1 .O(n2)0,1,2,...,n1

Wenn wir die Aufmerksamkeit auf eine zusammenhängende Folge von Elementen beschränken, ist es auch einfach, die optimale Folge in O(n) Zeit zu finden, indem Teilsummen modulo gespeichert werden n: sei S[i]=a[0]+a[1]++a[i] , für jeden Rest r merken Sie sich den größten Index j so dass , und dann für jedes Sie S [j] -S [i] wobei ji S [ j ] - S [ i ] jS[j]r(modn)iS[j]S[i]jist der Index, der r=S[i]modn .

Aber gibt es eine O(n) -Zeitlösung für den allgemeinen Fall? Vorschläge werden geschätzt! Ich denke, dies hat etwas mit linearer Algebra zu tun, aber ich bin mir nicht sicher, was genau.

Alternativ kann dies in O(nlogn) Zeit erfolgen?

Delta-Terminator
quelle
2
1. Sie haben genau die gleiche Frage zu Stack Overflow gestellt. Bitte nicht schreiben die gleiche Frage auf mehreren Seiten . Wir möchten nicht, dass mehrere Kopien auf mehreren SE-Sites herumlaufen. Wenn Sie keine akzeptable Antwort erhalten haben, ist es in Ordnung, Ihre Frage für die Migration auf eine andere Site zu kennzeichnen. Veröffentlichen Sie jedoch nicht dasselbe an einer anderen Stelle. 2. Können Sie einen Verweis / ein Zitat / einen Link auf das Lehrbuch oder den Kurs geben, in dem dies erschien? Wie sicher sind Sie, dass es eine O(n) -Zeitlösung gibt?
DW
5
Ist die Herausforderung an Ihrer Universität noch offen? Es wäre sehr hilfreich, den Link zum Kurs und die genaue Frage zu sehen, und wenn es wirklich und die Leute, die ihn vorbereitet haben, ihre Antwort erklären / veröffentlichen, wäre es großartig. O(n)
Evil
Es ist relativ einfach, es in O (n2) O (n2) zu finden, indem die größte Summe mit dem Rest 0,1,2, ..., n-10,1,2, ..., n-1 dynamisch programmiert und gespeichert wird. Könnten Sie das bitte etwas näher erläutern? Ich kann verstehen, wie das n-Quadrat sein würde, wenn wir nur zusammenhängende Elemente betrachten, aber mit nicht zusammenhängenden Elementen auch, wäre es nicht exponentiell in der Reihenfolge?
Nithish Inpursuit Ofhappiness

Antworten:

4

Hier sind ein paar zufällige Ideen:

  • Der Algorithmus zur dynamischen Programmierung kann umgedreht werden, um nach einer kleinsten Summe anstelle einer größten Summe zu suchen. Am Ende suchen Sie nur nach einer Summe, die zum Rest der Summe des gesamten Arrays kongruent ist, anstatt einer, die kongruent zu Null ist. Wenn wir die Elemente in aufsteigender Reihenfolge verarbeiten, kann der dynamische Algorithmus manchmal beendet werden, bevor das gesamte Array verarbeitet wird.

    Die Kosten wären wenn wir Elemente verarbeiten würden . Es gibt keine Untergrenze von für diesen Algorithmus, da wir nicht alle Elemente sortieren müssen. Es dauert nur , um die kleinsten Elemente zu erhalten.k Ω ( n log n ) O ( n log k ) kO(nk)kΩ(nlogn)O(nlogk)k

  • Wenn wir uns um die Menge mit der größten Größe kümmern würden, könnten wir anstelle der Menge mit der größten Summe eine auf einer schnellen Fouriertransformation basierende Polynommultiplikation verwenden, um das Problem in zu lösen Zeit. Ähnlich wie in 3SUM, wenn der Domänenbereich begrenzt ist. (Hinweis: Verwenden Sie wiederholtes Quadrieren, um eine binäre Suche durchzuführen. Andernfalls erhalten Sie wobei die Anzahl der ausgelassenen Elemente ist.)O ( n k ( log n ) ( log log n ) )O(n(logn)2(loglogn))O(nk(logn)(loglogn))k

  • Wenn ist und fast alle Reste ein Vielfaches eines der Faktoren sind, kann durch die Konzentration auf die Reste, die kein Vielfaches dieses Faktors sind, erhebliche Zeitersparnis erzielt werden.nnn

  • Wenn ein Rest rsehr häufig ist oder nur wenige Reste vorhanden sind, kann das Verfolgen der rInformationen über den nächsten offenen Steckplatz, wenn Sie von hier aus beginnen und weiter voranschreiten , eine Menge von Suchanfragen nach offenen Stellen ersparen Zeit.

  • Sie können einen Protokollfaktor rasieren, indem Sie nur die Erreichbarkeit nachverfolgen und Bitmasken verwenden (im umgedrehten dynamischen Algorithmus) und dann zurückverfolgen, sobald Sie den Zielrest erreicht haben.

  • Der dynamische Programmieralgorithmus kann sehr leicht parallel ausgeführt werden. Mit einem Prozessor für jeden Pufferslot können Sie zu . Alternativ können die Schaltkreistiefenkosten durch Verwendung der Breite und durch Teilen und Erobern der Aggregation anstelle der iterativen Aggregation bis hinunter zu .O ( n 2 ) O ( log 2 n )O(n)O(n2)O(log2n)

  • (Meta) Ich vermute sehr, dass es sich bei dem Problem, das Sie erhalten haben, um zusammenhängende Summen handelt. Wenn Sie auf das eigentliche Problem verweisen, ist dies leicht zu überprüfen. Ansonsten wundert es mich sehr, wie schwierig dieses Problem ist, da es in einem Kurs mit dem Titel "Einführung in Algorithmen" behandelt wurde. Aber vielleicht haben Sie im Unterricht einen Trick verdeckt, der es trivial macht.

Craig Gidney
quelle
Für Punkt eins. Es steht nicht in den Spezifikationen des Problems, also können Sie das nicht annehmen. Das Problem ist auch nicht, dass Sie das Array nicht ändern oder neue erstellen können. Das einzige, was Sie tun müssen, ist zu finden, dass die summierten Zahlen die größte Summe ergeben, die durch in Zeitkomplexität teilbar ist (normalerweise wird nur die Zeitkomplexität angenommen). O ( n )nO(n)
Nr.
2
@EvilJS Die Teilmenge mit der größten Summe mit dem Rest 0 entspricht der vollständigen Menge, nachdem die Teilmenge mit der kleinsten Summe mit dem Rest, der der Summe der vollständigen Menge entspricht, entfernt wurde. Die Suche nach einer kleinsten Summe, die zu kongruent ist, ist praktischer als die Suche nach einer größten Summe, die zu kongruent ist, da Sie abbrechen können, sobald Sie eine Lösung finden (wenn Sie Elemente in aufsteigender Reihenfolge verarbeiten), anstatt fortfahren zu müssen. r 2r1r2
Craig Gidney
-1

Mein vorgeschlagener Algorithmus lautet wie folgt:

Eine Summe ist durch n teilbar, wenn Sie nur Summanden addieren, die Vielfache von n sind.

Bevor Sie beginnen, erstellen Sie eine Hashmap mit einem int als Schlüssel und einer Liste von Indizes als Wert. Sie erstellen auch eine Ergebnisliste mit Indizes.

Anschließend durchlaufen Sie das Array und fügen Ihrer Ergebnisliste jeden Index hinzu, dessen Mod n Null ist. Für jeden anderen Index gehen Sie wie folgt vor:

Sie subtrahieren den Wert mod n dieses Index von n. Dieses Ergebnis ist der Schlüssel für Ihre Hashmap, die Indizes für Elemente mit dem erforderlichen Wert speichert. Jetzt fügen Sie diesen Index der Liste in der Hashmap hinzu und fahren fort.

Nachdem Sie das Array durchlaufen haben, berechnen Sie die Ausgabe. Dazu sortieren Sie jede Liste in der Hashmap nach dem Wert, auf den der Index zeigt. Nun betrachten Sie jedes Paar in der Hashmap, das sich zu n summiert. Wenn also n = 7 ist, durchsuchen Sie die Hashmap nach 3 und 4. Wenn Sie in beiden einen Eintrag erhalten haben, nehmen Sie die beiden größten Werte, entfernen Sie sie aus ihren Listen und fügen Sie sie Ihrer Ergebnisliste hinzu.

Letzte Empfehlung: habe den Algorithmus noch nicht getestet, schreibe einen Testfall mit einem Brute-Force-Algorithmus dagegen.

Tobias Würfl
quelle
2
Gierig, linear, funktioniert nicht. Sie betrachten nur Elemente, die durch n teilbar sind, und Paare, die durch n teilbar sind. Was ist mit Dreifachen und mehr? Es garantiert nicht die maximale Teilmengen-Summe im einfachen Fall. [2, 1, 8] -> Die maximale Summe ist 9, aber Ihr Algorithmus gibt 3 zurück.
Evil
@EvilJS Was ist mit deinem Sub- Algorithmus passiert ? n2
Delta-Terminator
Vielen Dank, dass Sie mich auf diesen Fehler aufmerksam gemacht haben. Meine Idee zur Verbesserung wäre, eine Hashmap von Listenstapeln zu erstellen, die nach steigendem Wert geordnet ist und erst nach Abschluss eines Durchgangs durch das Array zu akkumulieren beginnt.
Tobias Würfl
Du meinst Array von Arrays, die sortiert werden und "hashmap" ist% n? Sie müssen sie immer noch sortieren, und wenn Sie sie sortiert haben, ist es in Ordnung, einen Minimal- / Maximalwert zu nehmen, aber es ist unvermeidlich, tatsächlich eine Teilmenge auszuwählen, was im schlimmsten Fall keinen Nutzen bringt. Wie auch immer, wenn Sie einige Verbesserungen haben, könnten Sie vielleicht den Beitrag bearbeiten?
Evil
Ja, das war eine ziemlich schnelle Idee mit den Stacks. Tatsächlich benötigen Sie nur Listen in der Hashmap, die Sie sortieren. Ich war mir nicht sicher, ob es höflich ist, meine erste Antwort zu bearbeiten. Immerhin habe ich bei meinem ersten Versuch einen Fehler gemacht.
Tobias Würfl
-2

Verwenden Sie diese DP-Methode von ( /programming/4487438/maximum-sum-of-non-consecutive-elements?rq=1 ):

Bei gegebenem Array A [0..n] sei M (i) die optimale Lösung unter Verwendung der Elemente mit den Indizes 0..i. Dann ist M (-1) = 0 (in der Wiederholung verwendet), M (0) = A [0] und M (i) = max (M (i - 1), M (i - 2) + A [i ]) für i = 1, ..., n. M (n) ist die Lösung, die wir wollen. Das ist O (n) . Sie können ein anderes Array verwenden, um zu speichern, welche Auswahl für jedes Teilproblem getroffen wurde, und so die tatsächlich ausgewählten Elemente wiederherstellen.

Ändern Sie die Rekursion in M ​​(i) = max (M (i - 1), M (i - 2) + A [i]), so dass sie nur dann gespeichert wird, wenn sie durch N teilbar ist

Stecknadelkopf
quelle
2
Das funktioniert nicht - ich lasse Sie herausfinden, warum. (Hinweis: Versuchen Sie, es auf dem Konstanten-1-Array auszuführen.) Bei diesem Problem sind auch aufeinanderfolgende Elemente zulässig.
Yuval Filmus
1
Dies ist eine sehr gute Lösung für ein völlig anderes (und viel einfacheres) Problem.
Evil