|
|
|
@ -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;
|
|
|
|
|