ads1_2021/code/golang/pkg/algorithms/search/poison/poison.go

172 lines
5.1 KiB
Go

package poison
/* ---------------------------------------------------------------- *
* IMPORTS
* ---------------------------------------------------------------- */
import (
"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.Debug("Bereite Vorkoster vor")
n := len(L)
testers := [][]int{}
for i := 0; i < n; i++ {
metrics.AddSpaceCost()
logging.Debug("Füge Vorkoster hinzu, der nur Getränk %[1]v testet.", i)
testers = append(testers, []int{i})
}
logging.Debug("Warte auf Effekte")
effects := waitForEffects(L, testers)
logging.Debug("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.Debug("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.Debug("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.Debug("Warte auf Effekte")
effects := waitForEffects(L, testers)
logging.Debug("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
}