diff --git a/code/python/src/algorithms/rucksack/algorithms.py b/code/python/src/algorithms/rucksack/algorithms.py index b85a3e9..d774a99 100644 --- a/code/python/src/algorithms/rucksack/algorithms.py +++ b/code/python/src/algorithms/rucksack/algorithms.py @@ -137,12 +137,11 @@ def rucksack_branch_and_bound_algorithm( while not S.empty(): lb, u, can_add_all, can_add_none = estimate_lower_bound(mask=S.top(), capacity=capacity, weights=weights, values=values, items=items); if verbose: - logged_steps.append((lb_estimate, lb, u, str(S))); + logged_steps.append((lb_estimate, lb, str(S), u, can_add_all, can_add_none)); # Update nur nötig, wenn die (eingeschätzte) untere Schranke von A das bisherige Minimum verbessert: A: Mask = S.pop(); if lb < lb_estimate: # Bound, wenn sich A nicht weiter aufteilen lässt od. man A wie eine einelementige Option behandeln kann: - # Branch sonst if not A.splittable() or can_add_all or can_add_none: lb_estimate = lb; if can_add_all: @@ -151,9 +150,9 @@ def rucksack_branch_and_bound_algorithm( vector = A.pad_zeros(); else: vector = A; + # Branch sonst else: B, C = A.split(); - # NOTE: per Wahl erfüllt mind. eine Möglichkeit in B die Kapazitätsschranke. S.push(B); # Nur dann C auf Stack legen, wenn mind. eine Möglichkeit in C die Kapazitätsschranke erfüllt: if sum(weights[C.indexes_one]) <= capacity: @@ -237,35 +236,45 @@ def estimate_lower_bound( NOTE: Diese Funktion wird `g(vector)` im Skript bezeichnet. ''' - - # Berechnungen bei Items mit bekanntem Status in Rucksack: indexes_one = mask.indexes_one; - weight_rucksack = sum(weights[indexes_one]); - value_rucksack = sum(values[indexes_one]); - - # Löse mit Greedy-Algorithmus auf Items mit unbekanntem Status. - # NOTE: Lösung ist eine Überschätzung des max-Wertes. indexes_unset = mask.indexes_unset; - soln_rest = rucksack_greedy_algorithm( - capacity = capacity - weight_rucksack, # <- Kapazität = Restgewicht - weights = weights[indexes_unset], - values = values[indexes_unset], - items = items[indexes_unset], - fractional = True, - verbose = False, - ); - value_rest = soln_rest.total_value; - - # Lösung des [0,1]-Vektors speichern: n = len(mask); vector = np.zeros(shape=(n,), dtype=float); + + # Berechnungen bei Items mit bekanntem Status in Rucksack: + value_rucksack = sum(values[indexes_one]); + weight_rucksack = sum(weights[indexes_one]); vector[indexes_one] = 1; - vector[indexes_unset] = soln_rest.vector; - # Prüfe, ob man als Lösung alles hinzufügen kann: - can_add_all = all([m == 1 for m in soln_rest.vector]); - can_add_none = all([m == 0 for m in soln_rest.vector]); + # Für Rest des Rucksacks (Items mit unbekanntem Status): + weight_rest = capacity - weight_rucksack; + can_add_all = False; + can_add_none = False; + # Prüfe, ob man als Lösung alles/nichts hinzufügen kann: + if len(indexes_unset) > 0 and sum(weights[indexes_unset]) <= weight_rest: + can_add_all = True; + vector[indexes_unset] = 1; + value_rest = sum(values[indexes_unset]); + elif len(indexes_unset) > 0 and min(weights[indexes_unset]) > weight_rest: + can_add_none = True; + vector[indexes_unset] = 0; + value_rest = 0; + # Sonst mit Greedy-Algorithmus lösen: + # NOTE: Lösung ist eine Überschätzung des max-Wertes. + else: + soln_rest = rucksack_greedy_algorithm( + capacity = weight_rest, # <- Kapazität = Restgewicht + weights = weights[indexes_unset], + values = values[indexes_unset], + items = items[indexes_unset], + fractional = True, + verbose = False, + ); + value_rest = soln_rest.total_value; + vector[indexes_unset] = soln_rest.vector; - # Einschätzung des max-Wertes (Ausgabe mit -1 multiplizieren): + # Einschätzung des max-Wertes: value_max_est = value_rucksack + value_rest; + + # Ausgabe mit -1 multiplizieren (weil maximiert wird): return -value_max_est, vector.tolist(), can_add_all, can_add_none; diff --git a/code/python/src/algorithms/rucksack/display.py b/code/python/src/algorithms/rucksack/display.py index 38934e4..649eb43 100644 --- a/code/python/src/algorithms/rucksack/display.py +++ b/code/python/src/algorithms/rucksack/display.py @@ -57,7 +57,7 @@ def display_order( # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ def display_sum( - vector: Union[List[int], List[float]], + vector: List[float], values: np.ndarray, as_maximum: bool = True, ) -> str: @@ -77,27 +77,29 @@ def display_sum( def display_branch_and_bound( values: np.ndarray, - steps: List[Tuple[float, float, Stack]] + steps: List[Tuple[float, float, Stack, List[float], bool, bool]] ) -> str: # füge Summen-Ausdrücke für Greedy-Alg hinzu: rows = []; used_vectors = []; - for lb_estimate, lb, u, S in steps: + for lb_estimate, lb, S, u, can_add_all, can_add_none in steps: + pad = '1' if can_add_all else ('0' if can_add_none else ''); if u in used_vectors: - rows.append((f'{lb_estimate:g}', f'{lb:g}', S)); + expr = f'{lb:g}'; else: used_vectors.append(u) - rows.append((f'{lb_estimate:g}', display_sum(vector=u, values=values, as_maximum=False), S)); + expr = display_sum(vector=u, values=values, as_maximum=False); + rows.append((f'{lb_estimate:g}', expr, pad, S)); table = pd.DataFrame(rows) \ - .rename(columns={0: 'b', 1: 'g(TOP(S))', 2: 'S'}) \ + .rename(columns={0: 'b', 1: 'g(TOP(S))', 2: 'pad?', 3: 'S'}) \ .reset_index(drop=True); # benutze pandas-Dataframe + tabulate, um schöner darzustellen: repr = tabulate( pd.DataFrame(table), - headers=['b', 'g(TOP(S))', 'S'], + headers=['b', 'g(TOP(S))', 'pad?', 'S'], showindex=False, - colalign=('left', 'left', 'right'), + colalign=('left', 'left', 'center', 'right'), tablefmt='rst' ); return repr; diff --git a/code/python/src/models/rucksack/mask.py b/code/python/src/models/rucksack/mask.py index 606da01..37c19c8 100644 --- a/code/python/src/models/rucksack/mask.py +++ b/code/python/src/models/rucksack/mask.py @@ -88,5 +88,9 @@ class Mask(): ''' return Mask([ MaskValue.ONE if u == MaskValue.UNSET else u for u in self.values ]); + @property + def support(self) -> List[int]: + return [ i for i, v in enumerate(self.values) if v == MaskValue.ONE ]; + def empty_mask(n: int): return Mask([MaskValue.UNSET for _ in range(n)]);