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:
RD 2022-06-14 14:40:02 +02:00
parent e3c3bbec37
commit 3b8f80cff9
12 changed files with 169 additions and 147 deletions

View File

@ -78,18 +78,20 @@
- &rucksack_1 - &rucksack_1
name: RUCKSACK name: RUCKSACK
algorithm: GREEDY algorithm: GREEDY
allow-fractional: true allow-fractional: false
capacity: 10 max-cost: 10
items: [a, b, c, d, e] items: [a, b, c, d, e]
weights: costs:
[3, 4, 5, 2, 1] [3, 4, 5, 2, 1]
values: values:
[8, 7, 8, 3, 2] [8, 7, 8, 3, 2]
- <<: *rucksack_1
allow-fractional: true
- <<: *rucksack_1 - <<: *rucksack_1
algorithm: BRANCH-AND-BOUND algorithm: BRANCH-AND-BOUND
- name: RUCKSACK - name: RUCKSACK
algorithm: BRANCH-AND-BOUND algorithm: BRANCH-AND-BOUND
capacity: 90 max-cost: 90
items: [ items: [
'Sonnenblumenkerne', 'Sonnenblumenkerne',
'Buchweizen', 'Buchweizen',
@ -97,7 +99,7 @@
'Hirse', 'Hirse',
'Sellerie', 'Sellerie',
] ]
weights: costs:
[30, 10, 50, 10, 80] [30, 10, 50, 10, 80]
values: values:
[17, 14, 17, 5, 25] [17, 14, 17, 5, 25]

View File

@ -0,0 +1,14 @@
# CommandRucksack
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**algorithm** | [**EnumRucksackAlgorithm**](EnumRucksackAlgorithm.md) | | [default to null]
**allowMinusfractional** | [**Boolean**](boolean.md) | | [optional] [default to false]
**maxMinuscost** | [**BigDecimal**](number.md) | Upper bound for total cost of rucksack. | [default to null]
**costs** | [**List**](number.md) | Array of cost for each item (e.g. volume, weight, price, time, etc.). | [default to null]
**values** | [**List**](number.md) | Value extracted from each item (e.g. energy, profit, etc.). | [default to null]
**items** | [**List**](string.md) | Optional names of the items (if empty, defaults to 1-based indexes). | [optional] [default to []]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -5,7 +5,7 @@ Name | Type | Description | Notes
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
**name** | [**EnumAlgorithmNames**](EnumAlgorithmNames.md) | | [default to null] **name** | [**EnumAlgorithmNames**](EnumAlgorithmNames.md) | | [default to null]
**dist** | [**List**](array.md) | | [default to null] **dist** | [**List**](array.md) | | [default to null]
**optimise** | [**EnumTSPOptimise**](EnumTSPOptimise.md) | | [default to null] **optimise** | [**EnumOptimiseMode**](EnumOptimiseMode.md) | | [default to null]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -1,4 +1,4 @@
# EnumTSPOptimise # EnumOptimiseMode
## Properties ## Properties
Name | Type | Description | Notes Name | Type | Description | Notes

View File

