master > master: code - python code umgezogen

This commit is contained in:
RD
2021-10-30 10:18:53 +02:00
parent f01710b445
commit 37dea15b2b
32 changed files with 74 additions and 72 deletions

View File

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.algorithms.search.exports import *;
from src.algorithms.sum.exports import *;

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import functools;
from src.core.log import *;
from src.setup.display import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CLASSES
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class OneShot(object):
state: bool;
def __init__(self):
self.state = True;
def setState(self, state: bool):
self.state = state;
def reload(self):
self.state = True;
def trigger(self):
self.state = False;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# DECORATORS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
## Trigger, der das fancy Zeug bei verschachtelten Algorithmen verhindert
nonnestedAlgorithms = OneShot();
def algorithmInfos(
name: str,
checks: bool = True,
metrics: bool = None,
outputnames: Union[str, Tuple[str]] = 'result',
preChecks: Any = None,
postChecks: Any = None
):
'''
Decorator für Algorthmen, der Folgendes macht:
- Zeigt vorm Start Console-Messages mit Namen des Algorithmus + Inputs.
- Zeit am Ende optional Metriken (bspw. Zeitkosten).
- Zeigt am Ende Console-Messages von Output/s.
- Prüft optional vor und nach Ausführung des Algorithmus, dass die Inputs/Outputs stimmen.
Bei rekursiven Definitionen werden die o. s. Punkte nur bei dem äußersten Aufruf gemacht.
'''
def func_decorator(func):
## Trigger, der das fancy Zeug bei verschachtelten Aufrufen verhindert
nonnestedRecursion = OneShot();
@functools.wraps(func)
def func_wrapper(**inputs):
try:
state1 = nonnestedAlgorithms.state;
state2 = nonnestedRecursion.state;
state = state1 and state2;
if state:
# Initialisierung
DisplayStartOfAlgorithm(name, **inputs);
RestartCounter();
# Prechecks
if checks and callable(preChecks):
preChecks(**inputs);
# Ausführung des Algorithmus:
nonnestedRecursion.trigger();
nonnestedAlgorithms.trigger();
outputs = func(*[], **inputs);
nonnestedAlgorithms.state = state1;
nonnestedRecursion.state = state2;
if state:
# benenne Outputs:
outputs_ = outputs if isinstance(outputs, tuple) else tuple([outputs]);
outputnames_ = outputnames if isinstance(outputnames, tuple) else tuple([outputnames]);
outputsNamed = { outputnames_[k]: value for k, value in enumerate(outputs_) };
# Letzte Messages
if metrics:
DisplayMetrics();
DisplayEndOfAlgorithm(**outputsNamed);
# Postchecks
if checks and callable(postChecks):
postChecks(**inputs, **outputsNamed);
except Exception as e:
nonnestedAlgorithms.state = state1;
nonnestedRecursion.state = state2;
raise e;
return outputs;
return func_wrapper;
return func_decorator;

View File

@@ -0,0 +1,66 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.local.maths import *;
from src.local.typing import *;
from src.core.log import *;
from src.algorithms.methods import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL VARIABLES/CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CHECKS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def preChecks(L: List[int], **_):
assert L == sorted(L), 'Ungültiger Input: L muss aufsteigend sortiert sein!';
return;
def postChecks(L: List[int], x: int, index: int, **_):
if x in L:
assert index >= 0, 'Der Algorithmus sollte nicht -1 zurückgeben.';
assert L[index] == x, 'Der Algorithmus hat den falschen Index bestimmt.';
else:
assert index == -1, 'Der Algorithmus sollte -1 zurückgeben.';
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM binary search
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='Binärsuchalgorithmus', outputnames='index', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def BinarySearch(L: List[int], x: int) -> int:
'''
Inputs: L = Liste von Zahlen, x = Zahl.
Annahme: L sei aufsteigend sortiert.
Outputs: Position von x in L, sonst 1 wenn x nicht in L.
'''
if len(L) == 0:
logDebug('x nicht in L');
return -1;
AddToCounter();
m = math.floor(len(L)/2);
if L[m] == x:
logDebug('x in Position m gefunden');
return m;
elif x < L[m]:
logDebug('Suche in linker Hälfte fortsetzen.');
index = BinarySearch(L=L[:m], x=x);
return index;
else: # x > L[m]
logDebug('Suche in rechter Hälfte fortsetzen.');
index = BinarySearch(L=L[m+1:], x=x);
if index >= 0:
index += (m + 1); # NOTE: muss Indexwert kompensieren
return index;

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.algorithms.search.sequential import SequentialSearch;
from src.algorithms.search.binary import BinarySearch;
from src.algorithms.search.interpol import InterpolationSearch;
from src.algorithms.search.jump import JumpSearchLinear;
from src.algorithms.search.jump import JumpSearchExponentiell;
from src.algorithms.search.ith_smallest import FindIthSmallest;
from src.algorithms.search.ith_smallest import FindIthSmallestDC;
from src.algorithms.search.poison import FindPoison;
from src.algorithms.search.poison import FindPoisonFast;

View File

@@ -0,0 +1,72 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.local.maths import *;
from src.local.typing import *;
from src.core.log import *;
from src.algorithms.methods import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL VARIABLES/CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CHECKS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def preChecks(L: List[int], **_):
assert L == sorted(L), 'Ungültiger Input: L muss aufsteigend sortiert sein!';
return;
def postChecks(L: List[int], x: int, index: int, **_):
if x in L:
assert index >= 0, 'Der Algorithmus sollte nicht -1 zurückgeben.';
assert L[index] == x, 'Der Algorithmus hat den falschen Index bestimmt.';
else:
assert index == -1, 'Der Algorithmus sollte -1 zurückgeben.';
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM interpolation
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='Interpolationssuchalgorithmus', outputnames='index', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def InterpolationSearch(L: List[int], x: int, u: int, v: int) -> int:
'''
Inputs: L = Liste von Zahlen, x = Zahl, [u, v] = Suchinterval.
Annahme: L sei aufsteigend sortiert.
Outputs: Position von x in L, sonst 1 wenn x nicht in L.
'''
if not(L[u] <= x and x <= L[v]):
logDebug('Element kann sich nicht in der Liste befinden!')
return -1;
p = getSuchposition(L=L, x=x, u=u, v=v);
logDebug('Interpolante von x in (u, v)={uv} ist p = {p}.'.format(uv=(u, v), p=p));
if L[p] == x:
logDebug('x in Position p gefunden');
return p;
elif x > L[p]:
logDebug('Suche in rechter Hälfte fortsetzen.');
return InterpolationSearch(L=L, x=x, u=p+1, v=v);
else: # x < L[p]
logDebug('Suche in linker Hälfte fortsetzen.');
return InterpolationSearch(L=L, x=x, u=u, v=p-1);
def getSuchposition(L: List[int], x: int, u: int, v: int) -> int:
'''
Inputs: L = Liste von Zahlen, x = Zahl, [u, v] = Suchinterval.
Outputs: Interpolierte Position, um Suchinterval ausgeglichen aufzuteilen.
'''
AddToCounter();
r = (x - L[u])/(L[v]-L[u]);
p = math.floor(u + r*(v-u))
return p;

View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.local.maths import *;
from src.local.typing import *;
from src.core.log import *;
from src.algorithms.search.sequential import SequentialSearch;
from src.algorithms.methods import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL VARIABLES/CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CHECKS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def preChecks(L: List[int], i: int, **_):
assert 1 <= i and i <= len(L), 'Der Wert von i muss zw. {lb} und {ub} liegen.'.format(lb = 1, ub = len(L));
assert sorted(L) == sorted(list(set(L))), 'Ungültiger Input: L darf keine Duplikate enthalten!';
return;
def postChecks(L: List[int], i: int, value: int, **_):
L = sorted(L);
assert L[i-1] == value, 'Der Algorithmus hat versagt.';
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM jump search
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='Auswahlproblem (i. kleinstes Element)', outputnames='value', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def FindIthSmallest(L: List[int], i: int) -> int:
'''
Inputs: L = Liste von Zahlen, i = Ordinalzahl
Annahmen:
- L enthält keine Duplikate.
- L enthält mindestens i Elemente.
Outputs: Wert des i. kleinste Element in L.
Beachte 1.kleinstes <==> Minimum.
'''
index = 0;
minValue = L[0];
AddToCounter(len(L));
for i_ in range(1, len(L)):
if L[i_] < minValue:
index = i_;
minValue = L[i_];
if i == 1:
logDebug('Das i. kleinste Element wurde gefunden.');
return minValue;
else:
logDebug('Entfernte Minimum: {value}.'.format(value = minValue));
i = i - 1;
return FindIthSmallest(L=L[:index] + L[(index+1):], i=i);
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM jump search (D & C)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='Auswahlproblem (i. kleinstes Element, D & C)', outputnames='value', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def FindIthSmallestDC(L: List[int], i: int) -> int:
'''
Inputs: L = Liste von Zahlen, i = Ordinalzahl
Annahmen:
- L enthält keine Duplikate.
- L enthält mindestens i Elemente.
Outputs: Wert des i. kleinste Element in L.
Beachte 1.kleinstes <==> Minimum.
'''
AddToCounter();
p = L[len(L)-1]; # NOTE: Pivotelement kann beliebig gewählt werden
Ll = [ x for x in L if x < p ];
Lr = [ x for x in L if x > p ];
if len(Ll) == i - 1:
logDebug('Es gibt i-1 Elemente vor p={p}. ==> i. kleinste Element = p'.format(p=p));
return p;
elif len(Ll) >= i:
logDebug('Es gibt >= i Elemente vor p={p}. ==> Suche in linker Hälfte!'.format(p=p));
return FindIthSmallestDC(L=Ll, i=i);
else:
i = i - (len(Ll) + 1)
logDebug('Es gibt < i-1 Elemente vor p={p}. ==> Suche in rechter Hälfte!'.format(p=p));
return FindIthSmallestDC(L=Lr, i=i);

