TDL/plateSearch.go

110 lines
2.4 KiB
Go

package main
import (
"fmt"
"sort"
)
const (
INFINITY = 26
)
var debugSearch bool
type DummyWriter struct {
}
func (d DummyWriter) Write(p []byte) (n int, err error) {
n = len(p)
return
}
func DebugSearch(input []rune) {
debugSearch = true
parsed := NewParser(input).Parse()
generator := NewStdGenerator(DummyWriter{})
generator.Generate(parsed)
}
func SearchPlates(plates map[float64]int, goalWeight float64) []float64 {
if debugSearch {
fmt.Println("searching", goalWeight)
}
root := &DFSNode{}
root.remainingWeight = goalWeight
root.residualPlates = make(map[float64]int)
root.plate = -1
for k, v := range plates {
root.residualPlates[k] = v
}
optimum := root
root.search(&optimum)
usedPlates := optimum.usedPlates()
if debugSearch {
fmt.Println("found", usedPlates)
}
return usedPlates
}
type DFSNode struct {
parent *DFSNode
depth int
remainingWeight float64
plate float64
residualPlates map[float64]int
}
func (parent *DFSNode) newChild(plate float64) *DFSNode {
node := &DFSNode{}
node.parent = parent
node.depth = parent.depth + 1
node.remainingWeight = parent.remainingWeight - plate*2
node.plate = plate
node.residualPlates = make(map[float64]int)
for k, v := range parent.residualPlates {
node.residualPlates[k] = v
}
node.residualPlates[plate] -= 2
return node
}
func (n *DFSNode) usedPlates() (plates []float64) {
for node := n; node != nil; node = node.parent {
if node.plate > 0 {
plates = append([]float64{node.plate}, plates...)
}
}
return
}
func (n *DFSNode) successors() (children []*DFSNode) {
keys := []float64{}
for weight, amount := range n.residualPlates {
if amount >= 2 && weight*2 <= n.remainingWeight && (n.plate == -1 || weight <= n.plate) && n.remainingWeight/weight <= INFINITY {
keys = append(keys, weight)
}
}
sort.Float64s(keys)
for i := len(keys) - 1; i >= 0; i-- {
children = append(children, n.newChild(keys[i]))
}
return
}
func (n *DFSNode) search(optimum **DFSNode) {
successors := n.successors()
if len(successors) == 0 {
// leaf
if n.remainingWeight < (*optimum).remainingWeight || (n.remainingWeight == (*optimum).remainingWeight && n.depth < (*optimum).depth) {
*optimum = n
if debugSearch {
fmt.Printf("optimum: residual plates %v, remaining weight %v\n", (*optimum).residualPlates, (*optimum).remainingWeight)
}
}
} else {
for _, succ := range successors {
succ.search(optimum)
}
}
}