Fix SA solver: track currentScore, add move-to-room, 20 restarts

This commit is contained in:
Ian Gulliver
2026-02-15 21:37:39 -08:00
parent 5dd20ab6df
commit 5ca85196a2

166
main.go
View File

@@ -948,62 +948,132 @@ func handleSolve(db *sql.DB) http.HandlerFunc {
} }
} }
initialAssignment := make([]int, n)
copy(initialAssignment, assignment)
bestAssignment := make([]int, n) bestAssignment := make([]int, n)
copy(bestAssignment, assignment) copy(bestAssignment, assignment)
bestScore := score(assignment) bestScore := score(assignment)
temp := 10.0 roomCount := func(a []int, room int) int {
cooling := 0.9995 c := 0
iterations := 50000 for _, r := range a {
if r == room { c++ }
for iter := 0; iter < iterations; iter++ {
i := rand.Intn(n)
j := rand.Intn(n)
if assignment[i] == assignment[j] { continue }
iRoot := ufFind(i)
jRoot := ufFind(j)
if iRoot == jRoot { continue }
iGroup := groups[iRoot]
jGroup := groups[jRoot]
iRoom := assignment[i]
jRoom := assignment[j]
roomI := 0
roomJ := 0
for _, a := range assignment {
if a == iRoom { roomI++ }
if a == jRoom { roomJ++ }
} }
return c
}
newRoomI := roomI - len(iGroup) + len(jGroup) for restart := 0; restart < 20; restart++ {
newRoomJ := roomJ - len(jGroup) + len(iGroup) if restart == 0 {
if newRoomI > roomSize || newRoomJ > roomSize { continue } copy(assignment, initialAssignment)
for _, m := range iGroup { assignment[m] = jRoom }
for _, m := range jGroup { assignment[m] = iRoom }
if !feasible(assignment) {
for _, m := range iGroup { assignment[m] = iRoom }
for _, m := range jGroup { assignment[m] = jRoom }
continue
}
newScore := score(assignment)
delta := newScore - bestScore
if delta > 0 || rand.Float64() < math.Exp(float64(delta)/temp) {
if newScore > bestScore {
bestScore = newScore
copy(bestAssignment, assignment)
}
} else { } else {
for _, m := range iGroup { assignment[m] = iRoom } perm := rand.Perm(len(groupList))
for _, m := range jGroup { assignment[m] = jRoom } for i := range roomCap { roomCap[i] = roomSize }
ok := true
for _, pi := range perm {
grp := groupList[pi]
placed := false
order := rand.Perm(numRooms)
for _, room := range order {
if roomCap[room] < len(grp) { continue }
valid := true
for _, member := range grp {
for p := range mustApart {
partner := -1
if p[0] == member { partner = p[1] }
if p[1] == member { partner = p[0] }
if partner >= 0 && assignment[partner] == room {
valid = false
break
}
}
if !valid { break }
}
if !valid { continue }
for _, member := range grp { assignment[member] = room }
roomCap[room] -= len(grp)
placed = true
break
}
if !placed {
ok = false
break
}
}
if !ok {
copy(assignment, initialAssignment)
}
}
currentScore := score(assignment)
if currentScore > bestScore {
bestScore = currentScore
copy(bestAssignment, assignment)
}
temp := 10.0
cooling := 0.9995
iterations := 50000
for iter := 0; iter < iterations; iter++ {
i := rand.Intn(n)
iRoot := ufFind(i)
iGroup := groups[iRoot]
iRoom := assignment[i]
if rand.Intn(2) == 0 {
targetRoom := rand.Intn(numRooms)
if targetRoom == iRoom { continue }
newCount := roomCount(assignment, targetRoom) + len(iGroup)
if newCount > roomSize { continue }
for _, m := range iGroup { assignment[m] = targetRoom }
if !feasible(assignment) {
for _, m := range iGroup { assignment[m] = iRoom }
continue
}
newScore := score(assignment)
delta := newScore - currentScore
if delta >= 0 || rand.Float64() < math.Exp(float64(delta)/temp) {
currentScore = newScore
if currentScore > bestScore {
bestScore = currentScore
copy(bestAssignment, assignment)
}
} else {
for _, m := range iGroup { assignment[m] = iRoom }
}
} else {
j := rand.Intn(n)
if assignment[j] == iRoom { continue }
jRoot := ufFind(j)
if iRoot == jRoot { continue }
jGroup := groups[jRoot]
jRoom := assignment[j]
newRoomI := roomCount(assignment, iRoom) - len(iGroup) + len(jGroup)
newRoomJ := roomCount(assignment, jRoom) - len(jGroup) + len(iGroup)
if newRoomI > roomSize || newRoomJ > roomSize { continue }
for _, m := range iGroup { assignment[m] = jRoom }
for _, m := range jGroup { assignment[m] = iRoom }
if !feasible(assignment) {
for _, m := range iGroup { assignment[m] = iRoom }
for _, m := range jGroup { assignment[m] = jRoom }
continue
}
newScore := score(assignment)
delta := newScore - currentScore
if delta >= 0 || rand.Float64() < math.Exp(float64(delta)/temp) {
currentScore = newScore
if currentScore > bestScore {
bestScore = currentScore
copy(bestAssignment, assignment)
}
} else {
for _, m := range iGroup { assignment[m] = iRoom }
for _, m := range jGroup { assignment[m] = jRoom }
}
}
temp *= cooling
} }
temp *= cooling
} }
type roomMember struct { type roomMember struct {