View File

@@ -0,0 +1,99 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.local.maths import *;
from src.local.typing import *;
from src.core.log import *;
from src.algorithms.search.sequential import SequentialSearch;
from src.algorithms.methods import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL VARIABLES/CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CHECKS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def preChecks(L: List[int], **_):
assert L == sorted(L), 'Ungültiger Input: L muss aufsteigend sortiert sein!';
assert L == sorted(list(set(L))), 'Ungültiger Input: L darf keine Duplikate enthalten!';
return;
def postChecks(L: List[int], x: int, index: int, **_):
if x in L:
assert index >= 0, 'Der Algorithmus sollte nicht -1 zurückgeben.';
assert L[index] == x, 'Der Algorithmus hat den falschen Index bestimmt.';
else:
assert index == -1, 'Der Algorithmus sollte -1 zurückgeben.';
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM jump search
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='Sprungsuche', outputnames='index', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def JumpSearchLinear(L: List[int], x: int, m: int) -> int:
'''
Inputs: L = Liste von Zahlen, x = Zahl, m = lineare Sprunggröße.
Annahmen:
- L sei aufsteigend sortiert.
- L enthält keine Duplikate.
Outputs: Position von x in L, sonst 1 wenn x nicht in L.
'''
i = 0;
while i*m < len(L):
AddToCounter();
offset = i*m;
block = L[offset:][:m];
elementAfterBlock = block[-1] + 1;
if x < elementAfterBlock:
logDebug('Element muss sich im Block [{i0}, {i1}) befinden.'.format(i0 = i*m, i1 = (i+1)*m));
index = SequentialSearch(L=block, x=x);
if index >= 0:
index += offset; # NOTE: muss wegen Offset kompensieren
return index;
logDebug('Element befindet sich nicht im im Block [{i0}, {i1}) befinden.'.format(i0 = i*m, i1 = (i+1)*m));
i += 1;
return -1;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM jump search - exponentiell
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='Sprungsuche (exponentiell)', outputnames='index', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def JumpSearchExponentiell(L: List[int], x: int) -> int:
'''
Inputs: L = Liste von Zahlen, x = Zahl.
Annahmen:
- L sei aufsteigend sortiert.
- L enthält keine Duplikate.
Outputs: Position von x in L, sonst 1 wenn x nicht in L.
'''
i0 = 0;
i1 = 1;
while i0 < len(L):
AddToCounter();
block = L[i0:i1];
elementAfterBlock = block[-1] + 1;
if x < elementAfterBlock:
logDebug('Element muss sich im Block [{i0}, {i1}) befinden.'.format(i0 = i0, i1 = i1));
index = SequentialSearch(L=block, x=x);
if index >= 0:
index += i0; # NOTE: muss wegen Offset kompensieren
return index;
logDebug('Element befindet sich nicht im Block [{i0}, {i1}) befinden.'.format(i0 = i0, i1 = i1));
i0 = i1;
i1 *= 2;
return -1;

View File

@@ -0,0 +1,145 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.local.maths import *;
from src.local.typing import *;
from src.core.log import *;
from src.algorithms.methods import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL VARIABLES/CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CHECKS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def preChecks(L: List[bool], **_):
assert sum(L) > 0, 'Mindestens ein Getränk muss vergiftet sein!';
assert sum(L) == 1, 'Höchstens ein Getränk darf vergiftet sein!';
return;
def postChecks(L: List[bool], index: int, **_):
assert L[index] == True, 'Der Algorithmus hat das vergiftete Getränk nicht erfolgreich bestimmt.';
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM find poison
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='Giftsuche (O(n) Vorkoster)', outputnames='index', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def FindPoison(L: List[bool]) -> int:
'''
Inputs: L = Liste von Getränken: durch boolesche Werte wird dargestellt, ob ein Getränk vergiftet ist.
Annahme: Genau ein Getränk sei vergiftet.
Outputs: Der Index des vergifteten Getränks, falls es eines gibt, ansonsten -1.
NOTE: Zeitkosten hier messen nur die Anzahl der Vorkoster.
'''
logDebug('Bereite Vorkoster vor');
n = len(L);
testers = [];
for i in range(n):
AddToCounter();
logDebug('Füge Vorkoster hinzu, der nur Getränk {i} testet.'.format(i=i))
testers.append([i]);
logDebug('Warte auf Effekte');
effects = waitForEffects(L, testers);
logDebug('Effekte auswerten, um vergiftete Getränke zu lokalisieren.');
poisened = evaluateEffects(testers, effects);
if len(poisened) > 0:
return poisened[0];
return -1;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM find poison fast
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='Giftsuche (O(log(n)) Vorkoster)', outputnames='index', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def FindPoisonFast(L: List[bool]) -> List[int]:
'''
Inputs: L = Liste von Getränken: durch boolesche Werte wird dargestellt, ob ein Getränk vergiftet ist.
Annahme: Genau ein Getränk sei vergiftet.
Outputs: Der Index des vergifteten Getränks, falls es eines gibt, ansonsten -1.
NOTE: Zeitkosten hier messen nur die Anzahl der Vorkoster.
'''
logDebug('Bereite Vorkoster vor');
n = len(L);
p = math.floor(math.log2(n));
testers = [];
## Für jedes Bit i=0 bis p ...
for i in range(p+1):
tester0 = [ k for k in range(n) if nthBit(number=k, digit=i) == 0 ];
tester1 = [ k for k in range(n) if not (k in tester0) ];
# NOTE: tester1 ist virtuell: aus den Effekten auf tester0 und den Annahmen lassen sich die Effekte auf tester0 erschließen.
# Darum zählen wir nicht 2 sondern 1 Vorkoster.
AddToCounter(1);
logDebug('Füge Vorkoster hinzu, der alle Getränke k testet mit {i}. Bit = 0.'.format(i=i))
testers.append(tester0);
testers.append(tester1);
logDebug('Warte auf Effekte');
effects = waitForEffects(L, testers);
logDebug('Effekte auswerten, um vergiftete Getränke zu lokalisieren.');
poisened = evaluateEffects(testers, effects);
if len(poisened) > 0:
return poisened[0];
return -1;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# AUXILIARY METHOD wait for effects, evaluate effects
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def waitForEffects(L: List[bool], testers: List[List[int]]) -> List[int]:
'''
Inputs:
- L = Liste von Getränken: durch boolesche Werte wird dargestellt, ob ein Getränk vergiftet ist.
- testers = Liste von Vorkostern. Jeder Vorkoster kostet eine 'Teilliste' der Getränke.
Outputs: effects = eine Liste, die jedem Vorkoster zuordnet, wie viele vergiftete Getränke er konsumiert hat.
'''
m = len(testers);
effects = [];
for i in range(m):
effect = sum([L[k] for k in testers[i]]);
effects.append(effect);
return effects;
def evaluateEffects(testers: List[List[int]], effects: List[int]) -> List[int]:
'''
Inputs:
- testers = Liste von Vorkostern. Jeder Vorkoster kostet eine 'Teilliste' der Getränke.
- effects = eine Liste, die jedem Vorkoster zuordnet, wie viele vergiftete Getränke er konsumiert hat.
Annahmen: Vorkoster wurden so angewiesen, dass es garantiert ist, vergiftete Getränke zu finden, wenn es die gibt.
Outputs: Liste der Indexes aller vergifteten Getränke.
'''
## Werte Effekte aus, um Gift zu lokalisieren:
search = set([]);
## Zuerst die Indexes der Getränke bei allen vergifteten Tester zusammenführen:
for i in range(len(testers)):
if effects[i] > 0:
search = search.union(testers[i]);
## jetzt eliminieren wir alle Getränke, die von nicht vergifteten Testern konsumiert wurden:
for i in range(len(testers)):
if effects[i] == 0:
search = search.difference(testers[i]);
return list(search);
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# AUXILIARY METHOD n. Bit der binären Darstellung einer Zahl
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def nthBit(number: int, digit: int) -> int:
number_binary = bin(number)[2:][::-1];
if digit < len(number_binary):
return int(number_binary[digit]);
return 0;

