174 lines
5.1 KiB
Go
174 lines
5.1 KiB
Go
|
package poison
|
||
|
|
||
|
/* ---------------------------------------------------------------- *
|
||
|
* IMPORTS
|
||
|
* ---------------------------------------------------------------- */
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
|
||
|
"ads/internal/core/logging"
|
||
|
"ads/internal/core/metrics"
|
||
|
"ads/internal/core/utils"
|
||
|
)
|
||
|
|
||
|
/* ---------------------------------------------------------------- *
|
||
|
* GLOBAL VARIABLES/CONSTANTS
|
||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||
|
|
||
|
//
|
||
|
|
||
|
/* ---------------------------------------------------------------- *
|
||
|
* ALGORITHM find poison
|
||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||
|
|
||
|
/*
|
||
|
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.
|
||
|
*/
|
||
|
func FindPoison(L []int) int {
|
||
|
logging.LogDebug("Bereite Vorkoster vor")
|
||
|
n := len(L)
|
||
|
testers := [][]int{}
|
||
|
for i := 0; i < n; i++ {
|
||
|
metrics.AddSpaceCost()
|
||
|
logging.LogDebug(fmt.Sprintf("Füge Vorkoster hinzu, der nur Getränk %[1]v testet.", i))
|
||
|
testers = append(testers, []int{i})
|
||
|
}
|
||
|
logging.LogDebug("Warte auf Effekte")
|
||
|
effects := waitForEffects(L, testers)
|
||
|
logging.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
|
||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||
|
|
||
|
/*
|
||
|
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.
|
||
|
*/
|
||
|
func FindPoisonFast(L []int) int {
|
||
|
logging.LogDebug("Bereite Vorkoster vor")
|
||
|
n := len(L)
|
||
|
p := utils.LengthOfBinary(n)
|
||
|
testers := [][]int{}
|
||
|
// Für jedes Bit i=0 bis p-1 ...
|
||
|
for i := 0; i < p; i++ {
|
||
|
tester0 := []int{}
|
||
|
tester1 := []int{}
|
||
|
for k := 0; k < n; k++ {
|
||
|
if utils.NthBit(k, i) == 0 {
|
||
|
tester0 = append(tester0, k)
|
||
|
} else {
|
||
|
tester1 = append(tester1, k)
|
||
|
}
|
||
|
}
|
||
|
/*
|
||
|
* 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.
|
||
|
*/
|
||
|
metrics.AddSpaceCost(1)
|
||
|
logging.LogDebug(fmt.Sprintf("Füge Vorkoster hinzu, der alle Getränke k testet mit %[1]v. Bit von k = 0.", i))
|
||
|
testers = append(testers, tester0)
|
||
|
testers = append(testers, tester1)
|
||
|
}
|
||
|
logging.LogDebug("Warte auf Effekte")
|
||
|
effects := waitForEffects(L, testers)
|
||
|
logging.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
|
||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||
|
|
||
|
/*
|
||
|
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.
|
||
|
*/
|
||
|
func waitForEffects(L []int, testers [][]int) []int {
|
||
|
m := len(testers)
|
||
|
effects := make([]int, m)
|
||
|
for i, tester := range testers {
|
||
|
effect := 0
|
||
|
for _, k := range tester {
|
||
|
effect += L[k]
|
||
|
}
|
||
|
effects[i] = effect
|
||
|
}
|
||
|
return effects
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
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.
|
||
|
*/
|
||
|
func evaluateEffects(testers [][]int, effects []int) []int {
|
||
|
var states = map[int]bool{}
|
||
|
var poisened = []int{}
|
||
|
|
||
|
// Werte Effekte aus, um Gift zu lokalisieren:
|
||
|
// Zuerst die Indexes der Getränke bei allen vergifteten Tester zusammenführen:
|
||
|
for i, tester := range testers {
|
||
|
// wenn Tester positiv testet, dann ist eines der von ihm probierten Getränks vergiftet
|
||
|
if effects[i] > 0 {
|
||
|
// markiere alle vom Tester probierten Getränke
|
||
|
for _, k := range tester {
|
||
|
states[k] = true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// jetzt eliminieren wir alle Getränke, die von nicht vergifteten Testern konsumiert wurden:
|
||
|
for i, tester := range testers {
|
||
|
// wenn Tester negativ testet, dann ist KEINES der von ihm probierten Getränks vergiftet
|
||
|
if effects[i] == 0 {
|
||
|
// schließe alle vom Tester probierten Getränke aus
|
||
|
for _, k := range tester {
|
||
|
states[k] = false
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// was übrig bleibt sind die vergifteten Getränke, vorausgesetzt genug Vorkoster wurden ausgewählt
|
||
|
for k, state := range states {
|
||
|
if state {
|
||
|
poisened = append(poisened, k)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return poisened
|
||
|
}
|