Optimize part2 + benchmark
This commit is contained in:
parent
edce14cd40
commit
5ffebae101
@ -4,8 +4,9 @@ import be.vandewalleh.aoc.utils.input.Day
|
|||||||
import be.vandewalleh.aoc.utils.input.Groups
|
import be.vandewalleh.aoc.utils.input.Groups
|
||||||
import be.vandewalleh.aoc.utils.input.Input
|
import be.vandewalleh.aoc.utils.input.Input
|
||||||
import be.vandewalleh.aoc.utils.input.createDay
|
import be.vandewalleh.aoc.utils.input.createDay
|
||||||
|
import org.eclipse.collections.api.multimap.list.MutableListMultimap
|
||||||
|
import org.eclipse.collections.api.multimap.set.MutableSetMultimap
|
||||||
import org.eclipse.collections.impl.factory.Multimaps
|
import org.eclipse.collections.impl.factory.Multimaps
|
||||||
import org.eclipse.collections.impl.factory.primitive.IntSets
|
|
||||||
|
|
||||||
@Day(16)
|
@Day(16)
|
||||||
class Day16(@Groups val input: Input<List<List<String>>>) {
|
class Day16(@Groups val input: Input<List<List<String>>>) {
|
||||||
@ -42,7 +43,7 @@ class Day16(@Groups val input: Input<List<List<String>>>) {
|
|||||||
return invalid.sum()
|
return invalid.sum()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isTicketValid(ticket: List<Int>, ranges: List<IntRange>): Boolean {
|
private fun isTicketValid(ticket: List<Int>, ranges: Iterable<IntRange>): Boolean {
|
||||||
for (number in ticket) {
|
for (number in ticket) {
|
||||||
if (!ranges.any { number in it }) return false
|
if (!ranges.any { number in it }) return false
|
||||||
}
|
}
|
||||||
@ -52,26 +53,59 @@ class Day16(@Groups val input: Input<List<List<String>>>) {
|
|||||||
private fun inRanges(value: Int, ranges: List<IntRange>) = ranges.any { value in it }
|
private fun inRanges(value: Int, ranges: List<IntRange>) = ranges.any { value in it }
|
||||||
|
|
||||||
fun part2(): Long {
|
fun part2(): Long {
|
||||||
val minMax = Multimaps.mutable.list.empty<String, IntRange>()
|
val rangesByName = namedRanges()
|
||||||
|
val validTickets = validTickets(rangesByName)
|
||||||
|
val indexesByCategory = indexesByCategory(rangesByName, validTickets)
|
||||||
|
|
||||||
for (line in rangesGroup) {
|
removeDuplicates(indexesByCategory)
|
||||||
val name = line.substringBefore(":")
|
|
||||||
extractRanges(line).forEach {
|
val myTicketValues = myTicket.split(",").map { it.toInt() }
|
||||||
minMax.put(name, it)
|
|
||||||
|
var mult = 1L
|
||||||
|
indexesByCategory.forEachKeyValue { category, index ->
|
||||||
|
if (category.startsWith("departure")) {
|
||||||
|
mult *= myTicketValues[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val validTickets = mutableListOf<List<Int>>()
|
return mult
|
||||||
for (line in nearbyTicketsGroup) {
|
}
|
||||||
val ticket = line.splitToSequence(",").map { it.toInt() }.toList()
|
|
||||||
if (isTicketValid(ticket, minMax.valuesView().toList())) {
|
private fun removeDuplicates(indexesByCategory: MutableSetMultimap<String, Int>) {
|
||||||
validTickets.add(ticket)
|
val toBeRemoved = HashSet<Int>()
|
||||||
|
val queue = ArrayDeque<Int>()
|
||||||
|
|
||||||
|
indexesByCategory.multiValuesView()
|
||||||
|
.sortedBy { it.size() }
|
||||||
|
.forEach { it.forEach { if (toBeRemoved.add(it)) queue.add(it) } }
|
||||||
|
|
||||||
|
queue.removeLast()
|
||||||
|
|
||||||
|
val categoriesToRemove = mutableListOf<String>()
|
||||||
|
|
||||||
|
while (queue.isNotEmpty()) {
|
||||||
|
val duplicate = queue.removeFirst()
|
||||||
|
|
||||||
|
categoriesToRemove.clear()
|
||||||
|
|
||||||
|
for (entry in indexesByCategory.keyMultiValuePairsView()) {
|
||||||
|
if (entry.two.size() < 2) continue
|
||||||
|
categoriesToRemove.add(entry.one)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (category in categoriesToRemove) {
|
||||||
|
indexesByCategory.remove(category, duplicate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val indexesByCategory = Multimaps.mutable.list.empty<String, Int>()
|
private fun indexesByCategory(
|
||||||
|
rangesByName: MutableListMultimap<String, IntRange>,
|
||||||
|
validTickets: MutableList<List<Int>>,
|
||||||
|
): MutableSetMultimap<String, Int> {
|
||||||
|
val indexesByCategory = Multimaps.mutable.set.empty<String, Int>()
|
||||||
|
|
||||||
for (entry in minMax.keyMultiValuePairsView()) {
|
for (entry in rangesByName.keyMultiValuePairsView()) {
|
||||||
val category = entry.one
|
val category = entry.one
|
||||||
val ranges = entry.two.toList()
|
val ranges = entry.two.toList()
|
||||||
|
|
||||||
@ -83,38 +117,29 @@ class Day16(@Groups val input: Input<List<List<String>>>) {
|
|||||||
if (allInRange) indexesByCategory.put(category, i)
|
if (allInRange) indexesByCategory.put(category, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return indexesByCategory
|
||||||
|
}
|
||||||
|
|
||||||
val removed = IntSets.mutable.empty()
|
private fun validTickets(rangesByName: MutableListMultimap<String, IntRange>): MutableList<List<Int>> {
|
||||||
while (!indexesByCategory.multiValuesView().all { it.size() == 1 }) {
|
val validTickets = mutableListOf<List<Int>>()
|
||||||
val values = indexesByCategory.multiValuesView()
|
for (line in nearbyTicketsGroup) {
|
||||||
val categoriesToRemove = mutableListOf<String>()
|
val ticket = line.splitToSequence(",").map { it.toInt() }.toList()
|
||||||
|
if (isTicketValid(ticket, rangesByName.valuesView().toList())) {
|
||||||
val one = values.filter { it.size() == 1 }
|
validTickets.add(ticket)
|
||||||
.map { it.first() }
|
|
||||||
.find { removed.add(it) }
|
|
||||||
?: error("No values left to remove")
|
|
||||||
|
|
||||||
for (entry in indexesByCategory.keyMultiValuePairsView()) {
|
|
||||||
val values = entry.two
|
|
||||||
if (values.size() < 2) continue
|
|
||||||
val category = entry.one
|
|
||||||
if (values.find { it == one } != null) categoriesToRemove.add(category)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (category in categoriesToRemove) {
|
|
||||||
indexesByCategory.remove(category, one)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val myTicketValues = myTicket.split(",").map { it.toInt() }
|
return validTickets
|
||||||
var mult = 1L
|
}
|
||||||
|
|
||||||
indexesByCategory.forEachKeyValue { category, index ->
|
private fun namedRanges(): MutableListMultimap<String, IntRange> {
|
||||||
if (category.startsWith("departure")) {
|
val rangesByName = Multimaps.mutable.list.empty<String, IntRange>()
|
||||||
mult *= myTicketValues[index]
|
for (line in rangesGroup) {
|
||||||
|
val name = line.substringBefore(":")
|
||||||
|
extractRanges(line).forEach {
|
||||||
|
rangesByName.put(name, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return rangesByName
|
||||||
return mult
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
39
days/src/test/kotlin/Day16Benchmark.kt
Normal file
39
days/src/test/kotlin/Day16Benchmark.kt
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package be.vandewalleh.aoc.days
|
||||||
|
|
||||||
|
import be.vandewalleh.aoc.utils.input.createDay
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import org.openjdk.jmh.annotations.*
|
||||||
|
import org.openjdk.jmh.infra.Blackhole
|
||||||
|
import org.openjdk.jmh.runner.Runner
|
||||||
|
import org.openjdk.jmh.runner.options.Options
|
||||||
|
import org.openjdk.jmh.runner.options.OptionsBuilder
|
||||||
|
|
||||||
|
/*
|
||||||
|
Benchmark Mode Cnt Score Error Units
|
||||||
|
Day16Benchmark.part2 avgt 5 1.748 ± 0.047 ms/op
|
||||||
|
*/
|
||||||
|
|
||||||
|
@BenchmarkMode(Mode.AverageTime)
|
||||||
|
@OutputTimeUnit(TimeUnit.MILLISECONDS)
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
@Warmup(iterations = 3)
|
||||||
|
@Measurement(iterations = 5)
|
||||||
|
open class Day16Benchmark {
|
||||||
|
|
||||||
|
private val day16 = createDay<Day16>()
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
fun part2(blackhole: Blackhole) {
|
||||||
|
blackhole.consume(day16.part2())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun main() {
|
||||||
|
val opt: Options = OptionsBuilder()
|
||||||
|
.include(Day16Benchmark::class.simpleName)
|
||||||
|
.forks(1)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
Runner(opt).run()
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user