En typisk lösning finns på sid 165. Den går ut på att låta ett undre index, l, stega igenom alla element i sekvensen. För varje l låter vi ett övre index, h, stega igenom alla högre index och samtidigt beräkna alla delsekvensers summor. Eftersom den består av två nästlade loopar är storleksordningen av dess komplexitetsfunktion O(n2). Det är knappast acceptabelt men snabbare går det inte att hitta en lösning om alla möjliga delsekvensers summor ska beräknas.
Vi ska nu se hur problemet kan lösas snabbare med hjälp av söndra och härska (denna lösning finns på sid 167). Först ska det delas upp i mindre delproblem (söndra). Detta görs genom att dela sekvensen i två halvor. Den sökta delsekvensen kan nu finnas på tre olika ställen:
Vi betraktar först fall tre. Här går det att undvika nästlade loopar eftersom en sekvens som börjar i den ena halvan och slutar i den andra måste innehålla både det översta elemntet i den nedre sekvensen och det nedersta elementet i den övre sekvensen. Vi tar fram den största delsumman i den undre halvan genom att loopa från det översta elemntet i den nedre sekvensen och nedåt och beräkna alla delsummor. Sedan loopar vi på samma sätt genom den övre delsekvensen. Genom att summera dessa båda delsummor får vi den största summan som börjar i den ena halvan och slutar i den andra. Denna lösning består av två loopar som inte är nästlade, dess komplexitetsfunktion är alltså av storleksordningen O(2n) = O(n).
Nu gäller det att hitta lösningar på fall ett och två. Det görs genom att fortsätta att dela upp dem i halvor och göra rekursiva anrop ända tills vi får delsekvenser som bara består av ett element. Den största summan i dessa minimala sekvenser är naturligtvis värdet av det enda elementet (eller 0 om elementet är negativt). Här är ett program med denna lösning, DivideAndConquer.c .
Nu gäller det lösa ut T(n) ur denna funktion. Vi börjar
med att dividera båda leden med n: T(n)/n = T(n/2)/(n/2) + 1.
Detta gäller för alla n som är tvåpotenser. Alltså
gäller alla dessa ekvationer:
T(n)/n = T(n/2)/(n/2) + 1
T(n/2)/(n/2) = T(n/4)/(n/4) + 1
T(n/4)/(n/4) = T(n/8)/(n/8) + 1
...
T(2)/2 = T(1)/1 + 1
Nu summerar vi dessa ekvationer. Det visar sig då att de flesta termerna
(tex T(n/2)/(n/2)) finns på båda sidor och alltså kan
strykas. Efter att ha strukit alla dessa termer återstår T(n)/n
= T(1)/1 + log2 n. log2 n är summan av alla 1:orna
eftersom det finns log2 n ekvationer. T(1) = 1 => T(n) = n
+ nlog n. Detta innebär att O(T(n)) = nlog n eftersom nlog n
växer snabbare än n när n växer.
Detta var ett typiskt exempel på hur exekveringshastigheten med hjälp av tekniken söndra och härska kunde ökas från O(n 2) till O(nlog n).
Vi har nu sett att om ett problem delas i två delproblem av halva
storleken som löses rekursivt och om det tar O(n) tid att exekvera funktionen
en gång så blir exekveringshastigheten O(nlog n). Men vad händer
om vi delar upp problemet i tre delproblem av halva storleken? Eller om vi
delar det i sju delproblem som har storleken en tredjedel och det tar O(n
2) tid att exekvera funktionen en gång? Här kommer en generell
formel för exekekveringstiden. Beviset liknar det ovan med vi tar inte
upp det här.
A är antalet delproblem, A >= 1.
B är storleken av delproblemen, om tex delproblemen är hälften
så stora som det ursprungliga problemet är B = 2. B > 1.
k anger att det tar O(nk) tid att exekvera funktionen en
gång.
Då gäller att O(T(n)), där T(n) = AT(n/B) + O(n
k ), är:
O(nlogBA) om A > Bk
O(nklogB n) om A = Bk
O(nk) om A < Bk
Vad innebär detta egentligen? I problemet med delsekvensens summa var A = 2, B = 2 och k = 1. Detta innebär att fall två ovan gäller och att O(T(n)) är n logn som vi redan sett. Om vi i stället varit tvugna att dela problemet i tre halvstora delproblem hade fall ett gällt och O(T(n)) hade varit nlog23 = n1,59. Det betyder att tiden för att exekvera funktionen en gång (termen O(n k)) saknar betydelse i detta fall så länge k < 1,59. Faktiskt finns k inte heller med i uttrycket för O(T(n)). Om däremot k hade varit större än 1,59 hade den termen tagit över och vi hade hamnat i fall tre där A och B saknar betydelse.
Avslutningsvis kan vi konstatera att O(T(n)) blir mindre i fall två och tre om k blir mindre. I fall ett är k redan så liten att O(n k) saknar betydelse. Då finns det ingen anledning att minska k ytterligare. I fall ett och två kan exekveringstiden minskas genom att A minskas eller B ökas. I fall ett saknar k betydelse.