Finish day 13
This commit is contained in:
parent
3f063f931c
commit
0035850050
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
Reference in New Issue