master > master: code py - algorithmen für rucksackproblem
This commit is contained in:
17
code/python/src/algorithms/rucksack/__init__.py
Normal file
17
code/python/src/algorithms/rucksack/__init__.py
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from src.algorithms.rucksack.algorithms import *;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'rucksack_greedy_algorithm',
|
||||
'rucksack_branch_and_bound_algorithm',
|
||||
];
|
||||
205
code/python/src/algorithms/rucksack/algorithms.py
Normal file
205
code/python/src/algorithms/rucksack/algorithms.py
Normal file
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from src.thirdparty.types import *;
|
||||
from src.thirdparty.maths import *;
|
||||
|
||||
from models.generated.config import *;
|
||||
from src.models.rucksack import *;
|
||||
from src.models.stacks import *;
|
||||
from src.algorithms.rucksack.display import *;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'rucksack_greedy_algorithm',
|
||||
'rucksack_branch_and_bound_algorithm',
|
||||
];
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHOD greedy algorithm
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def rucksack_greedy_algorithm(
|
||||
capacity: float,
|
||||
weights: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
fractional: bool,
|
||||
verbose: bool,
|
||||
) -> Solution:
|
||||
'''
|
||||
Durch den Greedy-Algorithm wird der optimale Wert eines Rucksacks
|
||||
unter Rücksicht der Kapizitätsschranke eingeschätzt.
|
||||
|
||||
NOTE: Wenn man `fractional = True` verwendet, liefert der Algorithmus
|
||||
eine obere Schranke des maximalen Wertes beim Originalproblem.
|
||||
'''
|
||||
# sortiere daten:
|
||||
resort_by_value_per_weight(weights=weights, values=values, items=items);
|
||||
|
||||
# führe greedy aus:
|
||||
n = len(weights);
|
||||
weight_total = 0;
|
||||
vector = [ 0 for _ in range(n) ];
|
||||
rucksack = [];
|
||||
for i in range(n):
|
||||
# füge Item i hinzu, solange das Gesamtgewicht noch <= Schranke
|
||||
if weight_total + weights[i] <= capacity:
|
||||
weight_total += weights[i];
|
||||
rucksack.append(i);
|
||||
vector[i] = 1;
|
||||
# sonst abbrechen. Falls Bruchteile erlaubt, füge einen Bruchteil des i. Items hinzu
|
||||
else:
|
||||
if fractional:
|
||||
rucksack.append(i);
|
||||
vector[i] = (capacity - weight_total)/weights[i];
|
||||
break;
|
||||
|
||||
# verbose output hier behandeln (irrelevant für Algorithmus):
|
||||
if verbose:
|
||||
repr = display_greedy(vector=vector, values=values);
|
||||
print('');
|
||||
print('\x1b[1mRucksack Problem - Greedy\x1b[0m');
|
||||
print('');
|
||||
print(repr);
|
||||
print('');
|
||||
|
||||
# Lösung ausgeben
|
||||
return Solution(
|
||||
vector = vector,
|
||||
items = items[rucksack].tolist(),
|
||||
weights = weights[rucksack].tolist(),
|
||||
values = values[rucksack].tolist(),
|
||||
);
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHOD branch and bound algorithm
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def rucksack_branch_and_bound_algorithm(
|
||||
capacity: float,
|
||||
weights: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
verbose: bool,
|
||||
) -> Solution:
|
||||
'''
|
||||
Durch Branch & Bound wird der optimale Wert eines Rucksacks
|
||||
unter Rücksicht der Kapizitätsschranke exakt und effizienter bestimmt.
|
||||
'''
|
||||
|
||||
resort_by_value_per_weight(weights=weights, values=values, items=items);
|
||||
logged_steps = [];
|
||||
|
||||
vector = empty_mask(n=len(weights));
|
||||
lb_estimate = np.inf;
|
||||
S = Stack();
|
||||
S.push(vector);
|
||||
while not S.empty():
|
||||
lb, u = 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)));
|
||||
# Update nur nötig, wenn die (eingeschätzte) untere Schranke von A das bisherige Minimum verbessert:
|
||||
A: Mask = S.pop();
|
||||
if lb < lb_estimate:
|
||||
# Branch:
|
||||
if A.splittable():
|
||||
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:
|
||||
S.push(C);
|
||||
# Bound, wenn die Maske den Rucksack komplett bestimmt:
|
||||
else:
|
||||
lb_estimate = lb;
|
||||
vector = A;
|
||||
|
||||
# verbose output hier behandeln (irrelevant für Algorithmus):
|
||||
if verbose:
|
||||
repr = display_branch_and_bound(values=values, steps=logged_steps);
|
||||
print('');
|
||||
print('\x1b[1mRucksack Problem - Branch & Bound\x1b[0m');
|
||||
print('');
|
||||
print(repr);
|
||||
print('');
|
||||
|
||||
# Lösung ausgeben
|
||||
rucksack = vector.indexes_one; # Indexes von Items im Rucksack
|
||||
return Solution(
|
||||
vector = vector.decision,
|
||||
items = items[rucksack].tolist(),
|
||||
values = values[rucksack].tolist(),
|
||||
weights = weights[rucksack].tolist(),
|
||||
);
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# AUXILIARY METHOD resort
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def resort_by_value_per_weight(
|
||||
weights: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
) -> List[int]:
|
||||
'''
|
||||
Sortiert Daten absteigend nach values/weights.
|
||||
'''
|
||||
n = len(weights);
|
||||
indexes = list(range(n));
|
||||
order = sorted(indexes, key=lambda i: -values[i]/weights[i]);
|
||||
weights[:] = weights[order];
|
||||
values[:] = values[order];
|
||||
items[:] = items[order];
|
||||
return order;
|
||||
|
||||
def estimate_lower_bound(
|
||||
mask: Mask,
|
||||
capacity: float,
|
||||
weights: np.ndarray,
|
||||
values: np.ndarray,
|
||||
items: np.ndarray,
|
||||
) -> Tuple[float, List[float]]:
|
||||
'''
|
||||
Wenn partielle Information über den Rucksack festgelegt ist,
|
||||
kann man bei dem unbekannten Teil das Rucksack-Problem
|
||||
mit Greedy-Algorithmus »lösen«,
|
||||
um schnell eine gute Einschätzung zu bestimmen.
|
||||
|
||||
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);
|
||||
vector[indexes_one] = 1;
|
||||
vector[indexes_unset] = soln_rest.vector;
|
||||
|
||||
# Einschätzung des max-Wertes (Ausgabe mit -1 multiplizieren):
|
||||
value_max_est = value_rucksack + value_rest;
|
||||
return -value_max_est, vector.tolist();
|
||||
62
code/python/src/algorithms/rucksack/display.py
Normal file
62
code/python/src/algorithms/rucksack/display.py
Normal file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# IMPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
from src.thirdparty.types import *;
|
||||
from src.thirdparty.maths import *;
|
||||
|
||||
from src.models.stacks import *;
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# EXPORTS
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
__all__ = [
|
||||
'display_greedy',
|
||||
'display_branch_and_bound',
|
||||
];
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# METHODS display
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def display_greedy(
|
||||
vector: Union[List[int], List[float]],
|
||||
values: np.ndarray,
|
||||
) -> str:
|
||||
value = sum([ u*x for u, x in zip(vector,values)]);
|
||||
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
|
||||
]);
|
||||
return f'{value:g} (={expr})';
|
||||
|
||||
def display_branch_and_bound(
|
||||
values: np.ndarray,
|
||||
steps: List[Tuple[float, float, Stack]]
|
||||
) -> str:
|
||||
# füge Summen-Ausdrücke für Greedy-Alg hinzu:
|
||||
rows = [];
|
||||
used_vectors = [];
|
||||
for lb_estimate, lb, u, S in steps:
|
||||
if u in used_vectors:
|
||||
rows.append((f'{lb_estimate:g}', f'{lb:g}', S));
|
||||
else:
|
||||
used_vectors.append(u)
|
||||
rows.append((f'{lb_estimate:g}', display_greedy(vector=u, values=values), S));
|
||||
|
||||
table = pd.DataFrame(rows) \
|
||||
.rename(columns={0: 'b', 1: 'g(TOP(S))', 2: 'S'}) \
|
||||
.reset_index(drop=True);
|
||||
# benutze pandas-Dataframe + tabulate, um schöner darzustellen:
|
||||
repr = tabulate(
|
||||
pd.DataFrame(table),
|
||||
headers=['b', 'g(TOP(S))', 'S'],
|
||||
showindex=False,
|
||||
colalign=('left', 'left', 'right'),
|
||||
tablefmt='rst'
|
||||
);
|
||||
return repr;
|
||||
Reference in New Issue
Block a user