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

120
main.go
View File

@@ -948,63 +948,133 @@ 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)
roomCount := func(a []int, room int) int {
c := 0
for _, r := range a {
if r == room { c++ }
}
return c
}
for restart := 0; restart < 20; restart++ {
if restart == 0 {
copy(assignment, initialAssignment)
} else {
perm := rand.Perm(len(groupList))
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 temp := 10.0
cooling := 0.9995 cooling := 0.9995
iterations := 50000 iterations := 50000
for iter := 0; iter < iterations; iter++ { for iter := 0; iter < iterations; iter++ {
i := rand.Intn(n) i := rand.Intn(n)
j := rand.Intn(n)
if assignment[i] == assignment[j] { continue }
iRoot := ufFind(i) iRoot := ufFind(i)
jRoot := ufFind(j)
if iRoot == jRoot { continue }
iGroup := groups[iRoot] iGroup := groups[iRoot]
jGroup := groups[jRoot]
iRoom := assignment[i] iRoom := assignment[i]
jRoom := assignment[j]
roomI := 0 if rand.Intn(2) == 0 {
roomJ := 0 targetRoom := rand.Intn(numRooms)
for _, a := range assignment { if targetRoom == iRoom { continue }
if a == iRoom { roomI++ } newCount := roomCount(assignment, targetRoom) + len(iGroup)
if a == jRoom { roomJ++ } 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)
newRoomI := roomI - len(iGroup) + len(jGroup) delta := newScore - currentScore
newRoomJ := roomJ - len(jGroup) + len(iGroup) 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 } if newRoomI > roomSize || newRoomJ > roomSize { continue }
for _, m := range iGroup { assignment[m] = jRoom } for _, m := range iGroup { assignment[m] = jRoom }
for _, m := range jGroup { assignment[m] = iRoom } for _, m := range jGroup { assignment[m] = iRoom }
if !feasible(assignment) { if !feasible(assignment) {
for _, m := range iGroup { assignment[m] = iRoom } for _, m := range iGroup { assignment[m] = iRoom }
for _, m := range jGroup { assignment[m] = jRoom } for _, m := range jGroup { assignment[m] = jRoom }
continue continue
} }
newScore := score(assignment) newScore := score(assignment)
delta := newScore - bestScore delta := newScore - currentScore
if delta > 0 || rand.Float64() < math.Exp(float64(delta)/temp) { if delta >= 0 || rand.Float64() < math.Exp(float64(delta)/temp) {
if newScore > bestScore { currentScore = newScore
bestScore = newScore if currentScore > bestScore {
bestScore = currentScore
copy(bestAssignment, assignment) copy(bestAssignment, assignment)
} }
} else { } else {
for _, m := range iGroup { assignment[m] = iRoom } for _, m := range iGroup { assignment[m] = iRoom }
for _, m := range jGroup { assignment[m] = jRoom } for _, m := range jGroup { assignment[m] = jRoom }
} }
}
temp *= cooling temp *= cooling
} }
}
type roomMember struct { type roomMember struct {
ID int64 `json:"id"` ID int64 `json:"id"`