|
|
|
@ -130,13 +130,13 @@ def rucksack_branch_and_bound_algorithm(
|
|
|
|
|
while not S.empty(): |
|
|
|
|
# top-Element auslesen und Bound berechnen: |
|
|
|
|
A: Mask = S.top(); |
|
|
|
|
bound_subtree, choice, order_, pad = estimate_lower_bound(mask=A, max_cost=max_cost, costs=costs, values=values, items=items); |
|
|
|
|
bound_subtree, choice, order_, state = estimate_lower_bound(mask=A, max_cost=max_cost, costs=costs, values=values, items=items); |
|
|
|
|
|
|
|
|
|
# für logging (irrelevant für Algorithmus): |
|
|
|
|
if verbose: |
|
|
|
|
step = Step(bound=bound, bound_subtree=bound_subtree, stack_str=str(S), choice=choice, order=order_, indexes=A.indexes_unset, pad=pad); |
|
|
|
|
step = Step(bound=bound, bound_subtree=bound_subtree, stack_str=str(S), choice=choice, order=order_, indexes=A.indexes_unset, solution=state); |
|
|
|
|
if bound_subtree < bound: |
|
|
|
|
if not A.splittable() or pad != MaskValue.UNSET: |
|
|
|
|
if state is not None: |
|
|
|
|
step.move = EnumBranchAndBoundMove.BOUND; |
|
|
|
|
step.bound = bound_subtree; |
|
|
|
|
else: |
|
|
|
@ -147,12 +147,9 @@ def rucksack_branch_and_bound_algorithm(
|
|
|
|
|
# Update nur nötig, wenn die (eingeschätzte) untere Schranke von A das bisherige Minimum verbessert: |
|
|
|
|
if bound_subtree < bound: |
|
|
|
|
# Bound aktualisieren, wenn sich A nicht weiter aufteilen od. wenn sich A wie eine einelementige Option behandeln läst: |
|
|
|
|
if not A.splittable() or pad != MaskValue.UNSET: |
|
|
|
|
if state is not None: |
|
|
|
|
bound = bound_subtree; |
|
|
|
|
# falls A als einelementige Menge betrachtet werden kann, ersetze unbekannte Werte: |
|
|
|
|
if pad != MaskValue.UNSET: |
|
|
|
|
A = A.pad(pad); |
|
|
|
|
mask = A; |
|
|
|
|
mask = state; |
|
|
|
|
# Branch sonst |
|
|
|
|
else: |
|
|
|
|
B, C = A.split(); |
|
|
|
@ -207,7 +204,7 @@ def estimate_lower_bound(
|
|
|
|
|
costs: np.ndarray, |
|
|
|
|
values: np.ndarray, |
|
|
|
|
items: np.ndarray, |
|
|
|
|
) -> Tuple[float, List[Fraction], List[int], MaskValue]: |
|
|
|
|
) -> Tuple[float, List[Fraction], List[int], Optional[Mask]]: |
|
|
|
|
''' |
|
|
|
|
Wenn partielle Information über den Rucksack festgelegt ist, |
|
|
|
|
kann man bei dem unbekannten Teil das Rucksack-Problem |
|
|
|
@ -229,14 +226,17 @@ def estimate_lower_bound(
|
|
|
|
|
|
|
|
|
|
# Für Rest des Rucksacks (Items mit unbekanntem Status): |
|
|
|
|
cost_rest = max_cost - cost_rucksack; |
|
|
|
|
pad = MaskValue.UNSET; |
|
|
|
|
state = None; |
|
|
|
|
# Prüfe, ob man als Lösung alles/nichts hinzufügen kann: |
|
|
|
|
if len(indexes_unset) > 0 and sum(costs[indexes_unset]) <= cost_rest: |
|
|
|
|
pad = MaskValue.ONE; |
|
|
|
|
if len(indexes_unset) == 0: |
|
|
|
|
state = mask; |
|
|
|
|
value_rest = 0; |
|
|
|
|
elif sum(costs[indexes_unset]) <= cost_rest: |
|
|
|
|
state = mask.pad(MaskValue.ONE); |
|
|
|
|
choice[indexes_unset] = Fraction(1); |
|
|
|
|
value_rest = sum(values[indexes_unset]); |
|
|
|
|
elif len(indexes_unset) > 0 and min(costs[indexes_unset]) > cost_rest: |
|
|
|
|
pad = MaskValue.ZERO; |
|
|
|
|
elif min(costs[indexes_unset]) > cost_rest: |
|
|
|
|
state = mask.pad(MaskValue.ZERO); |
|
|
|
|
choice[indexes_unset] = Fraction(0); |
|
|
|
|
value_rest = 0; |
|
|
|
|
# Sonst mit Greedy-Algorithmus lösen: |
|
|
|
@ -259,4 +259,4 @@ def estimate_lower_bound(
|
|
|
|
|
value_max_est = value_rucksack + value_rest; |
|
|
|
|
|
|
|
|
|
# Ausgabe mit -1 multiplizieren (weil maximiert wird): |
|
|
|
|
return -value_max_est, choice.tolist(), order.tolist(), pad; |
|
|
|
|
return -value_max_est, choice.tolist(), order.tolist(), state; |
|
|
|
|