169 lines
3.6 KiB
Go
169 lines
3.6 KiB
Go
package main
|
|
|
|
import (
|
|
"math"
|
|
)
|
|
|
|
// Event type represents different events
|
|
type Event string
|
|
|
|
type Solver string
|
|
|
|
type SolverTimes map[Solver]map[Event]float64
|
|
|
|
type Assignment map[Solver][]Event
|
|
|
|
type PermWithTime struct {
|
|
Perm []Solver
|
|
Time float64
|
|
}
|
|
|
|
type AssignmentWithTime struct {
|
|
Assignment Assignment
|
|
Time float64
|
|
}
|
|
|
|
type Config struct {
|
|
SolverTimes SolverTimes `yaml:"solvers"`
|
|
Events []Event `yaml:"events"`
|
|
Participants []Solver `yaml:"participants"`
|
|
}
|
|
|
|
//
|
|
// Generic Functions
|
|
//
|
|
|
|
func Combinations[T comparable](lst []T, n int) func() ([]T, bool) {
|
|
length := len(lst)
|
|
current := make([]T, n)
|
|
index := make([]int, n)
|
|
|
|
// Initialize the first combination: ["lst[0]", "lst[0]", ..., "lst[0]"]
|
|
for i := 0; i < n; i++ {
|
|
current[i] = lst[0]
|
|
index[i] = 0
|
|
}
|
|
|
|
return func() ([]T, bool) {
|
|
if current == nil {
|
|
return nil, false
|
|
}
|
|
|
|
// Copy current combination to return
|
|
result := make([]T, n)
|
|
copy(result, current)
|
|
|
|
// Find the next combination
|
|
j := n - 1
|
|
for j >= 0 && index[j] == length-1 {
|
|
j--
|
|
}
|
|
|
|
if j < 0 {
|
|
current = nil
|
|
} else {
|
|
index[j]++
|
|
current[j] = lst[index[j]]
|
|
|
|
for k := j + 1; k < n; k++ {
|
|
index[k] = 0
|
|
current[k] = lst[0]
|
|
}
|
|
}
|
|
|
|
return result, current != nil
|
|
}
|
|
}
|
|
|
|
func CombinationsWithStart[T comparable](start []T, lst []T, n int) func() ([]T, bool) {
|
|
hasNext := true
|
|
iter := Combinations(lst, n-len(start))
|
|
var end []T
|
|
return func() ([]T, bool) {
|
|
end, hasNext = iter()
|
|
return append(start, end...), hasNext
|
|
}
|
|
}
|
|
|
|
func countCombinations[T comparable](lst []T, n int) int {
|
|
return int(math.Pow(float64(len(lst)), float64(n)))
|
|
}
|
|
|
|
//
|
|
// Relay Functions
|
|
//
|
|
|
|
func CalculateRelayTime(perm []Solver, events []Event, solverTimes SolverTimes) float64 {
|
|
solversTotalTimes := make(map[Solver]float64, len(events))
|
|
for i, solver := range perm {
|
|
solversTotalTimes[solver] += solverTimes[solver][events[i]]
|
|
}
|
|
|
|
var relayTime float64
|
|
for _, time := range solversTotalTimes {
|
|
if time > relayTime {
|
|
relayTime = time
|
|
}
|
|
}
|
|
return relayTime
|
|
}
|
|
|
|
//
|
|
// Worker Functions
|
|
//
|
|
|
|
func GenerateSolverCombinationsWorker(solverCombs chan<- func() ([]Solver, bool), solvers []Solver, events []Event) {
|
|
hasNext := true
|
|
iter := Combinations(solvers, len(events))
|
|
for hasNext {
|
|
var start []Solver
|
|
start, hasNext = iter()
|
|
solverCombs <- CombinationsWithStart(start, solvers, len(events))
|
|
}
|
|
}
|
|
|
|
func CalculateRelayTimesWorker(solverCombs <-chan func() ([]Solver, bool), assignments chan<- PermWithTime, events []Event, solverTimes SolverTimes) {
|
|
for solverComb := range solverCombs {
|
|
hasNext := true
|
|
for hasNext {
|
|
var comb []Solver
|
|
comb, hasNext = solverComb()
|
|
relayTime := CalculateRelayTime(comb, events, solverTimes)
|
|
assignments <- PermWithTime{comb, relayTime}
|
|
}
|
|
}
|
|
}
|
|
|
|
func Assign(solvers []Solver, solverTimes SolverTimes, events []Event) AssignmentWithTime {
|
|
solverCombs := make(chan func() ([]Solver, bool))
|
|
permsWithTime := make(chan PermWithTime)
|
|
|
|
go GenerateSolverCombinationsWorker(solverCombs, solvers, events)
|
|
|
|
bestAssignment := AssignmentWithTime{
|
|
Assignment: Assignment{},
|
|
Time: math.Inf(1),
|
|
}
|
|
// var bestAssignmentMu sync.Mutex
|
|
|
|
for i := 0; i < 64; i++ {
|
|
go CalculateRelayTimesWorker(solverCombs, permsWithTime, events, solverTimes)
|
|
}
|
|
|
|
totalPerms := countCombinations(solvers, len(events))
|
|
|
|
for i := 0; i < totalPerms; i++ {
|
|
// fmt.Println("checking assignment")
|
|
p := <-permsWithTime
|
|
if p.Time < bestAssignment.Time {
|
|
assignment := make(Assignment, len(p.Perm))
|
|
for i, solver := range p.Perm {
|
|
assignment[solver] = append(assignment[solver], events[i])
|
|
}
|
|
bestAssignment = AssignmentWithTime{assignment, p.Time}
|
|
}
|
|
}
|
|
|
|
return bestAssignment
|
|
}
|