0

决定尝试有关 Exercism 的字母数学问题,并使用蛮力但同时的方法,我以相当有效的方式解决了除了最终测试之外的所有问题。我无法弄清楚为什么最终测试失败了,因为唯一额外的复杂性是加数的数量。我认为这是我如何处理列的进位数字的问题,但这也应该让我在其他测试中绊倒。我的代码:

use combination::combine::*;
use combination::permutate::*;
use rayon::prelude::*;
use std::collections::HashMap;
use std::collections::HashSet;

const DIGITS: [u8; 10] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

pub fn solve(input: &str) -> Option<HashMap<char, u8>> {
    let alphametic = Alphametic::from_str(input);
    alphametic.brute_force()
}

#[derive(Debug, Clone)]
struct Alphametic {
    firsts: HashSet<char>,
    columns: Vec<Vec<char>>,
    keys: HashSet<char>,
}

impl Alphametic {
    fn from_str(input: &str) -> Self {
        let mut firsts = HashSet::new();
        let terms = input
            .split_whitespace()
            .rev()
            .filter(|&sub| sub != "+" && sub != "==")
            .map(|sub| {
                firsts.insert(sub.chars().next().unwrap());
                sub
            })
            .map(|sub| sub.chars().rev().collect::<Vec<char>>())
            .collect::<Vec<Vec<char>>>();
        let max_cols = terms[0].len();
        let mut columns = vec![];
        for i in 0..max_cols {
            columns.push(
                terms
                    .iter()
                    .filter(|term| !(term.len() <= i))
                    .map(|term| term[i])
                    .collect::<Vec<char>>(),
            )
        }
        let keys = terms
            .iter()
            .flatten()
            .map(|&c| c)
            .collect::<HashSet<char>>();
        Alphametic {
            firsts,
            columns,
            keys,
        }
    }

    fn brute_force(&self) -> Option<HashMap<char, u8>> {
        let combo_len = self.keys.len();
        let combos = combine_vec(&DIGITS.to_vec(), combo_len);

        if let Some(soln) = combos
            .par_iter()
            .map(|combo| {
                let perms = permutate_vec(&combo);
                perms
                    .par_iter()
                    .map(|perm| self.to_guess(&perm))
                    .filter(|guess| self.no_leading_zeros(guess))
                    .find_first(|guess| self.guess_columns(guess.clone(), 0, 0).is_some())
            })
            .find_first(|x| x.is_some())
        {
            soln
        } else {
            None
        }
    }

    fn no_leading_zeros(&self, guess: &HashMap<char, u8>) -> bool {
        self.firsts.iter().all(|c| guess.get(c).unwrap_or(&0) > &0)
    }

    fn guess_columns(
        &self,
        guess: HashMap<char, u8>,
        column: usize,
        carry: u8,
    ) -> Option<HashMap<char, u8>> {
        if column >= self.columns.len() {
            Some(guess)
        } else {
            let col = &self.columns[column];
            let sum = *guess.get(&col[0]).unwrap();
            let addition = col[1..].iter().map(|c| guess.get(c).unwrap()).sum::<u8>() + carry;
            if sum == addition % 10 {
                self.guess_columns(guess, column + 1, addition / 10)
            } else {
                None
            }
        }
    }

    fn to_guess(&self, perm: &&Vec<u8>) -> HashMap<char, u8> {
        self.keys
            .iter()
            .zip(*perm)
            .map(|(k, v)| (*k, *v))
            .collect::<HashMap<char, u8>>()
    }
}

测试输入:

fn test_puzzle_with_ten_letters_and_199_addends() {
    assert_alphametic_solution_eq(
        "THIS + A + FIRE + THEREFORE + FOR + ALL + HISTORIES + I + TELL + A + TALE + THAT + FALSIFIES + ITS + TITLE + TIS + A + LIE + THE + TALE + OF + THE + LAST + FIRE + HORSES + LATE + AFTER + THE + FIRST + FATHERS + FORESEE + THE + HORRORS + THE + LAST + FREE + TROLL + TERRIFIES + THE + HORSES + OF + FIRE + THE + TROLL + RESTS + AT + THE + HOLE + OF + LOSSES + IT + IS + THERE + THAT + SHE + STORES + ROLES + OF + LEATHERS + AFTER + SHE + SATISFIES + HER + HATE + OFF + THOSE + FEARS + A + TASTE + RISES + AS + SHE + HEARS + THE + LEAST + FAR + HORSE + THOSE + FAST + HORSES + THAT + FIRST + HEAR + THE + TROLL + FLEE + OFF + TO + THE + FOREST + THE + HORSES + THAT + ALERTS + RAISE + THE + STARES + OF + THE + OTHERS + AS + THE + TROLL + ASSAILS + AT + THE + TOTAL + SHIFT + HER + TEETH + TEAR + HOOF + OFF + TORSO + AS + THE + LAST + HORSE + FORFEITS + ITS + LIFE + THE + FIRST + FATHERS + HEAR + OF + THE + HORRORS + THEIR + FEARS + THAT + THE + FIRES + FOR + THEIR + FEASTS + ARREST + AS + THE + FIRST + FATHERS + RESETTLE + THE + LAST + OF + THE + FIRE + HORSES + THE + LAST + TROLL + HARASSES + THE + FOREST + HEART + FREE + AT + LAST + OF + THE + LAST + TROLL + ALL + OFFER + THEIR + FIRE + HEAT + TO + THE + ASSISTERS + FAR + OFF + THE + TROLL + FASTS + ITS + LIFE + SHORTER + AS + STARS + RISE + THE + HORSES + REST + SAFE + AFTER + ALL + SHARE + HOT + FISH + AS + THEIR + AFFILIATES + TAILOR + A + ROOFS + FOR + THEIR + SAFE == FORTRESSES",
        &[
            ('A', 1),
            ('E', 0),
            ('F', 5),
            ('H', 8),
            ('I', 7),
            ('L', 2),
            ('O', 6),
            ('R', 3),
            ('S', 4),
            ('T', 9),
        ],
    );

我知道正在生成正确的猜测,因为如果插入对哈希图的条件检查,则测试期望它成功。

关于如何调试的任何提示?

4

1 回答 1

0

溢出u8就加法了。修复了一些对 u64 的强制转换。

于 2020-10-11T19:30:28.447 回答