@ -0,0 +1,8 @@
# EnumRucksackAlgorithm
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -14,10 +14,12 @@ Class | Method | HTTP request | Description
- [Command](.//Models/Command.md) - [Command](.//Models/Command.md)
- [CommandHirschberg](.//Models/CommandHirschberg.md) - [CommandHirschberg](.//Models/CommandHirschberg.md)
- [CommandRucksack](.//Models/CommandRucksack.md)
- [CommandTarjan](.//Models/CommandTarjan.md) - [CommandTarjan](.//Models/CommandTarjan.md)
- [CommandTsp](.//Models/CommandTsp.md) - [CommandTsp](.//Models/CommandTsp.md)
- [EnumAlgorithmNames](.//Models/EnumAlgorithmNames.md) - [EnumAlgorithmNames](.//Models/EnumAlgorithmNames.md)
- [EnumTSPOptimise](.//Models/EnumTSPOptimise.md) - [EnumOptimiseMode](.//Models/EnumOptimiseMode.md)
- [EnumRucksackAlgorithm](.//Models/EnumRucksackAlgorithm.md)
<a name="documentation-for-authorization"></a> <a name="documentation-for-authorization"></a>

View File

@ -8,6 +8,7 @@ Name | Type | Description | Notes
**tarjan** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [default to null] **tarjan** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [default to null]
**tsp** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [default to null] **tsp** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [default to null]
**hirschberg** | [**AppOptions_hirschberg**](AppOptions_hirschberg.md) | | [default to null] **hirschberg** | [**AppOptions_hirschberg**](AppOptions_hirschberg.md) | | [default to null]
**rucksack** | [**AppOptions_tarjan**](AppOptions_tarjan.md) | | [optional] [default to null]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md) [[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@ -116,8 +116,8 @@ components:
type: object type: object
required: required:
- algorithm - algorithm
- capacity - max-cost
- weights - costs
- values - values
properties: properties:
algorithm: algorithm:
@ -125,18 +125,18 @@ components:
allow-fractional: allow-fractional:
type: boolean type: boolean
default: false default: false
capacity: max-cost:
description: Maximum weight/volumed allowed in rucksack. description: Upper bound for total cost of rucksack.
type: number type: number
minimum: 0 minimum: 0
weights: costs:
description: Weights or volumes of each item. description: Array of cost for each item (e.g. volume, weight, price, time, etc.).
type: array type: array
items: items:
type: number type: number
exclusiveMinimum: 0 exclusiveMinimum: 0
values: values:
description: Value extracted from each item (e.g. monetary). description: Value extracted from each item (e.g. energy, profit, etc.).
type: array type: array
items: items:
type: number type: number

View File

@ -28,8 +28,8 @@ __all__ = [
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def rucksack_greedy_algorithm( def rucksack_greedy_algorithm(
capacity: float, max_cost: float,
weights: np.ndarray, costs: np.ndarray,
values: np.ndarray, values: np.ndarray,
items: np.ndarray, items: np.ndarray,
fractional: bool, fractional: bool,
@ -43,12 +43,11 @@ def rucksack_greedy_algorithm(
eine obere Schranke des maximalen Wertes beim Originalproblem. eine obere Schranke des maximalen Wertes beim Originalproblem.
''' '''
# sortiere daten: # sortiere daten:
order = resort_by_value_per_weight(weights=weights, values=values, items=items); order = get_sort_order(costs=costs, values=values);
uorder = iperm(order);
# verbose output hier behandeln (irrelevant für Algorithmus): # verbose output hier behandeln (irrelevant für Algorithmus):
if verbose: 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('');
print('\x1b[1mRucksack Problem - Greedy\x1b[0m'); print('\x1b[1mRucksack Problem - Greedy\x1b[0m');
print(''); print('');
@ -56,46 +55,39 @@ def rucksack_greedy_algorithm(
print(''); print('');
# führe greedy aus: # führe greedy aus:
n = len(weights); n = len(costs);
weight_total = 0; cost_total = 0;
vector = [ 0 for _ in range(n) ]; 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 # füge Item i hinzu, solange das Gesamtgewicht noch <= Schranke
if weight_total + weights[i] <= capacity: if cost_total + costs[i] <= max_cost:
weight_total += weights[i]; cost_total += costs[i];
vector[i] = 1; vector[i] = 1;
# sonst abbrechen. Falls Bruchteile erlaubt, füge einen Bruchteil des i. Items hinzu # falls Bruchteile erlaubt sind, füge einen Bruchteil des i. Items hinzu und abbrechen
else: elif fractional:
if fractional: vector[i] = (max_cost - cost_total)/costs[i];
vector[i] = (capacity - weight_total)/weights[i];
break; break;
# ansonsten weiter machen:
else:
continue;
# Aspekte der Lösung speichern: # Aspekte der Lösung speichern:
rucksack = [i for i, v in enumerate(vector) if v > 0]; # Indexes von Items im Rucksack rucksack = [i for i, v in enumerate(vector) if v > 0]; # Indexes von Items im Rucksack
soln = Solution( soln = Solution(
vector = vector, vector = vector,
items = items[rucksack].tolist(), items = items[rucksack].tolist(),
weights = weights[rucksack].tolist(), costs = costs[rucksack].tolist(),
values = values[rucksack].tolist(), values = values[rucksack].tolist(),
); );
# verbose output hier behandeln (irrelevant für Algorithmus): # verbose output hier behandeln (irrelevant für Algorithmus):
if verbose: if verbose:
# Umsortierung rückgängig machen: repr_rucksack = display_rucksack(items=items[rucksack], costs=costs[rucksack], values=values[rucksack]);
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);
print('\x1b[1mEingeschätzte Lösung\x1b[0m'); print('\x1b[1mEingeschätzte Lösung\x1b[0m');
print(''); print('');
if fractional: print(f'Mask: {soln.vector}');
print(f'Mask: {soln.vector}'); print('Rucksack:')
print(f' ---> {vector} (unter urspr. Sortierung)'); print(repr_rucksack);
print(f'Rucksack: {", ".join(items[rucksack])}.');
print(f'max. Value ≈ {expr_value}');
print(f'∑ Weights = {expr_weight}');
print(''); print('');
# Lösung ausgeben # Lösung ausgeben
@ -106,8 +98,8 @@ def rucksack_greedy_algorithm(
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def rucksack_branch_and_bound_algorithm( def rucksack_branch_and_bound_algorithm(
capacity: float, max_cost: float,
weights: np.ndarray, costs: np.ndarray,
values: np.ndarray, values: np.ndarray,
items: np.ndarray, items: np.ndarray,
verbose: bool, verbose: bool,
@ -117,12 +109,11 @@ def rucksack_branch_and_bound_algorithm(
unter Rücksicht der Kapizitätsschranke exakt und effizienter bestimmt. unter Rücksicht der Kapizitätsschranke exakt und effizienter bestimmt.
''' '''
order = resort_by_value_per_weight(weights=weights, values=values, items=items); order = get_sort_order(costs=costs, values=values);
uorder = iperm(order);
# verbose output hier behandeln (irrelevant für Algorithmus): # verbose output hier behandeln (irrelevant für Algorithmus):
if verbose: 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('');
print('\x1b[1mRucksack Problem - Branch & Bound\x1b[0m'); print('\x1b[1mRucksack Problem - Branch & Bound\x1b[0m');
print(''); print('');
@ -130,12 +121,12 @@ def rucksack_branch_and_bound_algorithm(
print(''); print('');
logged_steps = []; logged_steps = [];
vector = empty_mask(n=len(weights)); vector = empty_mask(n=len(costs));
lb_estimate = np.inf; lb_estimate = np.inf;
S = Stack(); S = Stack();
S.push(vector); S.push(vector);
while not S.empty(): 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: if verbose:
logged_steps.append((lb_estimate, lb, str(S), u, can_add_all, can_add_none)); 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: # 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(); B, C = A.split();
S.push(B); S.push(B);
# Nur dann C auf Stack legen, wenn mind. eine Möglichkeit in C die Kapazitätsschranke erfüllt: # 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); S.push(C);
# Aspekte der Lösung speichern # Aspekte der Lösung speichern
@ -164,29 +155,20 @@ def rucksack_branch_and_bound_algorithm(
vector = vector.decision, vector = vector.decision,
items = items[rucksack].tolist(), items = items[rucksack].tolist(),
values = values[rucksack].tolist(), values = values[rucksack].tolist(),
weights = weights[rucksack].tolist(), costs = costs[rucksack].tolist(),
); );
# verbose output hier behandeln (irrelevant für Algorithmus): # verbose output hier behandeln (irrelevant für Algorithmus):
if verbose: if verbose:
# NOTE: Information in Tabelle gemäß permutierten Daten: repr = display_branch_and_bound(values=values, steps=logged_steps, order=order);
repr = display_branch_and_bound(values=values, steps=logged_steps); repr_rucksack = display_rucksack(items=items[rucksack], costs=costs[rucksack], values=values[rucksack]);
# 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);
print('\x1b[1mLösung\x1b[0m'); print('\x1b[1mLösung\x1b[0m');
print(''); print('');
print(repr); print(repr);
print(''); print('');
print(f'Mask: {soln.vector}'); print(f'Mask: {soln.vector}');
print(f' ---> {vector} (unter urspr. Sortierung)'); print('Rucksack:');
print(f'Rucksack: {", ".join(items[rucksack])}.'); print(repr_rucksack);
print(f'max. Value ≈ {expr_value}');
print(f'∑ Weights = {expr_weight}');
print(''); print('');
# Lösung ausgeben # Lösung ausgeben
@ -196,35 +178,19 @@ def rucksack_branch_and_bound_algorithm(
# AUXILIARY METHOD resort # AUXILIARY METHOD resort
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def resort_by_value_per_weight( def get_sort_order(costs: np.ndarray, values: np.ndarray) -> List[int]:
weights: np.ndarray,
values: np.ndarray,
items: 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)); indexes = list(range(n));
order = sorted(indexes, key=lambda i: -values[i]/weights[i]); order = sorted(indexes, key=lambda i: -values[i]/costs[i]);
permute_data(weights=weights, values=values, items=items, perm=order);
return order; 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( def estimate_lower_bound(
mask: Mask, mask: Mask,
capacity: float, max_cost: float,
weights: np.ndarray, costs: np.ndarray,
values: np.ndarray, values: np.ndarray,
items: np.ndarray, items: np.ndarray,
) -> Tuple[float, List[float], bool]: ) -> Tuple[float, List[float], bool]:
@ -243,19 +209,19 @@ def estimate_lower_bound(
# Berechnungen bei Items mit bekanntem Status in Rucksack: # Berechnungen bei Items mit bekanntem Status in Rucksack:
value_rucksack = sum(values[indexes_one]); value_rucksack = sum(values[indexes_one]);
weight_rucksack = sum(weights[indexes_one]); cost_rucksack = sum(costs[indexes_one]);
vector[indexes_one] = 1; vector[indexes_one] = 1;
# Für Rest des Rucksacks (Items mit unbekanntem Status): # 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_all = False;
can_add_none = False; can_add_none = False;
# Prüfe, ob man als Lösung alles/nichts hinzufügen kann: # 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; can_add_all = True;
vector[indexes_unset] = 1; vector[indexes_unset] = 1;
value_rest = sum(values[indexes_unset]); 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; can_add_none = True;
vector[indexes_unset] = 0; vector[indexes_unset] = 0;
value_rest = 0; value_rest = 0;
@ -263,8 +229,8 @@ def estimate_lower_bound(
# NOTE: Lösung ist eine Überschätzung des max-Wertes. # NOTE: Lösung ist eine Überschätzung des max-Wertes.
else: else:
soln_rest = rucksack_greedy_algorithm( soln_rest = rucksack_greedy_algorithm(
capacity = weight_rest, # <- Kapazität = Restgewicht max_cost = cost_rest, # <- Kapazität = Restgewicht
weights = weights[indexes_unset], costs = costs[indexes_unset],
values = values[indexes_unset], values = values[indexes_unset],
items = items[indexes_unset], items = items[indexes_unset],
fractional = True, fractional = True,

View File

@ -17,8 +17,9 @@ from src.models.stacks import *;
__all__ = [ __all__ = [
'display_order', 'display_order',
'display_sum', 'display_rucksack',
'display_branch_and_bound', 'display_branch_and_bound',
'display_sum',
]; ];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -27,31 +28,88 @@ __all__ = [
def display_order( def display_order(
order: List[int], order: List[int],
weights: np.ndarray, costs: np.ndarray,
values: np.ndarray, values: np.ndarray,
items: np.ndarray, items: np.ndarray,
one_based: bool = False, one_based: bool = False,
) -> str: ) -> str:
table = pd.DataFrame({ table = pd.DataFrame({
'items': items, 'items': items,
'order': order, 'order': order,
'values': values, 'values': values,
'weights': weights, 'costs': costs,
'u': (values/weights), 'u': (values/costs),
}) \ }) \
.reset_index(drop=True); .reset_index(drop=True);
if one_based: if one_based:
table['order'] += 1; table['order'] += 1;
# benutze pandas-Dataframe + tabulate, um schöner darzustellen: # benutze pandas-Dataframe + tabulate, um schöner darzustellen:
repr = tabulate( repr = tabulate(
pd.DataFrame(table), table,
headers=['item', 'greedy order', 'value', 'weight', 'value/weight'], headers=['item', 'greedy order', 'value', 'cost', 'value/cost'],
showindex=False, showindex=False,
colalign=('left', 'center', 'center', 'center', 'right'), colalign=('left', 'center', 'center', 'center', 'right'),
tablefmt='rst' tablefmt='rst'
); );
return repr; 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 # METHOD display sum
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -59,47 +117,18 @@ def display_order(
def display_sum( def display_sum(
vector: List[float], vector: List[float],
values: np.ndarray, values: np.ndarray,
order: Optional[List[int]] = None,
as_maximum: bool = True, as_maximum: bool = True,
) -> str: ) -> 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([ expr = '+'.join([
f'{x:g}' if u == 1 else f'{Fraction(str(u))}·{x:g}' 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: if as_maximum:
return f'{value:g} = {expr}'; return f'{value:g} = {expr}';
else: else:
return f'-{value:g} = -({expr})'; 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;

View File

@ -27,15 +27,15 @@ __all__ = [
@run_safely() @run_safely()
def endpoint_rucksack(command: CommandRucksack) -> Result[CallResult, CallError]: def endpoint_rucksack(command: CommandRucksack) -> Result[CallResult, CallError]:
n = len(command.weights); n = len(command.costs);
assert len(command.values) == n, f'Number of weights must be {n}'; assert len(command.values) == n, 'Number of values and costs must coincide!';
assert len(command.items) in [0, n], f'Number of items must be 0 or {n}'; assert len(command.items) in [0, n], f'Number of items must be 0 or {n}!';
command.items = command.items or [ str(index + 1) for index in range(n) ]; command.items = command.items or [ str(index + 1) for index in range(n) ];
match command.algorithm: match command.algorithm:
case EnumRucksackAlgorithm.greedy: case EnumRucksackAlgorithm.greedy:
result = rucksack_greedy_algorithm( result = rucksack_greedy_algorithm(
capacity = command.capacity, max_cost = command.max_cost,
weights = np.asarray(command.weights[:]), costs = np.asarray(command.costs[:]),
values = np.asarray(command.values[:]), values = np.asarray(command.values[:]),
items = np.asarray(command.items[:]), items = np.asarray(command.items[:]),
fractional = command.allow_fractional, fractional = command.allow_fractional,
@ -43,8 +43,8 @@ def endpoint_rucksack(command: CommandRucksack) -> Result[CallResult, CallError]
); );
case EnumRucksackAlgorithm.branch_and_bound: case EnumRucksackAlgorithm.branch_and_bound:
result = rucksack_branch_and_bound_algorithm( result = rucksack_branch_and_bound_algorithm(
capacity = command.capacity, max_cost = command.max_cost,
weights = np.asarray(command.weights[:]), costs = np.asarray(command.costs[:]),
values = np.asarray(command.values[:]), values = np.asarray(command.values[:]),
items = np.asarray(command.items[:]), items = np.asarray(command.items[:]),
verbose = config.OPTIONS.rucksack.verbose, verbose = config.OPTIONS.rucksack.verbose,

View File

@ -24,10 +24,10 @@ __all__ = [
@dataclass @dataclass
class SolutionRaw(): class SolutionRaw():
vector: Union[List[int], List[float]] = field(); vector: List[float] = field();
items: List[str] = field(); items: List[str] = field();
values: List[float] = field(repr=False); values: List[float] = field(repr=False);
weights: List[float] = field(repr=False); costs: List[float] = field(repr=False);
class Solution(SolutionRaw): class Solution(SolutionRaw):
@property @property
@ -40,7 +40,7 @@ class Solution(SolutionRaw):
@property @property
def total_weight(self) -> float: def total_weight(self) -> float:
return sum([ self.vector[i]*x for (i, x) in zip(self.support, self.weights) ]); return sum([ self.vector[i]*x for (i, x) in zip(self.support, self.costs) ]);
@property @property
def total_value(self) -> float: def total_value(self) -> float: