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