feat: D10P2 solution
This commit is contained in:
@@ -1,11 +1,25 @@
|
||||
package dayten
|
||||
|
||||
import (
|
||||
"advent-of-code/internal/registry"
|
||||
"math"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"advent-of-code/internal/registry"
|
||||
)
|
||||
|
||||
const (
|
||||
positiveInfinity = math.MaxFloat64
|
||||
negativeInfinity = -math.MaxFloat64
|
||||
epsilon = 1e-9
|
||||
)
|
||||
|
||||
var (
|
||||
bracketRegex = regexp.MustCompile(`\[([.#]+)\]`)
|
||||
parenthesisRegex = regexp.MustCompile(`\(([^)]+)\)`)
|
||||
braceRegex = regexp.MustCompile(`\{([^}]+)\}`)
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -18,9 +32,6 @@ func ParseInput(filepath string) []string {
|
||||
}
|
||||
|
||||
func parseMachine(line string) ([]bool, [][]int) {
|
||||
bracketRegex := regexp.MustCompile(`\[([.#]+)\]`)
|
||||
parenthesisRegex := regexp.MustCompile(`\(([^)]+)\)`)
|
||||
|
||||
bracketMatch := bracketRegex.FindStringSubmatch(line)
|
||||
targetString := bracketMatch[1]
|
||||
target := make([]bool, len(targetString))
|
||||
@@ -45,6 +56,246 @@ func parseMachine(line string) ([]bool, [][]int) {
|
||||
return target, buttons
|
||||
}
|
||||
|
||||
func solveIntegerLinearMinimization(constraints [][]float64, objective []float64) int {
|
||||
best := positiveInfinity
|
||||
stack := [][][]float64{constraints}
|
||||
|
||||
for len(stack) > 0 {
|
||||
current := stack[len(stack)-1]
|
||||
stack = stack[:len(stack)-1]
|
||||
|
||||
numberOfRows := len(current)
|
||||
numberOfVariables := len(current[0]) - 1
|
||||
rightHandSideIdx := numberOfVariables + 1
|
||||
|
||||
nonBasic := make([]int, numberOfVariables+1)
|
||||
for idx := range numberOfVariables {
|
||||
nonBasic[idx] = idx
|
||||
}
|
||||
nonBasic[numberOfVariables] = -1
|
||||
|
||||
basic := make([]int, numberOfRows)
|
||||
for idx := range numberOfRows {
|
||||
basic[idx] = numberOfVariables + idx
|
||||
}
|
||||
|
||||
tableau := make([][]float64, numberOfRows+2)
|
||||
for idx := range numberOfRows {
|
||||
tableau[idx] = make([]float64, numberOfVariables+2)
|
||||
copy(tableau[idx][:numberOfVariables+1], current[idx])
|
||||
tableau[idx][rightHandSideIdx] = -1.0
|
||||
tableau[idx][numberOfVariables], tableau[idx][rightHandSideIdx] = tableau[idx][rightHandSideIdx], tableau[idx][numberOfVariables]
|
||||
}
|
||||
tableau[numberOfRows] = make([]float64, numberOfVariables+2)
|
||||
copy(tableau[numberOfRows][:numberOfVariables], objective)
|
||||
tableau[numberOfRows+1] = make([]float64, numberOfVariables+2)
|
||||
tableau[numberOfRows+1][numberOfVariables] = 1.0
|
||||
|
||||
pivot := func(row, column int) {
|
||||
k := 1.0 / tableau[row][column]
|
||||
for i := 0; i < numberOfRows+2; i++ {
|
||||
if i == row {
|
||||
continue
|
||||
}
|
||||
for j := 0; j < numberOfVariables+2; j++ {
|
||||
if j != column {
|
||||
tableau[i][j] -= tableau[row][j] * tableau[i][column] * k
|
||||
}
|
||||
}
|
||||
}
|
||||
for j := 0; j < numberOfVariables+2; j++ {
|
||||
tableau[row][j] *= k
|
||||
}
|
||||
for i := 0; i < numberOfRows+2; i++ {
|
||||
tableau[i][column] *= -k
|
||||
}
|
||||
tableau[row][column] = k
|
||||
basic[row], nonBasic[column] = nonBasic[column], basic[row]
|
||||
}
|
||||
|
||||
findOptimalPivot := func(phase int) bool {
|
||||
for {
|
||||
pivotColumn := -1
|
||||
minimumValue := positiveInfinity
|
||||
minimumIndex := math.MaxInt
|
||||
|
||||
for idx := 0; idx <= numberOfVariables; idx++ {
|
||||
if phase == 0 && nonBasic[idx] == -1 {
|
||||
continue
|
||||
}
|
||||
value := tableau[numberOfRows+phase][idx]
|
||||
if pivotColumn == -1 || value < minimumValue-epsilon || (math.Abs(value-minimumValue) <= epsilon && nonBasic[idx] < minimumIndex) {
|
||||
pivotColumn = idx
|
||||
minimumValue = value
|
||||
minimumIndex = nonBasic[idx]
|
||||
}
|
||||
}
|
||||
|
||||
if tableau[numberOfRows+phase][pivotColumn] > -epsilon {
|
||||
return true
|
||||
}
|
||||
|
||||
pivotRow := -1
|
||||
minimumRatio := positiveInfinity
|
||||
minimumBasicIndex := math.MaxInt
|
||||
|
||||
for idx := range numberOfRows {
|
||||
if tableau[idx][pivotColumn] > epsilon {
|
||||
ratio := tableau[idx][rightHandSideIdx] / tableau[idx][pivotColumn]
|
||||
if pivotRow == -1 || ratio < minimumRatio-epsilon || (math.Abs(ratio-minimumRatio) <= epsilon && basic[idx] < minimumBasicIndex) {
|
||||
pivotRow = idx
|
||||
minimumRatio = ratio
|
||||
minimumBasicIndex = basic[idx]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if pivotRow == -1 {
|
||||
return false
|
||||
}
|
||||
|
||||
pivot(pivotRow, pivotColumn)
|
||||
}
|
||||
}
|
||||
|
||||
extractSolution := func() (float64, []float64) {
|
||||
solution := make([]float64, numberOfVariables)
|
||||
for idx := range numberOfRows {
|
||||
if basic[idx] >= 0 && basic[idx] < numberOfVariables {
|
||||
solution[basic[idx]] = tableau[idx][rightHandSideIdx]
|
||||
}
|
||||
}
|
||||
result := 0.0
|
||||
for idx := range numberOfVariables {
|
||||
result += objective[idx] * solution[idx]
|
||||
}
|
||||
return result, solution
|
||||
}
|
||||
|
||||
var value float64
|
||||
var solution []float64
|
||||
|
||||
mostNegativeRow := 0
|
||||
mostNegativeValue := tableau[0][rightHandSideIdx]
|
||||
for idx := 1; idx < numberOfRows; idx++ {
|
||||
if tableau[idx][rightHandSideIdx] < mostNegativeValue {
|
||||
mostNegativeValue = tableau[idx][rightHandSideIdx]
|
||||
mostNegativeRow = idx
|
||||
}
|
||||
}
|
||||
|
||||
if mostNegativeValue < -epsilon {
|
||||
pivot(mostNegativeRow, numberOfVariables)
|
||||
if !findOptimalPivot(1) || tableau[numberOfRows+1][rightHandSideIdx] < -epsilon {
|
||||
value = negativeInfinity
|
||||
solution = nil
|
||||
} else {
|
||||
for i := range numberOfRows {
|
||||
if basic[i] == -1 {
|
||||
column := 0
|
||||
columnValue := tableau[i][0]
|
||||
columnIndex := nonBasic[0]
|
||||
for j := 1; j < numberOfVariables; j++ {
|
||||
if tableau[i][j] < columnValue-epsilon || (math.Abs(tableau[i][j]-columnValue) <= epsilon && nonBasic[j] < columnIndex) {
|
||||
column = j
|
||||
columnValue = tableau[i][j]
|
||||
columnIndex = nonBasic[j]
|
||||
}
|
||||
}
|
||||
pivot(i, column)
|
||||
}
|
||||
}
|
||||
if findOptimalPivot(0) {
|
||||
value, solution = extractSolution()
|
||||
} else {
|
||||
value = negativeInfinity
|
||||
solution = nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if findOptimalPivot(0) {
|
||||
value, solution = extractSolution()
|
||||
} else {
|
||||
value = negativeInfinity
|
||||
solution = nil
|
||||
}
|
||||
}
|
||||
|
||||
if value == negativeInfinity || value >= best-epsilon {
|
||||
continue
|
||||
}
|
||||
|
||||
fractionalIdx := -1
|
||||
var fractionalValue float64
|
||||
for idx, value := range solution {
|
||||
if math.Abs(value-math.Round(value)) > epsilon {
|
||||
fractionalIdx = idx
|
||||
fractionalValue = value
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if fractionalIdx == -1 {
|
||||
best = value
|
||||
continue
|
||||
}
|
||||
|
||||
numberOfColumns := len(current[0])
|
||||
fractionalPart := fractionalValue - math.Floor(fractionalValue)
|
||||
|
||||
ceilConstraint := make([]float64, numberOfColumns)
|
||||
ceilConstraint[fractionalIdx] = -1.0
|
||||
ceilConstraint[numberOfColumns-1] = -math.Ceil(fractionalValue)
|
||||
ceilBranch := make([][]float64, len(current)+1)
|
||||
copy(ceilBranch, current)
|
||||
ceilBranch[len(current)] = ceilConstraint
|
||||
|
||||
floorConstraint := make([]float64, numberOfColumns)
|
||||
floorConstraint[fractionalIdx] = 1.0
|
||||
floorConstraint[numberOfColumns-1] = math.Floor(fractionalValue)
|
||||
floorBranch := make([][]float64, len(current)+1)
|
||||
copy(floorBranch, current)
|
||||
floorBranch[len(current)] = floorConstraint
|
||||
|
||||
if fractionalPart > 0.5 {
|
||||
stack = append(stack, ceilBranch, floorBranch)
|
||||
} else {
|
||||
stack = append(stack, floorBranch, ceilBranch)
|
||||
}
|
||||
}
|
||||
|
||||
return int(math.Round(best))
|
||||
}
|
||||
|
||||
func findMinimumJoltagePresses(buttons [][]int, joltages []int) int {
|
||||
numberOfCounters, numberOfButtons := len(joltages), len(buttons)
|
||||
numberOfRows, numberOfColumns := 2*numberOfCounters+numberOfButtons, numberOfButtons+1
|
||||
constraints := make([][]float64, numberOfRows)
|
||||
for i := range constraints {
|
||||
constraints[i] = make([]float64, numberOfColumns)
|
||||
}
|
||||
for j := range numberOfButtons {
|
||||
constraints[numberOfRows-1-j][j] = -1.0
|
||||
}
|
||||
for buttonIdx, button := range buttons {
|
||||
for _, counterIdx := range button {
|
||||
constraints[counterIdx][buttonIdx] = 1.0
|
||||
constraints[counterIdx+numberOfCounters][buttonIdx] = -1.0
|
||||
}
|
||||
}
|
||||
for idx, value := range joltages {
|
||||
value := float64(value)
|
||||
constraints[idx][numberOfColumns-1] = value
|
||||
constraints[idx+numberOfCounters][numberOfColumns-1] = -value
|
||||
}
|
||||
objective := make([]float64, numberOfButtons)
|
||||
for idx := range objective {
|
||||
objective[idx] = 1.0
|
||||
}
|
||||
|
||||
return solveIntegerLinearMinimization(constraints, objective)
|
||||
}
|
||||
|
||||
func PartOne(data []string) int {
|
||||
total := 0
|
||||
for _, line := range data {
|
||||
@@ -87,5 +338,16 @@ func PartOne(data []string) int {
|
||||
}
|
||||
|
||||
func PartTwo(data []string) int {
|
||||
return 0
|
||||
total := 0
|
||||
for _, line := range data {
|
||||
_, buttons := parseMachine(line)
|
||||
braceMatch := braceRegex.FindStringSubmatch(line)
|
||||
parts := strings.Split(braceMatch[1], ",")
|
||||
joltages := make([]int, len(parts))
|
||||
for idx, part := range parts {
|
||||
joltages[idx], _ = strconv.Atoi(part)
|
||||
}
|
||||
total += findMinimumJoltagePresses(buttons, joltages)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user