Babeș-Bolyai Universität Cluj Napoca Fakultät für Mathematik und Informatik Grundlagen der Programmierung MLG5005 Seminar XI/XII Greedy Greedy Algorithmen sind anwendbar bei Konstruktionsaufgaben zum Finden einer optimalen Struktur. Sie finden eine Lösung, die sie schrittweise aufbauen, ohne zurückzusetzen. Es werden dabei nicht mehr Teillösungen konstruiert als unbedingt nötig. Bestandteile 1. eine sich verbrauchende Kandidatenliste 2. eine sich aufbauende Resultatliste 3. Eine Auswahlfunktion, die aus der Kandidatenliste den jeweils besten Teil auswählt 4. Eine Lösungfunktion, die kontrolliert, ob die aktuelle Resultatliste eine Lösung des Problems darstellt 5. Eine Okayfunktion, die kontrolliert, ob die Erweiterung einer Resultatliste durch den nächsten Kandidaten zulässig ist 6. Eine Zeilfunktion, die den Wert einer Lösung angibt • • Greedy Algorithmus ist sehr einfach und schnell zu realisieren unter Umständen ist die Lösung nicht optimal 1. Das Geldwechselproblem. Ein Geldbetrag soll mit der kleinstmöglichen Zahl von Münzen gegeben werden. def solve(coins, n): result = [] coins.sort() while n > 0: p = len(coins) - 1 while p > 0 and coins[p] > n: p -= 1 result.append(coins[p]) n -= coins[p] return result coins = [1, 5, 10, 50] for i in range(1, 1000): assert sum(solve(coins, i)) == I 2. Das Problem des Handlungsreisenden. Gegeben ist ein gewichteter, ungerichteter Graph. Knoten repräsentieren Städte, Kanten die mit Kosten gewichteten Verkehswege zwischen den Städten. Gesucht ist ein Tour durch den Graph, bei der jeden Knoten besucht wird und die am Ausgangsknoten endet, derart, dass die Kosten minimal sind. Greedy: die Strategie der kürzesten Verbindung → ABDBCE 3. Ein Scheduling Problem. Gegeben wird n Jobs der Größen s1, …, sn und n Prozessoren. Gesucht ist eine Abbildung der Jobs auf die Prozessoren mit minimaler Ausführungszeit. Beispiel: Jobs (min): 3, 5, 6, 10, 11, 14, 15, 18, 20 Prozessoren: 3 Greedy: Total 35 min Optimal: Total 34 min Divide et Impera Die Strategie Divide et Impera zur Lösung eines Problems, dessen Eingabe die Größe n hat, besteht in folgender Idee: a) Zerlege das Problem in mehrere Probleme der gleichen Art wie das ursprüngliche, so daß 1. die Eingabe jedes dieser Teilprobleme kleiner als n ist, 2. die Lösung des ursprünglichen Problems aus den Lösungen der Teilprobleme berechnet werden kann. b) Löse jedes der Teilprobleme 1. entweder durch weitere Zerlegung wie in a) 2. oder direkt (das Problem ist trivial) 1. Finde die kleinste Zahl einer List. def diMin(list): if len(list) == 1: return list[0] m = len(list) / 2 return min(diMin(list[:m]), diMin(list[m:])) print diMin(l) 2. Berechne den größten gemeinsamen Teiler. def scmmdc(a,b): while a!=b: if a>b: a-=b else: b-=a return a def cmmdc(x): if len(x)==1: return x[0] a = cmmdc(x[0:len(x)/2]) b = cmmdc(x[len(x)/2:]) return scmmdc(a,b) def testCmmdc(): d = {} d[2] = [2,4,6,8,22] d[12] = [12,48,60,1200] d[10] = [10,2000] for k in d.keys(): assert cmmdc(d[k]) == k testCmmdc() 3. Das Maximum-Subarray Problem. Gegeben ist eine Folge X von ganzen Zahlen im Array. Gesucht ist die Maximale Summe einer zusammenhängenden Teilfolge von X. Beispiel: eingabe: [-2, -5, 6, -2, -3, 1, 5, -6] ausgabe: Max sum: 7 T(n) ∈O(n*n*n) def maxSubArraySumVerySlow(arr): max = arr[0] for i in range(0, len(arr)): for j in range(i, len(arr)): s=0 for k in range(i, j+1): s+=arr[k] if s>max: max = s return max T(n) ∈O(n*n) def maxSubArraySumSlow(arr): max = arr[0] for i in range(0, len(arr)): s = 0 for j in range(i, len(arr)): s+=arr[j] if s>max: max=s return max Divide et Impera: T(n) ∈ O(n*log(n)) def maxCrossingSum(arr, l, m, h): sum = 0 i = m left_sum = -1000 while i >= l: sum = sum + arr[i] i-=1 if sum > left_sum: left_sum = sum; sum = 0 i=m+1 right_sum = -1000 while i<=h: sum = sum + arr[i] i+=1 if sum > right_sum: right_sum = sum; return left_sum + right_sum; def maxSubArraySum(arr, l, h): if l == h: return arr[l] m = (l + h)/2 return max(maxSubArraySum(arr, l, m), maxSubArraySum(arr, m+1, h), maxCrossingSum(arr, l, m, h)) print "Max sum: " + str(maxSubArraySum(arr, 0, len(arr)-1)) import unittest class SubArrayTest(unittest.TestCase): def test(self): self.assertEquals(maxSubArraySum([1], 0, 0), 1, "Array :"+str(arr)) self.assertEquals(maxSubArraySum([1, 1], 0, 1), 2, "Array :"+str(arr)) self.assertEquals(maxSubArraySum([1, -10, 1], 0, 2), 1, "Array :"+str(arr)) self.assertEquals(maxSubArraySum([-2, -5, 6, -2, -3, 1, 5, -6], 0, 7), 7, "Array :"+str(arr)) Komplexität: T(n) = 2T(n/2) + n T(n) ∈ O(n*log(n))