ads1_2021/code/algorithms/sum/maxsubsum.py

160 lines
5.7 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IMPORTS
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
from local.maths import *;
from local.typing import *;
from code.core.log import *;
from code.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;