Advent of Code 2020 day14
part1
mask = XXXXXXXXXXXXXXXXXXXXXXXXXXXXX1XXXX0X
mem[8] = 11
mem[7] = 101
mem[8] = 0
のような入力が与えられる。memory address, value ともに 36-bit unsigned integer として動作している。
36-bit それぞれに適用される mask が指定され、それ以降のmemoryへ書き込まれる値がそのmaskによって書き換えられる。
maskは
-
0
はその位置のbitを常に0
に上書きする -
1
はその位置のbitを常に1
に上書きする -
X
は何も変更しない
というルールで各値を書き換えるらしい。(memoryはすべて0
で初期化されている状態から開始し)一連の操作を行った後に書き込まれている値の合計値は幾つか、という問題。
上の例だと maskされた結果 mem[7]
には101
が、mem[8]
には64
が書き込まれることになり、合計値165
が答えになる。
書かれている通りに値を書き換えていくだけだが、毎回36bitすべて見ながら書き換えるのも面倒なので、1回の操作で書き換えられるよう 「0
で上書きするためのmask」と「1
で上書きするためのmask」を別々に用意する。前者は0b111....111
、後者は0b000...000
で初期値を用意し、maskを指定された際に ある位置の値が0
なら前者を、1
なら後者を変更してやる。X
は無視。
実際に書き込む値に適用する際には value & mask.0 | mask.1
を計算することで仕様通りの書き換えが出来る。
memoryはaddressが大きな値になる可能性もあるのでVec
ではなくHashMap
で持っておく。keyもvalueも36-bit unsigned integerがおさまるようにu64
を使っておく。
use std::collections::HashMap;
struct Solution {
inputs: Vec<String>,
}
impl Solution {
fn new(inputs: Vec<String>) -> Self {
Self { inputs }
}
fn solve_1(&self) -> u64 {
let re = Regex::new(r"^mem\[(\d+)\] = (\d+)$").unwrap();
let mut masks: (u64, u64) = ((1 << 36) - 1, 0);
let mut mem: HashMap<u64, u64> = HashMap::new();
for input in self.inputs.iter() {
if let Some(mask) = input.strip_prefix("mask = ") {
masks = ((1 << 36) - 1, 0);
for (i, c) in mask.chars().rev().enumerate() {
match c {
'0' => masks.0 &= !(1 << i),
'1' => masks.1 |= 1 << i,
_ => {}
}
}
} else if let Some(cap) = re.captures(input) {
if let (Ok(address), Ok(value)) = (cap[1].parse::<u64>(), cap[2].parse::<u64>()) {
mem.insert(address, value & masks.0 | masks.1);
}
}
}
mem.values().sum()
}
}
part2
別versionのprogramとして動かす必要があったらしい。今度はvalueではなくaddressの方を書き換える動作をするようだ。maskの動作も変わる。
-
0
は何も変更しない -
1
はその位置のbitを常に1
に上書きする -
X
はその位置のbitをfloating bitとして扱う
floating bitは有り得るすべての値の組み合わせを生じ、それらのaddressすべてに値を書き込むことになる。
mask = 000000000000000000000000000000X1001X
mem[42] = 100
mask = 00000000000000000000000000000000X0XX
mem[26] = 1
という例では、mem[42]
の42
がmaskによって26
, 27
, 58
, 59
になり、その4つの addressすべてに100
が書き込まれる。
mem[26]
は26
がmaskによって 16
, 17
, 18
, 19
, 24
, 25
, 26
, 27
になり、その8つのaddressすべてに1
が書き込まれる。
結果としてすべての値の合計は208
となる。
今度はmaskの0
を無視して、1
のときはpart1と同様の操作に。問題はX
のときだが、後で展開するためにとりあえず出現する位置だけを記録しておく。
let mut masks = (Vec::new(), 0);
for input in self.inputs.iter() {
if let Some(m) = input.strip_prefix("mask = ") {
masks.0.clear();
masks.1 = 0;
for (i, c) in m.chars().rev().enumerate() {
match c {
'1' => masks.1 |= 1 << i,
'X' => masks.0.push(i),
_ => {}
}
}
}
...
}
で、1
で上書きした値から開始し、X
が出現する位置が「0
の場合の値」と「1
の場合の値」をそれぞれ計算し追加していくことで すべての組み合わせを生成していく。VecDeque
を使うとやりやすい。
if let (Ok(address), Ok(value)) = (cap[1].parse::<u64>(), cap[2].parse::<u64>()) {
let mut addresses: VecDeque<u64> = VecDeque::new();
addresses.push_back(address | masks.1);
for &i in masks.0.iter() {
for _ in 0..addresses.len() {
if let Some(front) = addresses.pop_front() {
addresses.push_back(front | 1 << i);
addresses.push_back(front & !(1 << i));
}
}
}
...
}
あとはこうして生成されたすべてのaddressに対してvalueを入れていって合計値を計算するだけ。
code
use regex::Regex;
use std::collections::{HashMap, VecDeque};
use std::io::{BufRead, BufReader};
struct Solution {
inputs: Vec<String>,
re: Regex,
}
impl Solution {
fn new(inputs: Vec<String>) -> Self {
Self {
inputs,
re: Regex::new(r"^mem\[(\d+)\] = (\d+)$").unwrap(),
}
}
fn solve_1(&self) -> u64 {
let mut mask: (u64, u64) = ((1 << 36) - 1, 0);
let mut mem: HashMap<u64, u64> = HashMap::new();
for input in self.inputs.iter() {
if let Some(m) = input.strip_prefix("mask = ") {
mask = ((1 << 36) - 1, 0);
for (i, c) in m.chars().rev().enumerate() {
match c {
'0' => mask.0 &= !(1 << i),
'1' => mask.1 |= 1 << i,
_ => {}
}
}
} else if let Some(cap) = self.re.captures(input) {
if let (Ok(address), Ok(value)) = (cap[1].parse::<u64>(), cap[2].parse::<u64>()) {
mem.insert(address, value & mask.0 | mask.1);
}
}
}
mem.values().sum()
}
fn solve_2(&self) -> u64 {
let mut mem: HashMap<u64, u64> = HashMap::new();
let mut masks = (Vec::new(), 0);
for input in self.inputs.iter() {
if let Some(m) = input.strip_prefix("mask = ") {
masks.0.clear();
masks.1 = 0;
for (i, c) in m.chars().rev().enumerate() {
match c {
'1' => masks.1 |= 1 << i,
'X' => masks.0.push(i),
_ => {}
}
}
} else if let Some(cap) = self.re.captures(input) {
if let (Ok(address), Ok(value)) = (cap[1].parse::<u64>(), cap[2].parse::<u64>()) {
let mut addresses: VecDeque<u64> = VecDeque::new();
addresses.push_back(address | masks.1);
for &i in masks.0.iter() {
for _ in 0..addresses.len() {
if let Some(front) = addresses.pop_front() {
addresses.push_back(front | 1 << i);
addresses.push_back(front & !(1 << i));
}
}
}
for &address in addresses.iter() {
mem.insert(address, value);
}
}
}
}
mem.values().sum()
}
}
fn main() {
let solution = Solution::new(
BufReader::new(std::io::stdin().lock())
.lines()
.filter_map(|line| line.ok())
.collect(),
);
println!("{}", solution.solve_1());
println!("{}", solution.solve_2());
}
Discussion