Maximum Subarray: Teile und herrsche
Disclaimer: dies ist für eine Zuordnung. Ich bin nicht zu Fragen, für die explizit programmiert werden, es müssen nur genug helfen zu verstehen, die der Algorithmus beteiligt, so dass ich vielleicht beheben Sie die Fehler in meinem code.
Okay, also du bist wahrscheinlich vertraut mit der maximum-subarray problem: Berechnung und Rückgabe der größte zusammenhängende block von ganzen zahlen in einem array. Einfach genug, aber diese Zuordnung verlangt von mir zu tun es in drei verschiedenen Komplexitäten: O(n^3), O(n^2) und O(n log n). Ich habe die ersten beiden ohne viel Mühe (brute force) aber die Dritte gibt mir Kopfschmerzen ... buchstäblich.
Ich verstehen, wie der Algorithmus funktionieren soll: ein array an eine Funktion übergeben, teilt es in zwei Hälften rekursiv, vergleicht dann die einzelnen Komponenten zu finden, die maximale subarray in jeder Hälfte. Dann, da die maximale subarray muss, befindet sich ganz in der linken oder rechten Hälfte, oder in einem segment überlappen die beiden, Sie müssen das maximale subarray, die überlappungen Links und rechts. Vergleichen Sie die max subarrays für jeden Fall, und die größte wird Ihren Wert zurückgeben.
Ich glaube, ich habe code geschrieben, der führt, der Aufgabe angemessen, aber ich scheine falsch zu sein in der Bewertung. Ich habe versucht, an den Lehrer und TA für die Hilfe, aber ich habe nicht das Gefühl, dass ich immer überall mit, entweder von Ihnen. Unten ist der code hab ich es geschafft zu schreiben so weit. Bitte sagen Sie mir, wenn Sie irgendwelche eklatante Fehler. Nochmal, ich bin nicht auf der Suche nach expliziten code oder Antworten, aber helfen, zu verstehen, was ich falsch mache. Ich habe gesucht durch alle ähnliche Fälle hier vorgestellt und habe nicht gefunden was kann mir wirklich helfen. Habe ich auch getan, viele der Google-Suche nach Orientierung, aber auch das hilft nicht viel, entweder. Wie auch immer, hier ist der code in Frage:
int conquer(int arr[], int first, int mid, int last) {
int i = 0;
int maxLeft = 0;
int maxRight = 0;
int temp = 0;
for (i = mid; i >= first; i--) {
temp = temp + arr[i];
if (maxLeft < temp) {
maxLeft = temp;
}
}
temp = 0;
for (i = (mid + 1); i <= last; i++) {
temp = temp + arr[i];
if (maxRight < temp) {
maxRight = temp;
}
}
return (maxLeft + maxRight);
}
int divide(int arr[], int start, int end) {
int i;
int maxSum;
int maxLeftSum;
int maxRightSum;
int maxOverlapSum;
if (start == end) {
return arr[start];
} else {
int first = start;
int mid = (end / 2);
int last = end;
maxSum = 0;
maxLeftSum = 0;
for (i = first; i < mid; i++) {
maxSum = maxSum + arr[i];
if (maxLeftSum < maxSum) {
maxLeftSum = maxSum;
}
}
for (i = (mid + 1); i < last; i++) {
maxSum = maxSum + arr[i];
if (maxRightSum < maxSum) {
maxRightSum = maxSum;
}
}
maxOverlapSum = conquer(arr, first, mid, last);
}
if ((maxLeftSum > maxRightSum) && (maxLeftSum > maxOverlapSum)) {
return maxLeftSum;
} else if ((maxRightSum > maxLeftSum) && (maxRightSum > maxOverlapSum)) {
return maxRightSum;
} else
return maxOverlapSum;
}
Edit: die Fehler, die ich bin immer ein Falsches Ergebnis. Ich habe konsistente und korrekte Ergebnisse zwischen meine beiden anderen algorithmen, aber das ist falsch.
Edit #2: Hier ist eine aktualisierte version von meinem code abgespeckt ein bisschen und ich wurden ein paar Dinge. Es ist immer noch nicht korrekt läuft, sollte es doch viel näher...
#include <stdio.h>
#include <stdlib.h>
int numarr[] = {22, -27, 38, -34, 49, 40, 13, -44, -13, 28, 46, 7, -26, 42,
29, 0, -6, 35, 23, -37, 10, 12, -2, 18, -12, -49, -10, 37, -5, 17,
6, -11, -22, -17, -50, -40, 44, 14, -41, 19, -15, 45, -23, 48, -1,
-39, -46, 15, 3, -32, -29, -48, -19, 27, -33, -8, 11, 21, -43, 24,
5, 34, -36, -9, 16, -31, -7, -24, -47, -14, -16, -18, 39, -30, 33,
-45, -38, 41, -3, 4, -25, 20, -35, 32, 26, 47, 2, -4, 8, 9, 31, -28,
36, 1, -21, 30, 43, 25, -20, -42};
int length = ((sizeof(numarr))/(sizeof(int)));
int divide(int left, int right) {
int mid, i, temp, mLeft, mRight, mCross = 0;
if (left == right) {
return left;
} else if (left > right) {
return 0;
} else {
mid = (left + right) / 2;
divide(left, mid);
divide(mid + 1, right);
for (i = mid; i >= left; i--) {
temp = temp + numarr[i];
if (mLeft < temp) {
mLeft = temp;
}
}
for (i = mid + 1; i <= right; i++) {
temp = temp + numarr[i];
if (mRight < temp) {
mRight = temp;
}
}
mCross = (mLeft + mRight);
printf("mLeft: %d\n", mLeft);
printf("mRight: %d\n", mRight);
printf("mCross: %d\n", mCross);
return 0;
}
}
int main(int argc, char const *argv[])
{
divide(0, length);
return 0;
}
- Ich wünschte, ich hatte Zeit, diese zu beantworten, aber ich bin zu begeben Sie sich auf eine Stunde+ pendeln. Ich musste nur erwähnen, das ist einfach einer der schönsten vorgestellt Hausaufgaben Aufgaben, die ich gesehen habe, auf SO eine lange Zeit. Oh, und im Gegensatz zu den meisten Studenten, Ihre code-Formatierung, Namensgebung, etc, ist pro. Seine lesbar, ohne Kommentare, das sagt viel. Viel Glück.
- Sie implementieren müssen, um die Kluft Schritt. Ihre aktuelle Implementierung nur berechnet die überschneidung Summe für die Letzte iteration, so verpassen Sie die überlappung Summen der kleinere sub-arrays.
- Ich gehe davon aus, dass Sie Ihnen eine Antwort, wenn das so ist, bin ich gespannt, was Sie behaupten es ist. Es würde helfen zu wissen, wenn dein Algorithmus wird schließlich auf allen Zylindern.
- Ich weiß nicht, ob Sie, aber ich werde Sie wissen lassen, wenn ich es herausfinden. Ich bin fast sicher, ich habe den Algorithmus jetzt, so ist es nur eine Frage der Reinigung meiner Umsetzung, um es arbeiten ordnungsgemäß. Ich bin in der Nähe, dass, obwohl!
- Ich habe 223 für die Antwort, aber die Anzeige der partition, wenn kam, ist... anstrengenderen =P
- Ich glaube, die Antwort ist 239. Mein Lehrer gab uns verschiedene sets testen Sie unsere algorithmen, und die Antworten, damit wir wissen würden, wenn Sie richtig umgesetzt werden. Meine anderen algorithmen berechnet 239 auch so bin ich sehr sicher, dass seine Antwort richtig ist. Die neueste version dieser Implementierung ist, mich immer zu 254, also bin ich wirklich in der Nähe...
- sind Sie richtig. Es ist in der Tat, 239. Ich hatte einen Fehler in meiner Links-Kreuzung Schleife, die sich bewegte von
0..mid-1
eher als Mitte-1..0
ich gepostet habe hier eine Antwort wenn Sie interessiert sind, es zu sehen. Es basierte ursprünglich auf dem code. Es wird deutlich anders als dein code, dass ich den 0-basierten array mit jeder partition wie ich es beschrieben kurz in meiner Antwort weiter unten (siehemidsplit()
Probe). Ich auch pass basiert offset können Sie sehen, welche partition ergab die Summen. Vielen Dank für den Spaß, sir. kewl, problem, code für. - Einfach nur neugierig; könnten Sie bitte die O(n^2) und O(n^3) Ansatz-code?
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich bin immer noch starrte auf Ihr problem, aber ich bemerkte einige Fehler fast sofort.
Ersten, wenn
first
undlast
sind so etwas wie Ihre Namen wollen, finden Sie den Mittelpunkt falsch. Sie tun dies:wenn es sollte so sein:
Weiter, Ihre erste enumeration Schleife läuft von
[first,mid)
(Hinweis: der Ausschluss vonmid
auf der rechten Seite). Diese Schleife wird nicht gehören diearr[mid]
element:Ihre zweite läuft von
[mid+1,last)
, die auch nicht diearr[mid]
element:Diese lassen Sie ein Loch von einem element, insbesondere
arr[mid]
. Jetzt bin ich nicht behauptet, ich verstehe den Algorithmus, wie ich Sie gerade hatte kaum eine chance, es zu Lesen, aber wenn du hast mit der Abdeckung der gesamten Palette von[first,last)
, ist dies wahrscheinlich nicht gehen, um es zu tun. Auch das lehrbuch Algorithmus vorgestellt wie er durch das Papier, verbunden durch SauceMaster ist der Nachteil, mit einer Sprache, die nicht zulässt, dass Sie den Versatz in ein array übergeben und per Zeiger-Zerfall in einen Funktionsaufruf als die Basis-Adresse des Arrays. C erlaubt dies zu tun, und Sie sollten es nutzen. Ich denke, Sie werden feststellen, es macht das zahlen einfacher zu verstehen, und beseitigt die Notwendigkeit für einen Ihr übergebenen Indizes.Beispiel: eine Funktion, der ein array und Mitte spaltet und bezieht, könnte wie folgt Aussehen:
In jeder Rekursion, ist der split-Punkt immer das Ende einer Reihe, und verwendet, um die offset-Adresse der zweiten Reihe, die behandelt wird als seine eigene 0-basiertes array in der rekursiven Aufrufs. Keine Ahnung, ob Sie verwenden können, aber es macht es ein wenig einfacher (zumindest für mich) zu begreifen.
Schließlich Ihren teilen nicht zu sein scheinen viel recursing, dass ich sehen kann, was zu einem problem werden, da diese, nachdem alle, ist ein rekursiver Algorithmus. Es scheint, Sie sind fehlt mindestens ein Anruf
divide()
dort.Vielleicht habe ich etwas verpasst, das wäre nicht das erste mal, aber wie gesagt, ich habe nicht Gießen zu viel in es (noch) nicht.
John Bentley veröffentlichte ein Papier auf, das im Jahr 1984. Sie finden die PDF kostenlos online: http://www.akira.ruc.dk/~keld/Lehre/algoritmedesign_f03/Artikler/05/Bentley84.pdf
Er beginnt die Diskussion über die O(n log n) - Ansatz auf der zweiten Seite.
Ich glaube, Sie haben fast alle den code, den Sie brauchen, aber diese zwei Fragen stehen für mich:
mid
variable Berechnung vermuten.divide
Funktion ist nicht wirklich tun, jede Division.In eine rekursive Formulierung der Aufteilung eines zu erobern, würden Sie rekursiv aufrufen Ihre teilen-Funktion auf der unteren Hälfte des Arrays, und dann auf der oberen Hälfte des Arrays. Der conquer-Schritt könnte als der code berechnet die überschneidung Summe und gibt die größte der drei Kandidaten.
Bearbeiten: Beim nachdenken über das problem rekursiv, erkennen die Funktion und nutzen es. In diesem Fall, die
divide
Funktion gibt das maximum-sub-array-Summe für die zur Verfügung gestellten array. Daher der Weise zu berechnenmaxLeftSum
ist zu nennen, teilen Sie auf der linken sub-array. Ebenso fürmaxRightSum
.Glück!
mid
Berechnung. Ich habe es geändert, bei WhozCraig Vorschlag zu: -mid = (start + end / 2)
Bezüglich derdivide
Funktion, ich bin immer noch eine harte Zeit zu denken über diese rekursiv. Ich hatte rekursive Aufrufe in ursprünglich, aber versehentlich gelöschte Ihnen, und wusste nicht, waren Sie verschwunden. Das heißt, ich bin nicht ganz sicher, wo Sie zu setzen. Wenn ich Sie vor dem Schleifen, wird das Schleifen immer noch gültig? Wenn ich Sie nach dem Schleifen, Sie sind nutzlos, da die loops haben bereits iteriert durch den ganzen array-segment.Ich denke, Sie konzentrieren sich nur auf die Kreuzung subarray Teil.es gibt jedoch auch die linke subarray Teil und rechts subarray Teil,und Sie sowohl die Möglichkeit haben, die größer ist als die Kreuzung subarray .
Weil Englisch ist nicht meine erste Sprache, und ich bin nicht gut darin.ich bin nicht davon zu überzeugen, ich habe express genau das, was ich Ausdrücken will,so werde ich einfügen mein code.
hier ist mein code :