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 }