Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
A
Advent of Coding
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Christopher Bohn
Advent of Coding
Commits
2b3ac36e
Commit
2b3ac36e
authored
2 years ago
by
Christopher Bohn
Browse files
Options
Downloads
Patches
Plain Diff
Completed Year 2022 Day 15
parent
36263956
No related branches found
No related tags found
No related merge requests found
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
2022/README.md
+18
-7
18 additions, 7 deletions
2022/README.md
2022/src/main/java/edu/unl/cse/bohn/year2022/Day15.java
+99
-8
99 additions, 8 deletions
2022/src/main/java/edu/unl/cse/bohn/year2022/Day15.java
with
117 additions
and
15 deletions
2022/README.md
+
18
−
7
View file @
2b3ac36e
...
...
@@ -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
...
...
This diff is collapsed.
Click to expand it.
2022/src/main/java/edu/unl/cse/bohn/year2022/Day15.java
+
99
−
8
View file @
2b3ac36e
...
...
@@ -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
;
}
}
}
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment