master > master: code py - verbesserte Darstellung + »korrekte« Behandlung von Reihenfolgen
- im Kurs wird die Permutation nur für Greedy-Berechnungen angewandt - die Reihenfolge der Items in der Hauptberechnung bei B&B bleibt wie bei Angaben
This commit is contained in:
@@ -28,8 +28,8 @@ __all__ = [
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def rucksack_greedy_algorithm(
|
||||
capacity: float,
|
||||
weights: np.ndarray,
|
||||
max_cost: float,
|
||||
costs: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
fractional: bool,
|
||||
@@ -43,12 +43,11 @@ def rucksack_greedy_algorithm(
|
||||
eine obere Schranke des maximalen Wertes beim Originalproblem.
|
||||
'''
|
||||
# sortiere daten:
|
||||
order = resort_by_value_per_weight(weights=weights, values=values, items=items);
|
||||
uorder = iperm(order);
|
||||
order = get_sort_order(costs=costs, values=values);
|
||||
|
||||
# verbose output hier behandeln (irrelevant für Algorithmus):
|
||||
if verbose:
|
||||
repr = display_order(order=order, weights=weights[uorder], values=values[uorder], items=items[uorder], one_based=True);
|
||||
repr = display_order(order=order, costs=costs, values=values, items=items, one_based=True);
|
||||
print('');
|
||||
print('\x1b[1mRucksack Problem - Greedy\x1b[0m');
|
||||
print('');
|
||||
@@ -56,46 +55,39 @@ def rucksack_greedy_algorithm(
|
||||
print('');
|
||||
|
||||
# führe greedy aus:
|
||||
n = len(weights);
|
||||
weight_total = 0;
|
||||
n = len(costs);
|
||||
cost_total = 0;
|
||||
vector = [ 0 for _ in range(n) ];
|
||||
for i in range(n):
|
||||
for i in order:
|
||||
# füge Item i hinzu, solange das Gesamtgewicht noch <= Schranke
|
||||
if weight_total + weights[i] <= capacity:
|
||||
weight_total += weights[i];
|
||||
if cost_total + costs[i] <= max_cost:
|
||||
cost_total += costs[i];
|
||||
vector[i] = 1;
|
||||
# sonst abbrechen. Falls Bruchteile erlaubt, füge einen Bruchteil des i. Items hinzu
|
||||
else:
|
||||
if fractional:
|
||||
vector[i] = (capacity - weight_total)/weights[i];
|
||||
# falls Bruchteile erlaubt sind, füge einen Bruchteil des i. Items hinzu und abbrechen
|
||||
elif fractional:
|
||||
vector[i] = (max_cost - cost_total)/costs[i];
|
||||
break;
|
||||
# ansonsten weiter machen:
|
||||
else:
|
||||
continue;
|
||||
|
||||
# Aspekte der Lösung speichern:
|
||||
rucksack = [i for i, v in enumerate(vector) if v > 0]; # Indexes von Items im Rucksack
|
||||
soln = Solution(
|
||||
vector = vector,
|
||||
items = items[rucksack].tolist(),
|
||||
weights = weights[rucksack].tolist(),
|
||||
costs = costs[rucksack].tolist(),
|
||||
values = values[rucksack].tolist(),
|
||||
);
|
||||
|
||||
# verbose output hier behandeln (irrelevant für Algorithmus):
|
||||
if verbose:
|
||||
# Umsortierung rückgängig machen:
|
||||
vector = [ soln.vector[i] for i in order ];
|
||||
rucksack = sorted([ uorder[r] for r in rucksack ]);
|
||||
permute_data(weights=weights, values=values, items=items, perm=uorder);
|
||||
# Ausdrücke bestimmen:
|
||||
expr_value = display_sum(vector=vector, values=values);
|
||||
expr_weight = display_sum(vector=vector, values=weights);
|
||||
repr_rucksack = display_rucksack(items=items[rucksack], costs=costs[rucksack], values=values[rucksack]);
|
||||
print('\x1b[1mEingeschätzte Lösung\x1b[0m');
|
||||
print('');
|
||||
if fractional:
|
||||
print(f'Mask: {soln.vector}');
|
||||
print(f' ---> {vector} (unter urspr. Sortierung)');
|
||||
print(f'Rucksack: {", ".join(items[rucksack])}.');
|
||||
print(f'max. Value ≈ {expr_value}');
|
||||
print(f'∑ Weights = {expr_weight}');
|
||||
print(f'Mask: {soln.vector}');
|
||||
print('Rucksack:')
|
||||
print(repr_rucksack);
|
||||
print('');
|
||||
|
||||
# Lösung ausgeben
|
||||
@@ -106,8 +98,8 @@ def rucksack_greedy_algorithm(
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def rucksack_branch_and_bound_algorithm(
|
||||
capacity: float,
|
||||
weights: np.ndarray,
|
||||
max_cost: float,
|
||||
costs: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
verbose: bool,
|
||||
@@ -117,12 +109,11 @@ def rucksack_branch_and_bound_algorithm(
|
||||
unter Rücksicht der Kapizitätsschranke exakt und effizienter bestimmt.
|
||||
'''
|
||||
|
||||
order = resort_by_value_per_weight(weights=weights, values=values, items=items);
|
||||
uorder = iperm(order);
|
||||
order = get_sort_order(costs=costs, values=values);
|
||||
|
||||
# verbose output hier behandeln (irrelevant für Algorithmus):
|
||||
if verbose:
|
||||
repr = display_order(order=order, weights=weights[uorder], values=values[uorder], items=items[uorder], one_based=True);
|
||||
repr = display_order(order=order, costs=costs, values=values, items=items, one_based=True);
|
||||
print('');
|
||||
print('\x1b[1mRucksack Problem - Branch & Bound\x1b[0m');
|
||||
print('');
|
||||
@@ -130,12 +121,12 @@ def rucksack_branch_and_bound_algorithm(
|
||||
print('');
|
||||
|
||||
logged_steps = [];
|
||||
vector = empty_mask(n=len(weights));
|
||||
vector = empty_mask(n=len(costs));
|
||||
lb_estimate = np.inf;
|
||||
S = Stack();
|
||||
S.push(vector);
|
||||
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);
|
||||
lb, u, can_add_all, can_add_none = estimate_lower_bound(mask=S.top(), max_cost=max_cost, costs=costs, values=values, items=items);
|
||||
if verbose:
|
||||
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:
|
||||
@@ -155,7 +146,7 @@ def rucksack_branch_and_bound_algorithm(
|
||||
B, C = A.split();
|
||||
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:
|
||||
if sum(costs[C.indexes_one]) <= max_cost:
|
||||
S.push(C);
|
||||
|
||||
# Aspekte der Lösung speichern
|
||||
@@ -164,29 +155,20 @@ def rucksack_branch_and_bound_algorithm(
|
||||
vector = vector.decision,
|
||||
items = items[rucksack].tolist(),
|
||||
values = values[rucksack].tolist(),
|
||||
weights = weights[rucksack].tolist(),
|
||||
costs = costs[rucksack].tolist(),
|
||||
);
|
||||
|
||||
# verbose output hier behandeln (irrelevant für Algorithmus):
|
||||
if verbose:
|
||||
# NOTE: Information in Tabelle gemäß permutierten Daten:
|
||||
repr = display_branch_and_bound(values=values, steps=logged_steps);
|
||||
# Umsortierung rückgängig machen:
|
||||
vector = [ soln.vector[i] for i in order ];
|
||||
rucksack = sorted([ uorder[r] for r in rucksack ]);
|
||||
permute_data(weights=weights, values=values, items=items, perm=uorder);
|
||||
# Ausdrücke bestimmen:
|
||||
expr_value = display_sum(vector=vector, values=values);
|
||||
expr_weight = display_sum(vector=vector, values=weights);
|
||||
repr = display_branch_and_bound(values=values, steps=logged_steps, order=order);
|
||||
repr_rucksack = display_rucksack(items=items[rucksack], costs=costs[rucksack], values=values[rucksack]);
|
||||
print('\x1b[1mLösung\x1b[0m');
|
||||
print('');
|
||||
print(repr);
|
||||
print('');
|
||||
print(f'Mask: {soln.vector}');
|
||||
print(f' ---> {vector} (unter urspr. Sortierung)');
|
||||
print(f'Rucksack: {", ".join(items[rucksack])}.');
|
||||
print(f'max. Value ≈ {expr_value}');
|
||||
print(f'∑ Weights = {expr_weight}');
|
||||
print('Rucksack:');
|
||||
print(repr_rucksack);
|
||||
print('');
|
||||
|
||||
# Lösung ausgeben
|
||||
@@ -196,35 +178,19 @@ def rucksack_branch_and_bound_algorithm(
|
||||
# AUXILIARY METHOD resort
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def resort_by_value_per_weight(
|
||||
weights: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
) -> List[int]:
|
||||
def get_sort_order(costs: np.ndarray, values: np.ndarray) -> List[int]:
|
||||
'''
|
||||
Sortiert Daten absteigend nach values/weights.
|
||||
Sortiert Daten absteigend nach values/costs.
|
||||
'''
|
||||
n = len(weights);
|
||||
n = len(costs);
|
||||
indexes = list(range(n));
|
||||
order = sorted(indexes, key=lambda i: -values[i]/weights[i]);
|
||||
permute_data(weights=weights, values=values, items=items, perm=order);
|
||||
order = sorted(indexes, key=lambda i: -values[i]/costs[i]);
|
||||
return order;
|
||||
|
||||
def permute_data(
|
||||
weights: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
perm: List[int],
|
||||
):
|
||||
weights[:] = weights[perm];
|
||||
values[:] = values[perm];
|
||||
items[:] = items[perm];
|
||||
return;
|
||||
|
||||
def estimate_lower_bound(
|
||||
mask: Mask,
|
||||
capacity: float,
|
||||
weights: np.ndarray,
|
||||
max_cost: float,
|
||||
costs: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
) -> Tuple[float, List[float], bool]:
|
||||
@@ -243,19 +209,19 @@ def estimate_lower_bound(
|
||||
|
||||
# Berechnungen bei Items mit bekanntem Status in Rucksack:
|
||||
value_rucksack = sum(values[indexes_one]);
|
||||
weight_rucksack = sum(weights[indexes_one]);
|
||||
cost_rucksack = sum(costs[indexes_one]);
|
||||
vector[indexes_one] = 1;
|
||||
|
||||
# Für Rest des Rucksacks (Items mit unbekanntem Status):
|
||||
weight_rest = capacity - weight_rucksack;
|
||||
cost_rest = max_cost - cost_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:
|
||||
if len(indexes_unset) > 0 and sum(costs[indexes_unset]) <= cost_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:
|
||||
elif len(indexes_unset) > 0 and min(costs[indexes_unset]) > cost_rest:
|
||||
can_add_none = True;
|
||||
vector[indexes_unset] = 0;
|
||||
value_rest = 0;
|
||||
@@ -263,8 +229,8 @@ def estimate_lower_bound(
|
||||
# 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],
|
||||
max_cost = cost_rest, # <- Kapazität = Restgewicht
|
||||
costs = costs[indexes_unset],
|
||||
values = values[indexes_unset],
|
||||
items = items[indexes_unset],
|
||||
fractional = True,
|
||||
|
||||
@@ -17,8 +17,9 @@ from src.models.stacks import *;
|
||||
|
||||
__all__ = [
|
||||
'display_order',
|
||||
'display_sum',
|
||||
'display_rucksack',
|
||||
'display_branch_and_bound',
|
||||
'display_sum',
|
||||
];
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -27,31 +28,88 @@ __all__ = [
|
||||
|
||||
def display_order(
|
||||
order: List[int],
|
||||
weights: np.ndarray,
|
||||
costs: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
one_based: bool = False,
|
||||
) -> str:
|
||||
table = pd.DataFrame({
|
||||
'items': items,
|
||||
'order': order,
|
||||
'values': values,
|
||||
'weights': weights,
|
||||
'u': (values/weights),
|
||||
'items': items,
|
||||
'order': order,
|
||||
'values': values,
|
||||
'costs': costs,
|
||||
'u': (values/costs),
|
||||
}) \
|
||||
.reset_index(drop=True);
|
||||
if one_based:
|
||||
table['order'] += 1;
|
||||
# benutze pandas-Dataframe + tabulate, um schöner darzustellen:
|
||||
repr = tabulate(
|
||||
pd.DataFrame(table),
|
||||
headers=['item', 'greedy order', 'value', 'weight', 'value/weight'],
|
||||
table,
|
||||
headers=['item', 'greedy order', 'value', 'cost', 'value/cost'],
|
||||
showindex=False,
|
||||
colalign=('left', 'center', 'center', 'center', 'right'),
|
||||
tablefmt='rst'
|
||||
);
|
||||
return repr;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHOD display rucksack
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def display_rucksack(
|
||||
items: np.ndarray,
|
||||
costs: np.ndarray,
|
||||
values: np.ndarray,
|
||||
) -> str:
|
||||
table = pd.DataFrame({
|
||||
'items': items.tolist() + ['----', '∑'],
|
||||
'costs': costs.tolist() + ['', f'\x1b[92;1m{sum(costs)}\x1b[0m'],
|
||||
'values': values.tolist() + ['', f'\x1b[92;1m{sum(values)}\x1b[0m'],
|
||||
});
|
||||
repr = tabulate(
|
||||
table,
|
||||
headers=['item', 'cost', 'value'],
|
||||
showindex=False,
|
||||
colalign=('left', 'center', 'center'),
|
||||
tablefmt='rst'
|
||||
);
|
||||
return repr;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHOD display result of branch and bound
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def display_branch_and_bound(
|
||||
values: np.ndarray,
|
||||
steps: List[Tuple[float, float, Stack, List[float], bool, bool]],
|
||||
order: Optional[List[int]] = None,
|
||||
) -> str:
|
||||
# füge Summen-Ausdrücke für Greedy-Alg hinzu:
|
||||
rows = [];
|
||||
used_vectors = [];
|
||||
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:
|
||||
expr = f'{lb:g}';
|
||||
else:
|
||||
used_vectors.append(u)
|
||||
expr = display_sum(vector=u, values=values, as_maximum=False, order=order);
|
||||
rows.append((f'{lb_estimate:g}', expr, pad, S));
|
||||
|
||||
table = pd.DataFrame(rows) \
|
||||
.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(
|
||||
table,
|
||||
headers=['b', 'g(TOP(S))', 'pad?', 'S'],
|
||||
showindex=False,
|
||||
colalign=('left', 'left', 'center', 'right'),
|
||||
tablefmt='rst'
|
||||
);
|
||||
return repr;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHOD display sum
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@@ -59,47 +117,18 @@ def display_order(
|
||||
def display_sum(
|
||||
vector: List[float],
|
||||
values: np.ndarray,
|
||||
order: Optional[List[int]] = None,
|
||||
as_maximum: bool = True,
|
||||
) -> str:
|
||||
value = sum([ u*x for u, x in zip(vector,values)]);
|
||||
parts = [ (u, x) for u, x in zip(vector, values)];
|
||||
if not (order is None):
|
||||
parts = [ parts[j] for j in order ];
|
||||
value = sum([ u*x for u, x in parts]);
|
||||
expr = '+'.join([
|
||||
f'{x:g}' if u == 1 else f'{Fraction(str(u))}·{x:g}'
|
||||
for u, x in zip(vector,values) if u > 0
|
||||
for u, x in parts if u > 0
|
||||
]);
|
||||
if as_maximum:
|
||||
return f'{value:g} = {expr}';
|
||||
else:
|
||||
return f'-{value:g} = -({expr})';
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHOD display result of branch and bound
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def display_branch_and_bound(
|
||||
values: np.ndarray,
|
||||
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, 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:
|
||||
expr = f'{lb:g}';
|
||||
else:
|
||||
used_vectors.append(u)
|
||||
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: '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))', 'pad?', 'S'],
|
||||
showindex=False,
|
||||
colalign=('left', 'left', 'center', 'right'),
|
||||
tablefmt='rst'
|
||||
);
|
||||
return repr;
|
||||
|
||||
Reference in New Issue
Block a user