Skip to content
Snippets Groups Projects
Commit 2b3ac36e authored by Christopher Bohn's avatar Christopher Bohn :thinking:
Browse files

Completed Year 2022 Day 15

parent 36263956
No related branches found
No related tags found
No related merge requests found
......@@ -580,18 +580,29 @@ The subproblems are
- Determine which positions cannot contain a beacon (*i.e.*, which positions are no closer to the sensor
than the beacon)
Creating many, many beacons would seem to be untenable from a memory perspective.
I think we're going to have to dynamically build the map and then forget which sensor detected which beacon.
This is going to be a PITA Linked List of Linked Lists.
*Or*, I could make two passes, one to determine the matrix's dimensions, and one to populate it.
Okay, the problem (unsurprisingly) wasn't all the Sensor data; it was the 6,862,736 x 6,004,906 matrix.
Creating a 6,862,736 x 6,004,906 matrix is a bit of a memory hog.
What? You don't have several terabytes just lying around?
Clearly we need to go *back* to tracking the Sensor data and dynamically determine whether a given position is covered.
### Part 2
...
Hah! My CSCE 231 students who remember how to compute an address in a nested array will recognize what's happening here.
Our task is to find the one location that is not covered by a sensor and then compute its linear position in a nested
array -- er, um -- to compute its *tuning frequency*.
Is there a smarter (faster) way than iterating over 16 trillion positions? Probably. What do we know?
For any given sensor at (x,y) whose nearest sensor is distance d away, the sensor covers the square
(x-d/2,y-d/2) through (x+d/2,y+d/2) plus several other locations.
While 16 trillion locations is a bit much, we could handle 4 million rows, where each row is a set of covered ranges.
When we look for that *one* uncovered spot, we only have to examine the rows -- or rather, *row*, that isn't fully
covered by a range.
### Refactoring opportunity
The ranges could also be used to solve part 1 (no surprise there).
I don't think I'll do that, though, since I can optimize the part 2 solution for non-negative indices.
## Day 16
......
......@@ -2,13 +2,15 @@ package edu.unl.cse.bohn.year2022;
import edu.unl.cse.bohn.Puzzle;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.stream.IntStream;
@SuppressWarnings({"unused"})
public class Day15 extends Puzzle {
public static final int FULL_SIZE_PROBLEM = 4000000;
public static final int SAMPLE_SIZE_PROBLEM = 20;
public Day15(boolean isProductionReady) {
super(isProductionReady);
sampleData = """
......@@ -33,13 +35,63 @@ public class Day15 extends Puzzle {
for (String datum : data) {
new Sensor(datum);
}
//noinspection MagicNumber
return Sensor.countLocationsThatCannotHaveBeacon(isProductionReady ? 2000000 : 10);
return Sensor.countLocationsThatCannotHaveBeacon(isProductionReady
? FULL_SIZE_PROBLEM / 2
: SAMPLE_SIZE_PROBLEM / 2);
}
@SuppressWarnings("OverlyLongMethod")
@Override
public long computePart2(List<String> data) {
return 0;
// I'm not going to foolproof this. I'll just assume (correctly) that part 1 is executed before part 2
long upperLimit = isProductionReady ? FULL_SIZE_PROBLEM : SAMPLE_SIZE_PROBLEM;
List<Set<Range>> rows = new ArrayList<>(FULL_SIZE_PROBLEM + 1);
for (int i = 0; i <= FULL_SIZE_PROBLEM; i++) {
rows.add(new HashSet<>());
}
for (Sensor sensor : Sensor.sensors) { // breaking encapsulation a little
for (int y = sensor.getLeastY(); y <= sensor.getGreatestY(); y++) {
if (y >= 0 && y <= FULL_SIZE_PROBLEM) {
Set<Range> row = rows.get(y);
Range newRange = new Range(Math.max(0, sensor.getMinimumX(y)),
Math.min(FULL_SIZE_PROBLEM, sensor.getMaximumX(y)));
if (row.stream().noneMatch(newRange::isFullyEnclosedBy)) {
row.stream().filter(range -> range.isFullyEnclosedBy(newRange))
.findAny().ifPresent(row::remove);
row.add(newRange);
}
}
}
}
// Each row is now a set of overlapping ranges (with none fully enclosed by another)
// Let's merge!
Integer incompleteRowNumber = null;
Range nonOverlappingRange = null;
for (int rowNumber = 0; rowNumber <= upperLimit; rowNumber++) {
Set<Range> row = rows.get(rowNumber);
while (row.size() > 1 && incompleteRowNumber == null) {
Range range = row.stream().findAny().orElseThrow();
row.remove(range);
Range overlappingRange = row.stream().filter(range::overlaps).findAny().orElse(null);
if (overlappingRange == null) {
nonOverlappingRange = range;
incompleteRowNumber = rowNumber;
} else {
row.remove(overlappingRange);
row.add(Range.merge(range, overlappingRange));
}
}
}
assert incompleteRowNumber != null;
Range range = rows.get(incompleteRowNumber).stream().findAny().orElseThrow();
long y = (long)incompleteRowNumber;
long x;
if (range.maximumX < nonOverlappingRange.minimumX) {
x = range.maximumX + 1;
} else {
x = nonOverlappingRange.maximumX + 1;
}
return FULL_SIZE_PROBLEM * x + y;
}
private static class Sensor {
......@@ -48,7 +100,6 @@ public class Day15 extends Puzzle {
private static final Set<Sensor> sensors = new HashSet<>();
// private static boolean[][] mightContainAnUnknownBeacon = null;
private static int minimumX = Integer.MAX_VALUE;
private static int maximumX = Integer.MIN_VALUE;
private static int minimumY = Integer.MAX_VALUE;
......@@ -84,6 +135,28 @@ public class Day15 extends Puzzle {
return getDistanceToLocation(x, y) <= getDistanceToBeacon();
}
public int getMinimumX(int y) {
int beaconDistance = getDistanceToBeacon();
int rowDistance = Math.abs(this.y - y);
int leftoverDistance = beaconDistance - rowDistance;
return this.x - leftoverDistance;
}
public int getMaximumX(int y) {
int beaconDistance = getDistanceToBeacon();
int rowDistance = Math.abs(this.y - y);
int leftoverDistance = beaconDistance - rowDistance;
return this.x + leftoverDistance;
}
public int getLeastY() {
return this.y - getDistanceToBeacon();
}
public int getGreatestY() {
return this.y + getDistanceToBeacon();
}
@Override
public String toString() {
return "Sensor at x=" + x + ", y=" + y + ": closest beacon is at x=" + nearestBeaconX
......@@ -123,4 +196,22 @@ public class Day15 extends Puzzle {
return locationsInRangeOfSensor - locationsWithKnownBeacon;
}
}
private record Range(int minimumX, int maximumX) {
public boolean overlaps(Range other) {
return ((other.minimumX <= this.maximumX) && (this.maximumX <= other.maximumX))
|| ((this.minimumX <= other.maximumX) && (other.maximumX <= this.maximumX));
}
public boolean isFullyEnclosedBy(Range other) {
return (this.minimumX >= other.minimumX) && (this.maximumX <= other.maximumX);
}
public static Range merge(Range range1, Range range2) {
return range1.overlaps(range2)
? new Range(Integer.min(range1.minimumX, range2.minimumX),
Integer.max(range1.maximumX, range2.maximumX))
: null;
}
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment