ads1_2021/code/algorithms/sum/maxsubsum.py

180 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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;
# 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)).