Remove unused solver algorithms (SA, hybrid, original hill climb)
This commit is contained in:
@@ -139,18 +139,10 @@ func printStats(label string, results []runResult, runs int) {
|
|||||||
func main() {
|
func main() {
|
||||||
dir := flag.String("dir", "tmp", "directory with trip/students/constraints JSON files")
|
dir := flag.String("dir", "tmp", "directory with trip/students/constraints JSON files")
|
||||||
runs := flag.Int("runs", 20, "number of solver runs per parameter set")
|
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", "50", "comma-separated random placement counts")
|
||||||
numRandom := flag.String("random", "30", "comma-separated random placement counts (hillclimb)")
|
numPerturb := flag.String("perturb", "750", "comma-separated perturbation counts")
|
||||||
numPerturb := flag.String("perturb", "200", "comma-separated perturbation counts (hillclimb)")
|
perturbMin := flag.Int("pmin", 3, "perturbation min groups")
|
||||||
perturbMin := flag.Int("pmin", 2, "perturbation min groups (hillclimb)")
|
perturbMax := flag.Int("pmax", 8, "perturbation max groups")
|
||||||
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")
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
tripBytes, err := os.ReadFile(*dir + "/1")
|
tripBytes, err := os.ReadFile(*dir + "/1")
|
||||||
@@ -201,38 +193,6 @@ func main() {
|
|||||||
fmt.Printf("Prefer Not multiple: %d, No Prefer cost: %d\n", trip.PreferNotMultiple, trip.NoPreferCost)
|
fmt.Printf("Prefer Not multiple: %d, No Prefer cost: %d\n", trip.PreferNotMultiple, trip.NoPreferCost)
|
||||||
fmt.Printf("Runs per config: %d\n\n", *runs)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if *algo == "fast" || *algo == "both" || *algo == "all" {
|
|
||||||
randomCounts := parseIntList(*numRandom)
|
randomCounts := parseIntList(*numRandom)
|
||||||
perturbCounts := parseIntList(*numPerturb)
|
perturbCounts := parseIntList(*numPerturb)
|
||||||
for _, nr := range randomCounts {
|
for _, nr := range randomCounts {
|
||||||
@@ -257,73 +217,10 @@ func main() {
|
|||||||
results = append(results, runResult{sols[0].Score, assignments, elapsed})
|
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)
|
label := fmt.Sprintf("random=%d perturb=%d pmin=%d pmax=%d", nr, np, *perturbMin, *perturbMax)
|
||||||
printStats(label, results, *runs)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseIntList(s string) []int {
|
func parseIntList(s string) []int {
|
||||||
|
|||||||
676
solver/solver.go
676
solver/solver.go
@@ -1,7 +1,6 @@
|
|||||||
package solver
|
package solver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"slices"
|
"slices"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -28,34 +27,6 @@ var DefaultParams = Params{
|
|||||||
PerturbMax: 8,
|
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 {
|
type Solution struct {
|
||||||
Assignment []int
|
Assignment []int
|
||||||
Score int
|
Score int
|
||||||
@@ -251,14 +222,6 @@ func (s *solverState) score(assignment []int) int {
|
|||||||
return sc
|
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 {
|
func (s *solverState) feasibleForGroup(assignment []int, groupRoot int, room int) bool {
|
||||||
for _, m := range s.groups[groupRoot] {
|
for _, m := range s.groups[groupRoot] {
|
||||||
for _, partner := range s.mustApartFor[m] {
|
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 {
|
func SolveFast(n, roomSize, pnMultiple, npCost int, constraints []Constraint, params Params, rng *rand.Rand) []Solution {
|
||||||
if n == 0 {
|
if n == 0 {
|
||||||
return nil
|
return nil
|
||||||
@@ -926,448 +695,3 @@ func SolveFast(n, roomSize, pnMultiple, npCost int, constraints []Constraint, pa
|
|||||||
return results
|
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
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user