diff --git a/code/algorithms/search/exports.py b/code/algorithms/search/exports.py index 0acd389..1662b4b 100644 --- a/code/algorithms/search/exports.py +++ b/code/algorithms/search/exports.py @@ -12,3 +12,5 @@ from code.algorithms.search.jump import JumpSearchLinear; from code.algorithms.search.jump import JumpSearchExponentiell; from code.algorithms.search.ith_smallest import FindIthSmallest; from code.algorithms.search.ith_smallest import FindIthSmallestDC; +from code.algorithms.search.poison import FindPoison; +from code.algorithms.search.poison import FindPoisonFast; diff --git a/code/algorithms/search/poison.py b/code/algorithms/search/poison.py new file mode 100644 index 0000000..add42ee --- /dev/null +++ b/code/algorithms/search/poison.py @@ -0,0 +1,138 @@ +#!/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], **_): + # TODO + return; + +def postChecks(L: List[int], indexes: List[int], **_): + if sum(L) > 0: + assert [ k for k in range(len(L)) if L[k] == True ] == indexes, 'Der Algorithmus hat die vergifteten Getränke nicht erfolgreich bestimmt.'; + else: + assert len(indexes) == 0, 'Der Algorithmus sollte erkennen, dass Gift nicht vorhanden ist.'; + return; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ALGORITHM find poison +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +@algorithmInfos(name='Giftsuche (O(n) Vorkoster)', outputnames='indexes', checks=True, metrics=True, preChecks=preChecks, postChecks=postChecks) +def FindPoison(L: List[bool]) -> List[int]: + ''' + Inputs: L = Liste von Getränken: durch boolesche Werte wird dargestellt, ob ein Getränk vergiftet ist. + Outputs: Die Liste aller Indexes i mit L[i] == true, was den vergifteten Getränken entspricht. + + 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 = waitForEffect(L, testers); + logDebug('Effekte auswerten, um vergiftete Getränke zu lokalisieren.'); + poisened = evaluateEffects(testers, effects); + return poisened; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# ALGORITHM find poison fast +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +@algorithmInfos(name='Giftsuche (O(log(n)) Vorkoster)', outputnames='indexes', 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. + Outputs: Die Liste aller Indexes i mit L[i] == true, was den vergifteten Getränken entspricht. + + 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): + AddToCounter(2); + logDebug('Füge Vorkoster hinzu, der alle Getränke k testet mit {i}. Bit = 1.'.format(i=i)) + testers.append([ k for k in range(n) if nthBit(number=k, digit=i) == 1 ]); + logDebug('Füge Vorkoster hinzu, der alle Getränke k testet mit {i}. Bit = 0.'.format(i=i)) + testers.append([ k for k in range(n) if nthBit(number=k, digit=i) == 0 ]); + logDebug('Warte auf Effekte'); + effects = waitForEffect(L, testers); + logDebug('Effekte auswerten, um vergiftete Getränke zu lokalisieren.'); + poisened = evaluateEffects(testers, effects); + return poisened; + +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# AUXILIARY METHOD wait for effects, evaluate effects +# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +def waitForEffect(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; diff --git a/code/algorithms/sum/maxsubsum.py b/code/algorithms/sum/maxsubsum.py index 9b9d480..bb2ed0b 100644 --- a/code/algorithms/sum/maxsubsum.py +++ b/code/algorithms/sum/maxsubsum.py @@ -37,6 +37,7 @@ def postChecks(L: List[int], **_): 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 @@ -61,6 +62,7 @@ def MaxSubSum(L: List[float]) -> Tuple[float, int, int]: 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 @@ -157,3 +159,21 @@ def rRandSum(L: List[float]) -> Tuple[float, int, int]: 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)). diff --git a/code/config.yml b/code/config.yml index 06c8cfe..a65806e 100644 --- a/code/config.yml +++ b/code/config.yml @@ -32,6 +32,16 @@ parts: L: [1, 3, 5, 7, 11, 13, 16, 17, 23, 33, 34, 35] x: 17 m: 3 + ## Freiwilliges ÜB 2, Aufgabe 4, Beispiel + - command: 'algorithm-search-poison' + description: 'Freiwilliges ÜB2, A4, Beispiel' + inputs: &ref_inputs_ueb2_4 + L: [ false, false, false, false, false, false, false, false, true, false, false, false, false, false ] + # L: [ false, false, false, false, false, false, false, false, true, false, false, false, true, false ] + # L: [ false, false, false, false, false, false, false, false, false, false, false, false, false, false ] + - command: 'algorithm-search-poison-fast' + description: 'Freiwilliges ÜB2, A4, Beispiel' + inputs: *ref_inputs_ueb2_4 ## Freiwilliges ÜB 2, Aufgabe 5 - command: 'algorithm-search-binary' description: 'Freiwilliges ÜB2, A5a' diff --git a/code/main.py b/code/main.py index c33aeda..571bcaa 100644 --- a/code/main.py +++ b/code/main.py @@ -72,6 +72,10 @@ def LoopThroughCases(path: str): FindIthSmallest(L=inputs['L'], i=inputs['i']); elif command == 'algorithm-search-ith-element-dc': FindIthSmallestDC(L=inputs['L'], i=inputs['i']); + elif command == 'algorithm-search-poison': + FindPoison(L=inputs['L']); + elif command == 'algorithm-search-poison-fast': + FindPoisonFast(L=inputs['L']); else: raise ValueError('Command \033[1m{}\033[0m nicht erkannt'.format(command)); except Exception as e: