Finish day 13

This commit is contained in:
Kienan Stewart 2020-12-13 20:57:27 -05:00
parent 3f063f931c
commit 0035850050
3 changed files with 496 additions and 0 deletions

27
day13/build.zig Normal file
View File

@ -0,0 +1,27 @@
const Builder = @import("std").build.Builder;
pub fn build(b: *Builder) void {
// Standard target options allows the person running `zig build` to choose
// what target to build for. Here we do not override the defaults, which
// means any target is allowed, and the default is native. Other options
// for restricting supported target set are available.
const target = b.standardTargetOptions(.{});
// Standard release options allow the person running `zig build` to select
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("day13", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
const run_cmd = exe.run();
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| {
run_cmd.addArgs(args);
}
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
}

2
day13/input Normal file
View File

@ -0,0 +1,2 @@
1005595
41,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,37,x,x,x,x,x,557,x,29,x,x,x,x,x,x,x,x,x,x,13,x,x,x,17,x,x,x,x,x,23,x,x,x,x,x,x,x,419,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,19

467
day13/src/main.zig Normal file
View File

@ -0,0 +1,467 @@
const std = @import("std");
pub fn main() anyerror!void {
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
defer arena.deinit();
var gpa = &arena.allocator;
var f = try std.fs.cwd().openFile("input", .{});
var contents = try f.readToEndAlloc(gpa, std.math.maxInt(u32));
defer gpa.free(contents);
var buses = std.ArrayList(Bus).init(gpa);
defer buses.deinit();
var it = std.mem.tokenize(contents, "\n");
var estimated_arrival_at_bus_station: u64 = 0;
if (it.next()) |l| {
estimated_arrival_at_bus_station = try std.fmt.parseUnsigned(u64, l, 10);
}
if (it.next()) |l| {
var bus_it = std.mem.tokenize(l, ",");
var offset: u64 = 0;
while (bus_it.next()) |b| : (offset += 1) {
if (b[0] == 'x') {
continue;
}
var a = std.fmt.parseUnsigned(u64, b, 10) catch continue;
var bus = Bus{.id = a, .offset = offset};
std.log.debug("Bus: {}", .{bus});
try buses.append(bus);
}
}
var earliest_bus = find_earliest_bus_after(buses.items, estimated_arrival_at_bus_station);
var wait_time = earliest_bus.earliest_departure_after(estimated_arrival_at_bus_station) - estimated_arrival_at_bus_station;
std.log.info("Earliest bus '{}' will depart at {}, a wait of {}.",
.{earliest_bus.id, earliest_bus.earliest_departure_after(estimated_arrival_at_bus_station), wait_time});
std.log.info("Part one: {}", .{earliest_bus.id * wait_time});
// Part 2
// the challenge warns us it will "surely be" above 100000000000000
// 18446744073709551615 <-- u64 max
//var est_start : u64 = buses.items[0].earliest_departure_after(estimated_arrival_at_bus_station);
//est_start = 100000733946084;
est_start = 101775718927832;
// From Est start 101775718927832
// To 140354342739376 took 180.75m of CPU time,
// 598411311431841 <-- sol'n after using sieve + lcm
// It would have probably taken another (180.75)*3.26 = 9.82 hours to get there...
// lcm + sieve: ~ 0.003s, and starts working from 0.
var prize_start : u64 = 0;
var act = std.os.Sigaction {
.sigaction = handle_sigusr,
//.sigaction = std.os.linux.SIG_IGN,
.mask = std.os.empty_sigset,
.flags = 0,
};
std.os.sigaction(10, &act, null);
// Brute force approach
// while (prize_start == 0) : (est_start += buses.items[0].id) {
// var matches = true;
// for (buses.items[1..]) |b, k| {
// if (@mod(est_start + b.offset, b.id) % b.id != 0) {
// matches = false;
// //std.log.debug("Bus {} failed iteration starting at {}", .{b.id, est_start});
// break;
// }
// }
// if (matches) {
// prize_start = est_start;
// }
// }
prize_start = try find_bus_offset_departure(buses.items[0..], gpa);
std.log.info("Earliest depature match prize condition: {}", .{prize_start});
}
var t_last_sigusr : i128 = 0;
var est_start : u64 = 0;
var last_start_value : u64 = 0;
fn handle_sigusr(sig: i32, info: *std.os.siginfo_t, ctx_ptr: ?*const c_void) callconv(.C) void {
var values_per_second_avg : f64 = 0.0;
if (last_start_value == 0) {
std.log.warn("Last {}", .{est_start});
}
else {
var calcs_done : f64 = @intToFloat(f64, (est_start - last_start_value)/41);
values_per_second_avg = calcs_done
/ @intToFloat(f64, std.time.nanoTimestamp() - t_last_sigusr);
std.log.warn("Last {}, {}/s avg - {} calcs done",
.{est_start, values_per_second_avg * std.time.ns_per_s, calcs_done});
}
last_start_value = est_start;
t_last_sigusr = std.time.nanoTimestamp();
}
fn find_earliest_bus_after(buses: []Bus, time: u64) Bus {
var earliest_bus_index : u64 = 0;
var earliest_departure_time : u64 = std.math.maxInt(u64);
for (buses) |bus, k| {
var departs_at : u64 = Bus.earliest_departure(bus.id, time);
if (departs_at < earliest_departure_time) {
earliest_bus_index = k;
earliest_departure_time = departs_at;
}
}
return buses[earliest_bus_index];
}
const Bus = struct {
id: u64,
offset: u64 = 0,
pub fn earliest_departure_after(self: *Bus, time: u64) u64 {
return Bus.earliest_departure(self.id, time);
}
pub fn earliest_departure(id: u64, time: u64) u64 {
var d : u64 = ((time / id)+1) * id;
std.debug.assert(d >= time);
return d;
}
};
fn find_bus_offset_departure(buses: []Bus, a: *std.mem.Allocator) !u64 {
var list_pos : usize = 0;
var t : u64 = 0;
var increment : u64 = buses[0].id;
var bus_ids = try a.alloc(u64, buses.len);
defer a.free(bus_ids);
for (buses) |b, k| {
bus_ids[k] = b.id;
}
while (list_pos < (buses.len-1)) : (t += increment) {
// Check if (t+buses[list_pos+1].offset)%buses[list_pos+1].id == 0
if (@mod(t+buses[list_pos+1].offset, buses[list_pos+1].id) == 0) {
// Our bus arrives here, great.
increment = try lcm_by_factorization(std.testing.allocator, bus_ids[0..list_pos+2]);
std.log.debug("Bus {} (offset: {}) in pos {} at {}. New increment {}",
.{buses[list_pos+1].id, buses[list_pos+1].offset, list_pos+1, t, increment});
list_pos += 1;
}
}
// If we left the loop, we incremented, so decrement
t -= increment;
return t;
}
test "departure" {
var a : u64 = 939;
std.testing.expectEqual(Bus.earliest_departure(59, 939), 944);
}
test "prize_start" {
// 7,13,x,x,59,x,31,19
var buses = [_]Bus {
Bus{.id = 7},
Bus{.id = 13, .offset = 1},
Bus{.id = 59, .offset = 4},
Bus{.id = 31, .offset = 6},
Bus{.id = 19, .offset = 7},
};
est_start = 0;
var prize_start : u64 = 0;
while (prize_start == 0) : (est_start += buses[0].id) {
var matches = true;
for (buses[1..]) |b, k| {
if ((est_start + b.offset) % b.id != 0) {
matches = false;
break;
}
}
if (matches) {
prize_start = est_start;
}
}
std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
std.testing.expectEqual(prize_start, 1068781);
// No LCM version, because the example's prize_start if based on the
// arbitrary offset.
}
test "prize_ex1" {
//17,x,13,19
var buses = [_]Bus {
Bus{.id = 17},
Bus{.id = 13, .offset = 2},
Bus{.id = 19, .offset = 3},
};
est_start = 0;
var prize_start : u64 = 0;
while (prize_start == 0) : (est_start += buses[0].id) {
var matches = true;
for (buses[1..]) |b, k| {
if ((est_start + b.offset) % b.id != 0) {
matches = false;
break;
}
}
if (matches) {
prize_start = est_start;
}
}
std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
std.testing.expectEqual(prize_start, 3417);
// LCM version
prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
std.testing.expectEqual(prize_start, 3417);
}
test "prize_ex2" {
// 67,7,59,61
var buses = [_]Bus {
Bus{.id = 67},
Bus{.id = 7, .offset = 1},
Bus{.id = 59, .offset = 2},
Bus{.id = 61, .offset = 3},
};
est_start = 0;
var prize_start : u64 = 0;
while (prize_start == 0) : (est_start += buses[0].id) {
var matches = true;
for (buses[1..]) |b, k| {
if ((est_start + b.offset) % b.id != 0) {
matches = false;
break;
}
}
if (matches) {
prize_start = est_start;
}
}
std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
std.testing.expectEqual(prize_start, 754018);
// LCM version
prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
std.testing.expectEqual(prize_start, 754018);
}
test "prize_ex3" {
// 67,x,7,59,61
var buses = [_]Bus {
Bus{.id = 67},
Bus{.id = 7, .offset = 2},
Bus{.id = 59, .offset = 3},
Bus{.id = 61, .offset = 4},
};
est_start = 0;
var prize_start : u64 = 0;
while (prize_start == 0) : (est_start += buses[0].id) {
var matches = true;
for (buses[1..]) |b, k| {
if ((est_start + b.offset) % b.id != 0) {
matches = false;
break;
}
}
if (matches) {
prize_start = est_start;
}
}
std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
std.testing.expectEqual(prize_start, 779210);
// LCM version
prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
std.testing.expectEqual(prize_start, 779210);
}
test "prize_ex4" {
// 67,7,x, 59,61
var buses = [_]Bus {
Bus{.id = 67},
Bus{.id = 7, .offset = 1},
Bus{.id = 59, .offset = 3},
Bus{.id = 61, .offset = 4},
};
est_start = 0;
var prize_start : u64 = 0;
while (prize_start == 0) : (est_start += buses[0].id) {
var matches = true;
for (buses[1..]) |b, k| {
if ((est_start + b.offset) % b.id != 0) {
matches = false;
break;
}
}
if (matches) {
prize_start = est_start;
}
}
std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
std.testing.expectEqual(prize_start, 1261476);
// LCM version
prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
std.testing.expectEqual(prize_start, 1261476);
}
test "prize_ex5" {
// 1789,37,47,1889
var buses = [_]Bus {
Bus{.id = 1789},
Bus{.id = 37, .offset = 1},
Bus{.id = 47, .offset = 2},
Bus{.id = 1889, .offset = 3},
};
est_start = 0;
var prize_start : u64 = 0;
while (prize_start == 0) : (est_start += buses[0].id) {
var matches = true;
for (buses[1..]) |b, k| {
if ((est_start + b.offset) % b.id != 0) {
matches = false;
break;
}
}
if (matches) {
prize_start = est_start;
}
}
std.log.warn("Earliest depature match prize condition: {}", .{prize_start});
std.testing.expectEqual(prize_start, 1202161486);
// LCM version
prize_start = try find_bus_offset_departure(buses[0..], std.testing.allocator);
std.testing.expectEqual(prize_start, 1202161486);
}
fn lcm_by_factorization(allocator: *std.mem.Allocator, values: []u64) !u64 {
var highest_powers = std.hash_map.AutoHashMap(u64, u64).init(allocator);
defer highest_powers.deinit();
// For each item in the set, get the prime factorization of the item's value
for (values) |v| {
var list = try prime_factors(v, allocator);
defer list.deinit();
for (list.items) |factor| {
std.log.warn("{}^{} is a prime factor of {}", .{factor.base, factor.power, v});
if (highest_powers.get(factor.base)) |other_power| {
if (factor.power > other_power) {
highest_powers.putAssumeCapacity(factor.base, factor.power);
}
}
else {
try highest_powers.put(factor.base, factor.power);
}
}
}
var lcm : u64 = 1;
var it = highest_powers.iterator();
while (it.next()) |entry| {
std.log.warn("{}^{} is part of the LCM", .{entry.key, entry.value});
lcm *= std.math.pow(u64, entry.key, entry.value);
}
return lcm;
}
const Factor = struct {
base: u64,
power: u64 = 1,
};
fn prime_factors(value: u64, allocator: *std.mem.Allocator) !std.ArrayList(Factor) {
var factors = std.ArrayList(Factor).init(allocator);
// Using the "test and fail method"
var current : u64 = value;
var divisor : u64 = 2;
if (is_prime(value)) {
try factors.append(Factor{.base = value});
return factors;
}
while(current != 1) {
//std.log.warn("{} rem {} = {}", .{current, divisor, @rem(current, divisor)});
if (@rem(current, divisor) == 0) {
current = current / divisor;
// Add divisor to the list of factors if it's not there, otherwise
// increment the power
var index: ?usize = null;
for (factors.items) |v, k| {
if (v.base == divisor) {
index = k;
break;
}
}
if (index) |i| {
factors.items[i].power += 1;
}
else {
try factors.append(Factor{.base = divisor});
}
}
else {
// Out of this factor, increment the divisor until the next prime number
// divisor is reached.
divisor += 1;
while(!is_prime(divisor)) : (divisor+=1) {}
}
}
return factors;
}
fn is_prime(value: u64) bool {
if (value == 1) {
return false;
}
var i: u64 = 2;
while (i < (value-1)) : (i += 1) {
if (@rem(value, i) == 0) {
return false;
}
}
return true;
}
test "is_prime_49" {
std.testing.expect(is_prime(49) == false);
}
test "is_prime_1009" {
std.testing.expect(is_prime(1009));
}
test "prime_factors_100" {
var a = std.testing.allocator;
var factors = try prime_factors(100, a);
defer factors.deinit();
// for (factors.items) |v| {
// std.log.warn("{}^{} ", .{v.base, v.power});
// }
std.testing.expectEqual(factors.items.len, 2);
std.testing.expectEqual(factors.items[0].base, 2);
std.testing.expectEqual(factors.items[0].power, 2);
std.testing.expectEqual(factors.items[1].base, 5);
std.testing.expectEqual(factors.items[0].power, 2);
}
test "prime_factors_1009" {
var a = std.testing.allocator;
var factors = try prime_factors(1009, a);
defer factors.deinit();
// for (factors.items) |v| {
// std.log.warn("{}^{} ", .{v.base, v.power});
// }
std.testing.expectEqual(factors.items.len, 1);
std.testing.expectEqual(factors.items[0].base, 1009);
std.testing.expectEqual(factors.items[0].power, 1);
}
// This is a pretty long test...
test "prime_factors_1289732" {
var a = std.testing.allocator;
var factors = try prime_factors(128973, a);
defer factors.deinit();
// for (factors.items) |v| {
// std.log.warn("{}^{} ", .{v.base, v.power});
// }
std.testing.expectEqual(factors.items.len, 3);
std.testing.expectEqual(factors.items[0].base, 3);
std.testing.expectEqual(factors.items[0].power, 1);
std.testing.expectEqual(factors.items[1].base, 13);
std.testing.expectEqual(factors.items[1].power, 1);
std.testing.expectEqual(factors.items[2].base, 3307);
std.testing.expectEqual(factors.items[2].power, 1);
}