master > master: code py - algorithmen für rucksackproblem

This commit is contained in:
RD 2022-06-14 01:35:10 +02:00
parent 7cfaf253b3
commit ea36c82728
13 changed files with 500 additions and 1 deletions

View 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',
];

View 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();

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

View File

@ -38,4 +38,6 @@ def run_command(command: Command) -> Result[CallResult, CallError]:
return endpoint_tsp(command);
elif isinstance(command, CommandHirschberg):
return endpoint_hirschberg(command);
elif isinstance(command, CommandRucksack):
return endpoint_rucksack(command);
raise Exception(f'No endpoint set for `{command.name.value}`-command type.');

View File

@ -8,6 +8,7 @@
from src.endpoints.ep_algorithm_hirschberg import *;
from src.endpoints.ep_algorithm_tarjan import *;
from src.endpoints.ep_algorithm_tsp import *;
from src.endpoints.ep_algorithm_rucksack import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
@ -17,4 +18,5 @@ __all__ = [
'endpoint_hirschberg',
'endpoint_tarjan',
'endpoint_tsp',
'endpoint_rucksack',
];

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.thirdparty.code import *;
from src.thirdparty.maths import *;
from models.generated.commands import *;
from src.core.calls import *;
from src.setup import config;
from src.algorithms.rucksack import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'endpoint_rucksack',
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ENDPOINT
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@run_safely()
def endpoint_rucksack(command: CommandRucksack) -> Result[CallResult, CallError]:
n = len(command.weights);
assert len(command.values) == n, f'Number of weights must be {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) ];
match command.algorithm:
case EnumRucksackAlgorithm.greedy:
result = rucksack_greedy_algorithm(
capacity = command.capacity,
weights = np.asarray(command.weights[:]),
values = np.asarray(command.values[:]),
items = np.asarray(command.items[:]),
fractional = command.allow_fractional,
verbose = config.OPTIONS.rucksack.verbose,
);
case EnumRucksackAlgorithm.branch_and_bound:
result = rucksack_branch_and_bound_algorithm(
capacity = command.capacity,
weights = np.asarray(command.weights[:]),
values = np.asarray(command.values[:]),
items = np.asarray(command.items[:]),
verbose = config.OPTIONS.rucksack.verbose,
);
case _ as alg:
raise Exception(f'No algorithm implemented for {alg.value}.')
return Ok(CallResult(action_taken=True, message=result));

View File

@ -29,7 +29,7 @@ __all__ = [
def endpoint_tsp(command: CommandTsp) -> Result[CallResult, CallError]:
result = tsp_algorithm(
dist = np.asarray(command.dist, dtype=float),
optimise = min if command.optimise == EnumTSPOptimise.min else max,
optimise = min if command.optimise == EnumOptimiseMode.min else max,
verbose = config.OPTIONS.tsp.verbose,
);
return Ok(CallResult(action_taken=True, message=result));

View File

@ -42,4 +42,6 @@ def interpret_command(command: Command) -> Command:
return CommandTsp(**command.dict());
case EnumAlgorithmNames.hirschberg:
return CommandHirschberg(**command.dict());
case EnumAlgorithmNames.rucksack:
return CommandRucksack(**command.dict());
raise command;

View File

@ -0,0 +1,20 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.models.rucksack.mask import *;
from src.models.rucksack.solution import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'empty_mask',
'MaskValue',
'Mask',
'Solution',
];

View File

@ -0,0 +1,80 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations;
from src.thirdparty.types import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'empty_mask',
'MaskValue',
'Mask',
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ENUMS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class MaskValue(Enum):
ZERO = 0;
ONE = 1;
UNSET = '*';
class Mask():
index: int;
values: List[MaskValue];
def __init__(self, values: List[MaskValue]):
self.values = values;
if MaskValue.UNSET in values:
self.index = values.index(MaskValue.UNSET);
else:
self.index = -1;
return;
def __len__(self) -> int:
return len(self.values);
def __str__(self) -> str:
return ''.join([ str(m.value) for m in self.values ]);
@property
def decision(self) -> List[int]:
return [ x.value for x in self.values ];
@property
def indexes_set(self) -> List[int]:
return [i for i, value in enumerate(self.values) if value != MaskValue.UNSET];
@property
def indexes_one(self) -> List[int]:
return [i for i, value in enumerate(self.values) if value == MaskValue.ONE];
@property
def indexes_zero(self) -> List[int]:
return [i for i, value in enumerate(self.values) if value == MaskValue.ZERO];
@property
def indexes_unset(self) -> List[int]:
return [i for i, value in enumerate(self.values) if value == MaskValue.UNSET];
def splittable(self) -> bool:
return self.index >= 0;
def split(self) -> Tuple[Mask, Mask]:
vector1 = self.values[:];
vector1[self.index] = MaskValue.ZERO;
vector2 = self.values[:];
vector2[self.index] = MaskValue.ONE;
return Mask(vector1), Mask(vector2);
def empty_mask(n: int):
return Mask([MaskValue.UNSET for _ in range(n)]);

View File

@ -0,0 +1,43 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from __future__ import annotations
from dataclasses import asdict;
from src.thirdparty.types import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'Solution',
];
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ENUMS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@dataclass
class SolutionRaw():
vector: Union[List[int], List[float]] = field();
items: List[str] = field();
values: List[float] = field(repr=False);
weights: List[float] = field(repr=False);
class Solution(SolutionRaw):
@property
def support(self) -> List[float]:
return [ i for i, v in enumerate(self.vector) if v > 0 ];
@property
def total_weight(self) -> float:
return sum([ self.vector[i]*x for (i, x) in zip(self.support, self.weights) ]);
@property
def total_value(self) -> float:
return sum([ self.vector[i]*x for (i, x) in zip(self.support, self.values) ]);

View File

@ -39,6 +39,13 @@ class Stack:
def __contains__(self, value: Any) -> bool:
return value in self.elements;
def __iter__(self) -> Generator[Any, None, None]:
for value in self.elements:
yield value;
def __str__(self) -> str:
return ', '.join([str(value) for value in self.elements[::-1]]);
def push(self, value: Any):
'''
add element to stack
@ -68,3 +75,6 @@ class Stack:
checks if element in stack:
'''
return element in self.elements;
def empty(self) -> bool:
return len(self) == 0;

View File

@ -5,6 +5,7 @@
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from fractions import Fraction;
import math;
import numpy as np;
import pandas as pd;
@ -16,6 +17,7 @@ from tabulate import tabulate;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
__all__ = [
'Fraction',
'math',
'np',
'pd',