View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.local.maths import *;
from src.local.typing import *;
from src.core.log import *;
from src.algorithms.methods import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL VARIABLES/CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CHECKS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def preChecks(L: List[int], **_):
# Keine Checks!
return;
def postChecks(L: List[int], x: int, index: int, **_):
if x in L:
assert index >= 0, 'Der Algorithmus sollte nicht -1 zurückgeben.';
assert L[index] == x, 'Der Algorithmus hat den falschen Index bestimmt.';
else:
assert index == -1, 'Der Algorithmus sollte -1 zurückgeben.';
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM sequential search
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='Sequenziellsuchalgorithmus', outputnames='index', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def SequentialSearch(L: List[int], x: int) -> int:
'''
Inputs: L = Liste von Zahlen, x = Zahl.
Outputs: Position von x in L, sonst 1 wenn x nicht in L.
'''
n = len(L);
for i in range(n):
AddToCounter();
if L[i] == x:
logDebug('Element in Position {} gefunden.'.format(i));
return i;
logDebug('Element nicht in Position {}.'.format(i));
return -1;

View File

@@ -0,0 +1,9 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# EXPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.algorithms.sum.maxsubsum import MaxSubSum;
from src.algorithms.sum.maxsubsum import MaxSubSumDC;

View File

@@ -0,0 +1,179 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from src.local.maths import *;
from src.local.typing import *;
from src.core.log import *;
from src.algorithms.methods import *;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# GLOBAL VARIABLES/CONSTANTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# CHECKS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def preChecks(L: List[int], **_):
assert len(L) > 0, 'Liste darf nicht leer sein.';
return;
def postChecks(L: List[int], **_):
# TODO
return;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM max sub sum
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='MaxSubSum (Maximale Teilsumme)', outputnames=('maxSum', 'index_from', 'index_to'), checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def MaxSubSum(L: List[float]) -> Tuple[float, int, int]:
'''
Inputs: L = Liste von Zahlen
Outputs:
- maxSum = Wert der maximalen Summe einer Teilliste aufeinanderfolgender Elemente
- u, v = Indexes so dass die Teilliste [L[u], L[u+1], ..., L[v]] die maximale Summe aufweist
'''
maxSum: float = 0;
u: int = 0;
v: int = -1;
for i in range(len(L)):
## Bestimme maximale Teilsumme der linken Rände der Liste ab Index i:
maxSum_, _, k = lRandSum(L[i:]);
if maxSum_ > maxSum:
k += i; # NOTE: muss wegen Offset kompensieren
maxSum, u, v = maxSum_, i, k;
logDebug('max Teilsumme aktualisiert: Summe L[i] von i={u} .. {v} = {value}'.format(u = u, v = v, value = maxSum));
return maxSum, u, v;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ALGORITHM max sub sum (D & C)
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@algorithmInfos(name='MaxSubSum (Maximale Teilsumme mit D & C)', outputnames=('maxSum', 'index_from', 'index_to'), checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks)
def MaxSubSumDC(L: List[float]) -> Tuple[float, int, int]:
'''
Inputs: L = Liste von Zahlen
Outputs:
- maxSum = Wert der maximalen Summe einer Teilliste aufeinanderfolgender Elemente
- u, v = Indexes so dass die Teilliste [L[u], L[u+1], ..., L[v]] die maximale Summe aufweist
'''
maxSum = 0;
u = 0;
v = -1;
if len(L) == 1:
## wenn Liste aus 1 Element besteht, nicht teilen:
if L[0] > maxSum:
v = 0;
maxSum = L[0];
else:
u = math.ceil(len(L)/2);
Ll = L[:u];
Lr = L[u:];
## berechnet maximale Teilsumme der linken Hälfte:
maxSum1, u1, v1 = MaxSubSumDC(L=Ll);
## berechnet maximale Teilsumme der rechten Hälfte:
maxSum2, u2, v2 = MaxSubSumDC(L=Lr);
u2, v2 = u2 + len(Ll), v2 + len(Ll); # offsets kompensieren
## berechnet maximale Teilsumme mit Überschneidung zw. den Hälften:
maxSum3, u3, v3 = lrRandSum(Ll=Ll, Lr=Lr);
## bestimme Maximum der 3 Möglichkeiten:
maxSum = max(maxSum1, maxSum2, maxSum3);
if maxSum == maxSum1:
maxSum, u, v = maxSum1, u1, v1;
logDebug('max Teilsumme kommt in linker Partition vor: Summe L[i] von i={i} .. {j} = {value}'.format(L = L, i = u, j = v, value = maxSum));
elif maxSum == maxSum3:
maxSum, u, v = maxSum3, u3, v3;
logDebug('max Teilsumme kommt in Überschneidung vor: Summe L[i] von i={i} .. {j} = {value}'.format(L = L, i = u, j = v, value = maxSum));
else: # elif maxSum == maxSum2:
maxSum, u, v = maxSum2, u2, v2;
logDebug('max Teilsumme kommt in rechter Partition vor: Summe L[i] von i={i} .. {j} = {value}'.format(L = L, i = u, j = v, value = maxSum));
return maxSum, u, v;
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# AUXILIARY METHODS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def lrRandSum(Ll: List[float], Lr: List[float]) -> Tuple[float, int, int]:
'''
Bestimmt maximale Teilsumme einer Teiliste einer Liste,
wobei die Liste in zwei Intervalle partitioniert ist
und die Teilliste beide überschneidet.
Inputs: Ll, Lr = eine Partition einer Liste von Zahlen in zwei Intervalle
Outputs: maxSum, u=0, v
'''
maxSumL, u, _ = rRandSum(L=Ll);
maxSumR, _, v = lRandSum(L=Lr);
maxSum = maxSumL + maxSumR;
v += len(Ll) # offsets kompensieren
return maxSum, u, v;
def lRandSum(L: List[float]) -> Tuple[float, int, int]:
'''
Bestimmt maximale Teilsumme aller nicht leeren linken Segmente einer Liste.
Inputs: L = Liste von Zahlen
Outputs: maxSum, u(=0), v
'''
n = len(L);
## berechne kumulative Summen (vorwärts)
AddToCounter(n);
total = L[0];
maxSum = total;
u = 0;
v = 0;
for i in range(1, n):
total += L[i];
if total > maxSum:
v = i;
maxSum = total;
return maxSum, 0, v;
def rRandSum(L: List[float]) -> Tuple[float, int, int]:
'''
Bestimmt maximale Teilsumme aller nicht leeren rechten Segmente einer Liste.
Inputs: L = Liste von Zahlen
Outputs: maxSum, u, v(=len(L)-1)
'''
n = len(L);
## berechne kumulative Summen (rückwärts)
AddToCounter(n);
total = L[n-1];
maxSum = total;
u = n-1;
v = n-1;
for i in range(0, n-1)[::-1]:
total += L[i];
if total > maxSum:
u = i;
maxSum = total;
return maxSum, u, v;
# Sei N ∈ ℕ⁺
# Sei p so, dass 2^p ≤ N < 2^{p+1},
# Also p = floor(log₂(N)).
# Setze
# B(i,d) := {k < N | bit(k, i) = d}
# für i ∈ {0, 1, ..., p-1}
# und setze
# 𝒜 = {B(i,d) : i ∈ {0, 1, ..., p-1}, d ∈ {0,1}}.
# Seien k1, k2 ∈ N mit k1 ≠ k2.
# Dann existiert i ∈ {0, 1, ..., p-1},
# so dass
# d := bit(k1, i) ≠ bit(k2, i).
# Also
# k1 ∈ B(i, d) ∌ k2.
# Darum erfüllt 𝒜 die erwünschte Eigenschaft.
# Es gilt
# |𝒜| = 2p = 2·floor(log₂(N)) ∈ O(log(N)).