From 16e1080e7652643271d9d908795155c5048a8601 Mon Sep 17 00:00:00 2001 From: Ian Gulliver Date: Mon, 16 Feb 2026 12:43:32 -0800 Subject: [PATCH] Remove unused solver algorithms (SA, hybrid, original hill climb) --- cmd/solver-tune/main.go | 155 ++------- solver/solver.go | 676 ---------------------------------------- 2 files changed, 26 insertions(+), 805 deletions(-) diff --git a/cmd/solver-tune/main.go b/cmd/solver-tune/main.go index 8f12bf7..f679621 100644 --- a/cmd/solver-tune/main.go +++ b/cmd/solver-tune/main.go @@ -139,18 +139,10 @@ func printStats(label string, results []runResult, runs int) { func main() { dir := flag.String("dir", "tmp", "directory with trip/students/constraints JSON files") runs := flag.Int("runs", 20, "number of solver runs per parameter set") - algo := flag.String("algo", "both", "algorithm: hillclimb, fast, sa, hybrid, both, all") - numRandom := flag.String("random", "30", "comma-separated random placement counts (hillclimb)") - numPerturb := flag.String("perturb", "200", "comma-separated perturbation counts (hillclimb)") - perturbMin := flag.Int("pmin", 2, "perturbation min groups (hillclimb)") - perturbMax := flag.Int("pmax", 5, "perturbation max groups (hillclimb)") - saRestarts := flag.String("restarts", "20", "comma-separated SA/hybrid restart counts") - saSteps := flag.String("steps", "10000", "comma-separated SA step counts") - hybridSteps := flag.String("hsteps", "5000", "comma-separated hybrid SA step counts") - saTempHigh := flag.Float64("thigh", 5.0, "SA initial temperature") - saTempLow := flag.Float64("tlow", 0.01, "SA final temperature") - hybridTempHigh := flag.Float64("hthigh", 10.0, "hybrid SA initial temperature") - hybridTempLow := flag.Float64("htlow", 0.1, "hybrid SA final temperature") + numRandom := flag.String("random", "50", "comma-separated random placement counts") + numPerturb := flag.String("perturb", "750", "comma-separated perturbation counts") + perturbMin := flag.Int("pmin", 3, "perturbation min groups") + perturbMax := flag.Int("pmax", 8, "perturbation max groups") flag.Parse() tripBytes, err := os.ReadFile(*dir + "/1") @@ -201,127 +193,32 @@ func main() { fmt.Printf("Prefer Not multiple: %d, No Prefer cost: %d\n", trip.PreferNotMultiple, trip.NoPreferCost) fmt.Printf("Runs per config: %d\n\n", *runs) - if *algo == "hillclimb" || *algo == "both" { - randomCounts := parseIntList(*numRandom) - perturbCounts := parseIntList(*numPerturb) - for _, nr := range randomCounts { - for _, np := range perturbCounts { - params := solver.Params{ - NumRandom: nr, - NumPerturb: np, - PerturbMin: *perturbMin, - PerturbMax: *perturbMax, - } - var results []runResult - for run := range *runs { - rng := rand.New(rand.NewSource(int64(run * 31337))) - start := time.Now() - sols := solver.Solve(n, trip.RoomSize, trip.PreferNotMultiple, trip.NoPreferCost, constraints, params, rng) - elapsed := time.Since(start) - if len(sols) > 0 { - var assignments [][]int - for _, s := range sols { - assignments = append(assignments, s.Assignment) - } - results = append(results, runResult{sols[0].Score, assignments, elapsed}) - } - } - label := fmt.Sprintf("hillclimb random=%d perturb=%d pmin=%d pmax=%d", nr, np, *perturbMin, *perturbMax) - printStats(label, results, *runs) + randomCounts := parseIntList(*numRandom) + perturbCounts := parseIntList(*numPerturb) + for _, nr := range randomCounts { + for _, np := range perturbCounts { + params := solver.Params{ + NumRandom: nr, + NumPerturb: np, + PerturbMin: *perturbMin, + PerturbMax: *perturbMax, } - } - } - - if *algo == "fast" || *algo == "both" || *algo == "all" { - randomCounts := parseIntList(*numRandom) - perturbCounts := parseIntList(*numPerturb) - for _, nr := range randomCounts { - for _, np := range perturbCounts { - params := solver.Params{ - NumRandom: nr, - NumPerturb: np, - PerturbMin: *perturbMin, - PerturbMax: *perturbMax, - } - var results []runResult - for run := range *runs { - rng := rand.New(rand.NewSource(int64(run * 31337))) - start := time.Now() - sols := solver.SolveFast(n, trip.RoomSize, trip.PreferNotMultiple, trip.NoPreferCost, constraints, params, rng) - elapsed := time.Since(start) - if len(sols) > 0 { - var assignments [][]int - for _, s := range sols { - assignments = append(assignments, s.Assignment) - } - results = append(results, runResult{sols[0].Score, assignments, elapsed}) + var results []runResult + for run := range *runs { + rng := rand.New(rand.NewSource(int64(run * 31337))) + start := time.Now() + sols := solver.SolveFast(n, trip.RoomSize, trip.PreferNotMultiple, trip.NoPreferCost, constraints, params, rng) + elapsed := time.Since(start) + if len(sols) > 0 { + var assignments [][]int + for _, s := range sols { + assignments = append(assignments, s.Assignment) } + results = append(results, runResult{sols[0].Score, assignments, elapsed}) } - label := fmt.Sprintf("fast random=%d perturb=%d pmin=%d pmax=%d", nr, np, *perturbMin, *perturbMax) - printStats(label, results, *runs) - } - } - } - - if *algo == "sa" || *algo == "all" { - restartCounts := parseIntList(*saRestarts) - stepCounts := parseIntList(*saSteps) - for _, nr := range restartCounts { - for _, ns := range stepCounts { - params := solver.SAParams{ - Restarts: nr, - Steps: ns, - TempHigh: *saTempHigh, - TempLow: *saTempLow, - } - var results []runResult - for run := range *runs { - rng := rand.New(rand.NewSource(int64(run * 31337))) - start := time.Now() - sols := solver.SolveSA(n, trip.RoomSize, trip.PreferNotMultiple, trip.NoPreferCost, constraints, params, rng) - elapsed := time.Since(start) - if len(sols) > 0 { - var assignments [][]int - for _, s := range sols { - assignments = append(assignments, s.Assignment) - } - results = append(results, runResult{sols[0].Score, assignments, elapsed}) - } - } - label := fmt.Sprintf("sa restarts=%d steps=%d thigh=%.1f tlow=%.3f", nr, ns, *saTempHigh, *saTempLow) - printStats(label, results, *runs) - } - } - } - - if *algo == "hybrid" || *algo == "both" || *algo == "all" { - restartCounts := parseIntList(*saRestarts) - stepCounts := parseIntList(*hybridSteps) - for _, nr := range restartCounts { - for _, ns := range stepCounts { - params := solver.HybridParams{ - SARestarts: nr, - SASteps: ns, - TempHigh: *hybridTempHigh, - TempLow: *hybridTempLow, - } - var results []runResult - for run := range *runs { - rng := rand.New(rand.NewSource(int64(run * 31337))) - start := time.Now() - sols := solver.SolveHybrid(n, trip.RoomSize, trip.PreferNotMultiple, trip.NoPreferCost, constraints, params, rng) - elapsed := time.Since(start) - if len(sols) > 0 { - var assignments [][]int - for _, s := range sols { - assignments = append(assignments, s.Assignment) - } - results = append(results, runResult{sols[0].Score, assignments, elapsed}) - } - } - label := fmt.Sprintf("hybrid restarts=%d steps=%d thigh=%.1f tlow=%.3f", nr, ns, *hybridTempHigh, *hybridTempLow) - printStats(label, results, *runs) } + label := fmt.Sprintf("random=%d perturb=%d pmin=%d pmax=%d", nr, np, *perturbMin, *perturbMax) + printStats(label, results, *runs) } } } diff --git a/solver/solver.go b/solver/solver.go index ea52b00..97be1b8 100644 --- a/solver/solver.go +++ b/solver/solver.go @@ -1,7 +1,6 @@ package solver import ( - "math" "math/rand" "slices" "strconv" @@ -28,34 +27,6 @@ var DefaultParams = Params{ PerturbMax: 8, } -type SAParams struct { - Restarts int - Steps int - TempHigh float64 - TempLow float64 -} - -var DefaultSAParams = SAParams{ - Restarts: 20, - Steps: 10000, - TempHigh: 5.0, - TempLow: 0.01, -} - -type HybridParams struct { - SARestarts int - SASteps int - TempHigh float64 - TempLow float64 -} - -var DefaultHybridParams = HybridParams{ - SARestarts: 50, - SASteps: 5000, - TempHigh: 10.0, - TempLow: 0.1, -} - type Solution struct { Assignment []int Score int @@ -251,14 +222,6 @@ func (s *solverState) score(assignment []int) int { return sc } -func (s *solverState) roomCounts(assignment []int) []int { - counts := make([]int, s.numRooms) - for _, room := range assignment { - counts[room]++ - } - return counts -} - func (s *solverState) feasibleForGroup(assignment []int, groupRoot int, room int) bool { for _, m := range s.groups[groupRoot] { for _, partner := range s.mustApartFor[m] { @@ -634,200 +597,6 @@ func (t *solutionTracker) add(a []int, s int) { } } -func Solve(n, roomSize, pnMultiple, npCost int, constraints []Constraint, params Params, rng *rand.Rand) []Solution { - if n == 0 { - return nil - } - - st := newSolverState(n, roomSize, pnMultiple, npCost, constraints) - if st.hasHardConflict() { - return nil - } - - assignment := make([]int, n) - if !st.initialPlacement(assignment) { - for i := range n { - assignment[i] = i % st.numRooms - } - } - - initialAssignment := slices.Clone(assignment) - tracker := newTracker(assignment, st.score(assignment)) - - roomCount := func(a []int, room int) int { - c := 0 - for _, r := range a { - if r == room { - c++ - } - } - return c - } - - feasible := func(a []int) bool { - for p := range st.mustApart { - if a[p[0]] == a[p[1]] { - return false - } - } - rc := map[int]int{} - for _, room := range a { - rc[room]++ - } - for _, cnt := range rc { - if cnt > roomSize { - return false - } - } - return true - } - - hillClimb := func(assignment []int) int { - currentScore := st.score(assignment) - for { - bestDelta := 0 - bestMove := -1 - bestTarget := -1 - bestSwapJ := -1 - - for gi, gRoot := range st.uniqueGroups { - grp := st.groups[gRoot] - gRoom := assignment[grp[0]] - - for room := range st.numRooms { - if room == gRoom { - continue - } - if roomCount(assignment, room)+len(grp) > roomSize { - continue - } - for _, m := range grp { - assignment[m] = room - } - if feasible(assignment) { - delta := st.score(assignment) - currentScore - if delta > bestDelta { - bestDelta = delta - bestMove = gi - bestTarget = room - bestSwapJ = -1 - } - } - for _, m := range grp { - assignment[m] = gRoom - } - } - - for gj := gi + 1; gj < len(st.uniqueGroups); gj++ { - grp2 := st.groups[st.uniqueGroups[gj]] - g2Room := assignment[grp2[0]] - if gRoom == g2Room { - continue - } - newGRoom := roomCount(assignment, gRoom) - len(grp) + len(grp2) - newG2Room := roomCount(assignment, g2Room) - len(grp2) + len(grp) - if newGRoom > roomSize || newG2Room > roomSize { - continue - } - for _, m := range grp { - assignment[m] = g2Room - } - for _, m := range grp2 { - assignment[m] = gRoom - } - if feasible(assignment) { - delta := st.score(assignment) - currentScore - if delta > bestDelta { - bestDelta = delta - bestMove = gi - bestTarget = -1 - bestSwapJ = gj - } - } - for _, m := range grp { - assignment[m] = gRoom - } - for _, m := range grp2 { - assignment[m] = g2Room - } - } - } - - if bestDelta <= 0 { - break - } - - grp := st.groups[st.uniqueGroups[bestMove]] - gRoom := assignment[grp[0]] - if bestSwapJ < 0 { - for _, m := range grp { - assignment[m] = bestTarget - } - } else { - grp2 := st.groups[st.uniqueGroups[bestSwapJ]] - g2Room := assignment[grp2[0]] - for _, m := range grp { - assignment[m] = g2Room - } - for _, m := range grp2 { - assignment[m] = gRoom - } - } - currentScore += bestDelta - } - return currentScore - } - - perturb := func(src []int, count int) { - copy(assignment, src) - indices := rng.Perm(len(st.uniqueGroups)) - count = min(count, len(indices)) - for _, gi := range indices[:count] { - grp := st.groups[st.uniqueGroups[gi]] - oldRoom := assignment[grp[0]] - rooms := rng.Perm(st.numRooms) - for _, room := range rooms { - if room == oldRoom { - continue - } - if roomCount(assignment, room)+len(grp) > roomSize { - continue - } - for _, m := range grp { - assignment[m] = room - } - if feasible(assignment) { - break - } - for _, m := range grp { - assignment[m] = oldRoom - } - } - } - } - - copy(assignment, initialAssignment) - tracker.add(assignment, hillClimb(assignment)) - - for range params.NumRandom { - if st.randomPlacement(assignment, rng) { - tracker.add(assignment, hillClimb(assignment)) - } - } - - for range params.NumPerturb { - src := tracker.bestSolutions[rng.Intn(len(tracker.bestSolutions))] - perturb(src, params.PerturbMin+rng.Intn(params.PerturbMax-params.PerturbMin)) - tracker.add(assignment, hillClimb(assignment)) - } - - results := make([]Solution, len(tracker.bestSolutions)) - for i, sol := range tracker.bestSolutions { - results[i] = Solution{Assignment: sol, Score: tracker.bestScore} - } - return results -} - func SolveFast(n, roomSize, pnMultiple, npCost int, constraints []Constraint, params Params, rng *rand.Rand) []Solution { if n == 0 { return nil @@ -926,448 +695,3 @@ func SolveFast(n, roomSize, pnMultiple, npCost int, constraints []Constraint, pa return results } -func SolveSA(n, roomSize, pnMultiple, npCost int, constraints []Constraint, params SAParams, rng *rand.Rand) []Solution { - if n == 0 { - return nil - } - - st := newSolverState(n, roomSize, pnMultiple, npCost, constraints) - if st.hasHardConflict() { - return nil - } - - assignment := make([]int, n) - if !st.initialPlacement(assignment) { - for i := range n { - assignment[i] = i % st.numRooms - } - } - - tracker := newTracker(assignment, st.score(assignment)) - counts := st.roomCounts(assignment) - - tryMove := func(assignment []int, counts []int, rng *rand.Rand) (gi int, oldRoom int, newRoom int, swapGi int, ok bool) { - nGroups := len(st.uniqueGroups) - gi = rng.Intn(nGroups) - grp := st.groups[st.uniqueGroups[gi]] - oldRoom = assignment[grp[0]] - - if rng.Intn(3) == 0 && nGroups > 1 { - swapGi = rng.Intn(nGroups - 1) - if swapGi >= gi { - swapGi++ - } - grp2 := st.groups[st.uniqueGroups[swapGi]] - g2Room := assignment[grp2[0]] - if oldRoom == g2Room { - return 0, 0, 0, 0, false - } - newOldCount := counts[oldRoom] - len(grp) + len(grp2) - newG2Count := counts[g2Room] - len(grp2) + len(grp) - if newOldCount > roomSize || newG2Count > roomSize { - return 0, 0, 0, 0, false - } - for _, m := range grp { - assignment[m] = g2Room - } - for _, m := range grp2 { - assignment[m] = oldRoom - } - if !st.feasibleForGroup(assignment, st.uniqueGroups[gi], g2Room) || - !st.feasibleForGroup(assignment, st.uniqueGroups[swapGi], oldRoom) { - for _, m := range grp { - assignment[m] = oldRoom - } - for _, m := range grp2 { - assignment[m] = g2Room - } - return 0, 0, 0, 0, false - } - for _, m := range grp { - assignment[m] = oldRoom - } - for _, m := range grp2 { - assignment[m] = g2Room - } - return gi, oldRoom, g2Room, swapGi, true - } - - newRoom = rng.Intn(st.numRooms - 1) - if newRoom >= oldRoom { - newRoom++ - } - if counts[newRoom]+len(grp) > roomSize { - return 0, 0, 0, 0, false - } - for _, m := range grp { - assignment[m] = newRoom - } - if !st.feasibleForGroup(assignment, st.uniqueGroups[gi], newRoom) { - for _, m := range grp { - assignment[m] = oldRoom - } - return 0, 0, 0, 0, false - } - for _, m := range grp { - assignment[m] = oldRoom - } - return gi, oldRoom, newRoom, -1, true - } - - applyMove := func(assignment []int, counts []int, gi, oldRoom, newRoom, swapGi int) { - grp := st.groups[st.uniqueGroups[gi]] - if swapGi >= 0 { - grp2 := st.groups[st.uniqueGroups[swapGi]] - g2Room := assignment[grp2[0]] - counts[oldRoom] -= len(grp) - counts[g2Room] -= len(grp2) - for _, m := range grp { - assignment[m] = g2Room - } - for _, m := range grp2 { - assignment[m] = oldRoom - } - counts[g2Room] += len(grp) - counts[oldRoom] += len(grp2) - } else { - counts[oldRoom] -= len(grp) - for _, m := range grp { - assignment[m] = newRoom - } - counts[newRoom] += len(grp) - } - } - - for restart := range params.Restarts { - if restart > 0 { - if !st.randomPlacement(assignment, rng) { - continue - } - counts = st.roomCounts(assignment) - } - - currentScore := st.score(assignment) - - for step := range params.Steps { - t := params.TempHigh * math.Pow(params.TempLow/params.TempHigh, float64(step)/float64(params.Steps-1)) - - gi, oldRoom, newRoom, swapGi, ok := tryMove(assignment, counts, rng) - if !ok { - continue - } - - applyMove(assignment, counts, gi, oldRoom, newRoom, swapGi) - newScore := st.score(assignment) - delta := newScore - currentScore - - if delta >= 0 || rng.Float64() < math.Exp(float64(delta)/t) { - currentScore = newScore - tracker.add(assignment, currentScore) - } else { - if swapGi >= 0 { - grp := st.groups[st.uniqueGroups[gi]] - grp2 := st.groups[st.uniqueGroups[swapGi]] - g2Room := assignment[grp2[0]] - counts[g2Room] -= len(grp2) - counts[oldRoom] -= len(grp) - for _, m := range grp { - assignment[m] = oldRoom - } - for _, m := range grp2 { - assignment[m] = g2Room - } - counts[oldRoom] += len(grp) - counts[g2Room] += len(grp2) - } else { - grp := st.groups[st.uniqueGroups[gi]] - counts[newRoom] -= len(grp) - for _, m := range grp { - assignment[m] = oldRoom - } - counts[oldRoom] += len(grp) - } - } - } - } - - results := make([]Solution, len(tracker.bestSolutions)) - for i, sol := range tracker.bestSolutions { - results[i] = Solution{Assignment: sol, Score: tracker.bestScore} - } - return results -} - -func SolveHybrid(n, roomSize, pnMultiple, npCost int, constraints []Constraint, params HybridParams, rng *rand.Rand) []Solution { - if n == 0 { - return nil - } - - st := newSolverState(n, roomSize, pnMultiple, npCost, constraints) - if st.hasHardConflict() { - return nil - } - - numRooms := st.numRooms - - roomCount := func(a []int, room int) int { - c := 0 - for _, r := range a { - if r == room { - c++ - } - } - return c - } - - feasible := func(a []int) bool { - for p := range st.mustApart { - if a[p[0]] == a[p[1]] { - return false - } - } - rc := map[int]int{} - for _, room := range a { - rc[room]++ - } - for _, cnt := range rc { - if cnt > roomSize { - return false - } - } - return true - } - - hillClimb := func(assignment []int) int { - currentScore := st.score(assignment) - for { - bestDelta := 0 - bestMove := -1 - bestTarget := -1 - bestSwapJ := -1 - - for gi, gRoot := range st.uniqueGroups { - grp := st.groups[gRoot] - gRoom := assignment[grp[0]] - - for room := range numRooms { - if room == gRoom { - continue - } - if roomCount(assignment, room)+len(grp) > roomSize { - continue - } - for _, m := range grp { - assignment[m] = room - } - if feasible(assignment) { - delta := st.score(assignment) - currentScore - if delta > bestDelta { - bestDelta = delta - bestMove = gi - bestTarget = room - bestSwapJ = -1 - } - } - for _, m := range grp { - assignment[m] = gRoom - } - } - - for gj := gi + 1; gj < len(st.uniqueGroups); gj++ { - grp2 := st.groups[st.uniqueGroups[gj]] - g2Room := assignment[grp2[0]] - if gRoom == g2Room { - continue - } - newGRoom := roomCount(assignment, gRoom) - len(grp) + len(grp2) - newG2Room := roomCount(assignment, g2Room) - len(grp2) + len(grp) - if newGRoom > roomSize || newG2Room > roomSize { - continue - } - for _, m := range grp { - assignment[m] = g2Room - } - for _, m := range grp2 { - assignment[m] = gRoom - } - if feasible(assignment) { - delta := st.score(assignment) - currentScore - if delta > bestDelta { - bestDelta = delta - bestMove = gi - bestTarget = -1 - bestSwapJ = gj - } - } - for _, m := range grp { - assignment[m] = gRoom - } - for _, m := range grp2 { - assignment[m] = g2Room - } - } - } - - if bestDelta <= 0 { - break - } - - grp := st.groups[st.uniqueGroups[bestMove]] - gRoom := assignment[grp[0]] - if bestSwapJ < 0 { - for _, m := range grp { - assignment[m] = bestTarget - } - } else { - grp2 := st.groups[st.uniqueGroups[bestSwapJ]] - g2Room := assignment[grp2[0]] - for _, m := range grp { - assignment[m] = g2Room - } - for _, m := range grp2 { - assignment[m] = gRoom - } - } - currentScore += bestDelta - } - return currentScore - } - - assignment := make([]int, n) - if !st.initialPlacement(assignment) { - for i := range n { - assignment[i] = i % numRooms - } - } - - tracker := newTracker(assignment, st.score(assignment)) - tracker.add(assignment, hillClimb(assignment)) - - counts := make([]int, numRooms) - - for restart := range params.SARestarts { - if restart == 0 { - copy(assignment, tracker.bestSolutions[0]) - } else { - if !st.randomPlacement(assignment, rng) { - continue - } - } - for i := range counts { - counts[i] = 0 - } - for _, room := range assignment { - counts[room]++ - } - - currentScore := st.score(assignment) - - for step := range params.SASteps { - t := params.TempHigh * math.Pow(params.TempLow/params.TempHigh, float64(step)/float64(params.SASteps-1)) - - nGroups := len(st.uniqueGroups) - gi := rng.Intn(nGroups) - grp := st.groups[st.uniqueGroups[gi]] - oldRoom := assignment[grp[0]] - - var newRoom int - swapGi := -1 - moved := false - - if rng.Intn(3) == 0 && nGroups > 1 { - swapGi = rng.Intn(nGroups - 1) - if swapGi >= gi { - swapGi++ - } - grp2 := st.groups[st.uniqueGroups[swapGi]] - g2Room := assignment[grp2[0]] - if oldRoom != g2Room { - newOld := counts[oldRoom] - len(grp) + len(grp2) - newG2 := counts[g2Room] - len(grp2) + len(grp) - if newOld <= roomSize && newG2 <= roomSize { - for _, m := range grp { - assignment[m] = g2Room - } - for _, m := range grp2 { - assignment[m] = oldRoom - } - if st.feasibleForGroup(assignment, st.uniqueGroups[gi], g2Room) && - st.feasibleForGroup(assignment, st.uniqueGroups[swapGi], oldRoom) { - newRoom = g2Room - moved = true - counts[oldRoom] = newOld - counts[g2Room] = newG2 - } else { - for _, m := range grp { - assignment[m] = oldRoom - } - for _, m := range grp2 { - assignment[m] = g2Room - } - } - } - } - } - - if !moved { - swapGi = -1 - newRoom = rng.Intn(numRooms - 1) - if newRoom >= oldRoom { - newRoom++ - } - if counts[newRoom]+len(grp) <= roomSize { - for _, m := range grp { - assignment[m] = newRoom - } - if st.feasibleForGroup(assignment, st.uniqueGroups[gi], newRoom) { - moved = true - counts[oldRoom] -= len(grp) - counts[newRoom] += len(grp) - } else { - for _, m := range grp { - assignment[m] = oldRoom - } - } - } - } - - if !moved { - continue - } - - newScore := st.score(assignment) - delta := newScore - currentScore - - if delta >= 0 || rng.Float64() < math.Exp(float64(delta)/t) { - currentScore = newScore - } else { - if swapGi >= 0 { - grp2 := st.groups[st.uniqueGroups[swapGi]] - g2Room := assignment[grp2[0]] - for _, m := range grp { - assignment[m] = oldRoom - } - for _, m := range grp2 { - assignment[m] = g2Room - } - counts[oldRoom] = counts[oldRoom] - len(grp2) + len(grp) - counts[g2Room] = counts[g2Room] - len(grp) + len(grp2) - } else { - for _, m := range grp { - assignment[m] = oldRoom - } - counts[newRoom] -= len(grp) - counts[oldRoom] += len(grp) - } - } - } - - tracker.add(assignment, hillClimb(assignment)) - } - - results := make([]Solution, len(tracker.bestSolutions)) - for i, sol := range tracker.bestSolutions { - results[i] = Solution{Assignment: sol, Score: tracker.bestScore} - } - return results -}