1
0

Optimize part2 + benchmark

This commit is contained in:
Hubert Van De Walle 2020-12-16 11:09:55 +01:00
parent edce14cd40
commit 5ffebae101
2 changed files with 104 additions and 40 deletions

View File

@ -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
} }
} }

View 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()
}