generated from mschoi/template
Add composition functions to combinatoric module
- Introduced new functions for generating compositions, including `composition_of_length`, `composition`, `permuted_composition`, and their variants with minimum and maximum constraints. - Added depth-first search (DFS) logic for generating compositions and permutations. - Included unit tests to verify the correctness of the new composition functionalities. - Updated the combinatoric module to reflect these additions.
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
pub mod prob17;
|
||||
pub mod prob49;
|
||||
pub mod prob491;
|
||||
pub mod prob497;
|
||||
pub mod prob61;
|
||||
pub mod prob650;
|
||||
pub mod prob66;
|
||||
pub mod prob719;
|
||||
pub mod prob885;
|
||||
pub mod prob932;
|
||||
pub mod prob934;
|
||||
|
||||
579
src/project_euler/prob719.rs
Normal file
579
src/project_euler/prob719.rs
Normal file
@@ -0,0 +1,579 @@
|
||||
/// Number Splitting
|
||||
///
|
||||
/// We define an S-number to be a natural number, n, that is a perfect square and its square root can be obtained by splitting the decimal representation of n into k or more numbers then adding the numbers.
|
||||
///
|
||||
/// For example, 81 is an S-number because \(\sqrt{81} = 8 + 1\).
|
||||
///
|
||||
/// 6724 is an S-number: \(\sqrt{6724} = 6 + 72 + 4\).
|
||||
///
|
||||
/// 8281 is an S-number: \(\sqrt{8281} = 8 + 2 + 81\).
|
||||
///
|
||||
/// 9801 is an S-number: \(\sqrt{9801} = 98 + 0 + 1\).
|
||||
///
|
||||
/// T(N) is the sum of all S-numbers n <= N.
|
||||
///
|
||||
/// You are given T(10^4) = 41333.
|
||||
/// Find T(10^12).
|
||||
///
|
||||
use crate::utils::{
|
||||
combinatoric::{composition, composition_with_min_max},
|
||||
integer::is_square,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
|
||||
fn split_number(digits: &Vec<u32>, comp: Vec<&usize>) -> Vec<u32> {
|
||||
comp.iter()
|
||||
.fold((0, Vec::new()), |(cursor, mut result), &c| {
|
||||
result.push(
|
||||
digits[cursor..cursor + c]
|
||||
.iter()
|
||||
.fold(0, |num, &d| num * 10 + d),
|
||||
);
|
||||
(cursor + c, result)
|
||||
})
|
||||
.1
|
||||
}
|
||||
|
||||
fn is_s_number(n: usize) -> bool {
|
||||
if !is_square(n) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let square_root = (n as f64).sqrt() as u32;
|
||||
let digits = n
|
||||
.to_string()
|
||||
.chars()
|
||||
.map(|c| c.to_digit(10).unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
let compositions = composition(digits.len());
|
||||
for comp in compositions {
|
||||
for permuted_comp in comp.iter().permutations(comp.len()) {
|
||||
let split_numbers = split_number(&digits, permuted_comp);
|
||||
if split_numbers.iter().sum::<u32>() == square_root {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn list_of_s_numbers<T>(iter: T) -> Vec<usize>
|
||||
where
|
||||
T: IntoIterator<Item = usize>,
|
||||
{
|
||||
iter.into_iter().filter(|n| is_s_number(*n)).collect()
|
||||
}
|
||||
|
||||
pub fn sum_of_s_numbers<T>(iter: T) -> usize
|
||||
where
|
||||
T: IntoIterator<Item = usize>,
|
||||
{
|
||||
iter.into_iter().filter(|n| is_s_number(*n)).sum()
|
||||
}
|
||||
|
||||
pub fn sum_of_s_numbers_up_to(n: usize) -> usize {
|
||||
let sqrt_n = (n as f64).sqrt() as usize;
|
||||
sum_of_s_numbers((1..=sqrt_n).map(|i| i * i))
|
||||
}
|
||||
|
||||
pub fn list_of_s_numbers_up_to(n: usize) -> Vec<usize> {
|
||||
let sqrt_n = (n as f64).sqrt() as usize;
|
||||
list_of_s_numbers((1..=sqrt_n).map(|i| i * i))
|
||||
}
|
||||
|
||||
fn can_concatenate_to(n: usize, num_vec: &Vec<usize>, digit: usize) -> bool {
|
||||
for permuted_num_vec in num_vec.iter().permutations(num_vec.len()) {
|
||||
let concatenated_num = permuted_num_vec
|
||||
.iter()
|
||||
.fold(String::new(), |acc, num| format!("{}{}", acc, num))
|
||||
.parse::<usize>()
|
||||
.unwrap();
|
||||
let concatenated_num_digit = concatenated_num.to_string().len();
|
||||
if concatenated_num == n && concatenated_num_digit == digit {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn list_of_s_numbers_of_digit(digit: usize) -> HashSet<usize> {
|
||||
fn number_of_digit(d: usize) -> Vec<usize> {
|
||||
if d == 1 {
|
||||
return vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
}
|
||||
let start = 10u32.pow(d as u32 - 1);
|
||||
let end = 10u32.pow(d as u32);
|
||||
(start..end).map(|i| i as usize).collect()
|
||||
}
|
||||
let mut result = HashSet::new();
|
||||
|
||||
for comp in composition(digit) {
|
||||
let sum_max: u64 = comp.iter().map(|d| 10u64.pow(*d as u32) - 1).sum();
|
||||
if sum_max * sum_max <= 10u64.pow(digit as u32 - 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for num_vec in comp
|
||||
.iter()
|
||||
.map(|c| number_of_digit(*c))
|
||||
.multi_cartesian_product()
|
||||
{
|
||||
let sum = num_vec.iter().sum::<usize>();
|
||||
if can_concatenate_to(sum * sum, &num_vec, digit) {
|
||||
result.insert(sum * sum);
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SNumber {
|
||||
pub composition: Vec<usize>,
|
||||
pub digits: Vec<Option<u8>>,
|
||||
}
|
||||
|
||||
pub fn digit_to_num<'a, T>(digits: T) -> usize
|
||||
where
|
||||
T: IntoIterator<Item = &'a u8>,
|
||||
{
|
||||
digits.into_iter().fold(0, |acc, d| acc * 10 + *d as usize)
|
||||
}
|
||||
|
||||
pub fn digit_to_num_owned<T>(digits: T) -> usize
|
||||
where
|
||||
T: IntoIterator<Item = u8>,
|
||||
{
|
||||
digits.into_iter().fold(0, |acc, d| acc * 10 + d as usize)
|
||||
}
|
||||
|
||||
impl SNumber {
|
||||
fn new(composition: &Vec<usize>) -> Self {
|
||||
let length = composition.iter().sum::<usize>();
|
||||
Self {
|
||||
composition: composition.clone(),
|
||||
digits: vec![None; length],
|
||||
}
|
||||
}
|
||||
|
||||
fn check(&self) -> Option<usize> {
|
||||
if self.digits.iter().any(|d| d.is_none()) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let digits = self.digits.iter().map(|d| d.unwrap()).collect::<Vec<_>>();
|
||||
let num = digit_to_num(&digits[..]);
|
||||
let comp_sum = self
|
||||
.composition
|
||||
.iter()
|
||||
.fold((0, 0), |(sum, cursor), d| {
|
||||
(sum + digit_to_num(&digits[cursor..cursor + d]), cursor + d)
|
||||
})
|
||||
.0;
|
||||
if comp_sum * comp_sum == num {
|
||||
Some(num)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn min_digit(&self) -> Vec<u8> {
|
||||
self.digits
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, d)| {
|
||||
if i == 0 {
|
||||
d.unwrap_or(1)
|
||||
} else {
|
||||
d.unwrap_or(0)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn max_digit(&self) -> Vec<u8> {
|
||||
self.digits.iter().map(|d| d.unwrap_or(9)).collect()
|
||||
}
|
||||
|
||||
fn comp_sum(&self, digits: &Vec<u8>) -> usize {
|
||||
self.composition
|
||||
.iter()
|
||||
.fold((0, 0), |(sum, cursor), d| {
|
||||
(sum + digit_to_num(&digits[cursor..cursor + d]), cursor + d)
|
||||
})
|
||||
.0
|
||||
}
|
||||
|
||||
fn range_comp_sum(&self) -> (usize, usize) {
|
||||
let sum_min = self.comp_sum(&self.min_digit());
|
||||
let sum_max = self.comp_sum(&self.max_digit());
|
||||
(sum_min, sum_max)
|
||||
}
|
||||
|
||||
fn range_num(&self) -> (usize, usize) {
|
||||
let num_min = digit_to_num_owned(self.min_digit());
|
||||
let num_max = digit_to_num_owned(self.max_digit());
|
||||
(num_min, num_max)
|
||||
}
|
||||
|
||||
fn has_common_range(&self) -> bool {
|
||||
let (num_min, num_max) = self.range_num();
|
||||
let (sum_min, sum_max) = self.range_comp_sum();
|
||||
num_min <= sum_max * sum_max && num_max >= sum_min * sum_min
|
||||
}
|
||||
|
||||
fn dominant_digit(&self) -> usize {
|
||||
let mut place_vec = vec![1; self.digits.len()];
|
||||
let mut cursor = 0;
|
||||
for c in self.composition.iter() {
|
||||
let mut place = 10i32.pow(*c as u32 - 1);
|
||||
for _ in 0..*c {
|
||||
place_vec[cursor] = place;
|
||||
place /= 10;
|
||||
cursor += 1;
|
||||
}
|
||||
}
|
||||
self.digits
|
||||
.iter()
|
||||
.zip(place_vec.iter())
|
||||
.enumerate()
|
||||
.max_by_key(|(i, (d, p))| match d {
|
||||
Some(_) => (-1, 0),
|
||||
None => (**p, -(*i as i32)),
|
||||
})
|
||||
.unwrap()
|
||||
.0
|
||||
}
|
||||
|
||||
fn find_solution(&mut self) -> HashSet<usize> {
|
||||
if !self.has_common_range() {
|
||||
return HashSet::new();
|
||||
} else if let Some(num) = self.check() {
|
||||
return HashSet::from([num]);
|
||||
}
|
||||
|
||||
let dominant_digit = self.dominant_digit();
|
||||
let mut result = HashSet::new();
|
||||
let candidates = if dominant_digit == 0 { 1..=9 } else { 0..=9 };
|
||||
|
||||
for candidate in candidates {
|
||||
self.digits[dominant_digit] = Some(candidate);
|
||||
result.extend(self.find_solution());
|
||||
self.digits[dominant_digit] = None;
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
pub fn list_of_s_numbers_final(digit: usize) -> HashSet<usize> {
|
||||
let mut result = HashSet::new();
|
||||
let mut visited = HashSet::new();
|
||||
for i in 2..=digit {
|
||||
let minmax = (i - 1) / 2;
|
||||
for comp in composition_with_min_max(i, minmax) {
|
||||
let s_number = SNumber::new(&comp);
|
||||
if !s_number.has_common_range() {
|
||||
continue;
|
||||
}
|
||||
let length = comp.len();
|
||||
for perm in comp.into_iter().permutations(length) {
|
||||
if visited.contains(&perm) {
|
||||
continue;
|
||||
}
|
||||
visited.insert(perm.clone());
|
||||
let mut s_number = SNumber::new(&perm);
|
||||
result.extend(s_number.find_solution());
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn is_s_number_answer(comp_sum: i64, concatenated_num: i64) -> bool {
|
||||
if concatenated_num < comp_sum || comp_sum < 0 {
|
||||
return false;
|
||||
} else if concatenated_num == comp_sum {
|
||||
return true;
|
||||
}
|
||||
|
||||
let mut scissor = 10;
|
||||
while scissor < concatenated_num {
|
||||
let remain_concatenated_num = concatenated_num / scissor;
|
||||
let sliced = concatenated_num % scissor;
|
||||
if is_s_number_answer(comp_sum - sliced, remain_concatenated_num) {
|
||||
return true;
|
||||
}
|
||||
scissor *= 10;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn sum_of_s_numbers_answer(n: i64) -> i64 {
|
||||
let max_comp_sum = n.isqrt();
|
||||
let mut sum = 0;
|
||||
for comp_sum in 2..=max_comp_sum {
|
||||
if is_s_number_answer(comp_sum, comp_sum * comp_sum) {
|
||||
sum += comp_sum * comp_sum;
|
||||
}
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_split_number() {
|
||||
let digits = vec![1, 3, 4, 5];
|
||||
|
||||
let comp = vec![&2, &2];
|
||||
assert_eq!(split_number(&digits, comp), vec![13, 45]);
|
||||
|
||||
let comp = vec![&1, &1, &1, &1];
|
||||
assert_eq!(split_number(&digits, comp), vec![1, 3, 4, 5]);
|
||||
|
||||
let comp = vec![&1, &2, &1];
|
||||
assert_eq!(split_number(&digits, comp), vec![1, 34, 5]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_s_number() {
|
||||
assert!(is_s_number(81));
|
||||
assert!(is_s_number(6724));
|
||||
assert!(is_s_number(8281));
|
||||
assert!(is_s_number(9801));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sum_of_s_numbers() {
|
||||
assert_eq!(sum_of_s_numbers(1..=10000), 41333);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_of_s_numbers() {
|
||||
assert_eq!(
|
||||
list_of_s_numbers(1..=100000),
|
||||
vec![
|
||||
81, 100, 1296, 2025, 3025, 6724, 8281, 9801, 10000, 55225, 88209
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_s_numbers_up_to() {
|
||||
assert_eq!(
|
||||
list_of_s_numbers_up_to(100000),
|
||||
list_of_s_numbers(1..=100000)
|
||||
);
|
||||
assert_eq!(sum_of_s_numbers_up_to(100000), sum_of_s_numbers(1..=100000));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_of_s_numbers_of_digit() {
|
||||
assert_eq!(list_of_s_numbers_of_digit(2), HashSet::from([81]));
|
||||
assert_eq!(list_of_s_numbers_of_digit(3), HashSet::from([100]));
|
||||
assert_eq!(
|
||||
list_of_s_numbers_of_digit(4),
|
||||
HashSet::from([1296, 2025, 3025, 6724, 8281, 9801])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_list_of_s_numbers_of_digit_large() {
|
||||
assert_eq!(
|
||||
list_of_s_numbers_of_digit(5),
|
||||
HashSet::from([10000, 55225, 88209])
|
||||
);
|
||||
assert_eq!(
|
||||
list_of_s_numbers_of_digit(6),
|
||||
HashSet::from([
|
||||
136161, 136900, 143641, 171396, 431649, 455625, 494209, 571536, 627264, 826281,
|
||||
842724, 893025, 929296, 980100, 982081, 998001
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_digit_to_num() {
|
||||
assert_eq!(digit_to_num(&vec![1, 2, 3]), 123);
|
||||
assert_eq!(digit_to_num(&vec![1, 2, 3, 4]), 1234);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_check_s_number() {
|
||||
let mut s_number = SNumber::new(&vec![1, 1]);
|
||||
s_number.digits = vec![Some(8), Some(1)];
|
||||
assert_eq!(s_number.check(), Some(81));
|
||||
s_number.digits = vec![Some(1), Some(8)];
|
||||
assert_eq!(s_number.check(), None);
|
||||
|
||||
let mut s_number = SNumber::new(&vec![1, 2, 1]);
|
||||
s_number.digits = vec![Some(6), Some(7), Some(2), Some(4)];
|
||||
assert_eq!(s_number.check(), Some(6724));
|
||||
s_number.digits = vec![Some(4), Some(7), Some(2), Some(6)];
|
||||
assert_eq!(s_number.check(), None);
|
||||
|
||||
let mut s_number = SNumber::new(&vec![2, 1, 1]);
|
||||
s_number.digits = vec![Some(9), Some(8), Some(0), Some(1)];
|
||||
assert_eq!(s_number.check(), Some(9801));
|
||||
|
||||
s_number.digits = vec![Some(8), Some(2), Some(8), Some(1)];
|
||||
assert_eq!(s_number.check(), Some(8281));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_min_max_digit() {
|
||||
let mut s_number = SNumber::new(&vec![1, 1]);
|
||||
assert_eq!(s_number.min_digit(), vec![1, 0]);
|
||||
assert_eq!(s_number.max_digit(), vec![9, 9]);
|
||||
|
||||
s_number.digits[0] = Some(2);
|
||||
assert_eq!(s_number.min_digit(), vec![2, 0]);
|
||||
assert_eq!(s_number.max_digit(), vec![2, 9]);
|
||||
|
||||
s_number.digits[1] = Some(2);
|
||||
assert_eq!(s_number.min_digit(), vec![2, 2]);
|
||||
assert_eq!(s_number.max_digit(), vec![2, 2]);
|
||||
|
||||
let mut s_number = SNumber::new(&vec![2, 1, 1]);
|
||||
assert_eq!(s_number.min_digit(), vec![1, 0, 0, 0]);
|
||||
assert_eq!(s_number.max_digit(), vec![9, 9, 9, 9]);
|
||||
|
||||
s_number.digits[1] = Some(2);
|
||||
assert_eq!(s_number.min_digit(), vec![1, 2, 0, 0]);
|
||||
assert_eq!(s_number.max_digit(), vec![9, 2, 9, 9]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_num() {
|
||||
let mut s_number = SNumber::new(&vec![1, 1]);
|
||||
assert_eq!(s_number.range_num(), (10, 99));
|
||||
|
||||
s_number.digits[0] = Some(2);
|
||||
assert_eq!(s_number.range_num(), (20, 29));
|
||||
|
||||
s_number.digits[0] = None;
|
||||
s_number.digits[1] = Some(2);
|
||||
assert_eq!(s_number.range_num(), (12, 92));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_range_comp_sum() {
|
||||
let mut s_number = SNumber::new(&vec![1, 1]);
|
||||
assert_eq!(s_number.range_comp_sum(), (1, 18));
|
||||
|
||||
s_number.digits[0] = Some(2);
|
||||
assert_eq!(s_number.range_comp_sum(), (2, 11));
|
||||
|
||||
s_number.digits[0] = None;
|
||||
s_number.digits[1] = Some(2);
|
||||
assert_eq!(s_number.range_comp_sum(), (3, 11));
|
||||
|
||||
let mut s_number = SNumber::new(&vec![2, 1]);
|
||||
assert_eq!(s_number.range_comp_sum(), (10, 108));
|
||||
|
||||
s_number.digits[0] = Some(2);
|
||||
assert_eq!(s_number.range_comp_sum(), (20, 38));
|
||||
|
||||
s_number.digits[0] = None;
|
||||
s_number.digits[1] = Some(2);
|
||||
assert_eq!(s_number.range_comp_sum(), (12, 101));
|
||||
|
||||
let mut s_number = SNumber::new(&vec![1, 2, 1]);
|
||||
assert_eq!(s_number.range_comp_sum(), (1, 117));
|
||||
|
||||
s_number.digits[0] = Some(2);
|
||||
assert_eq!(s_number.range_comp_sum(), (2, 110));
|
||||
|
||||
s_number.digits[0] = None;
|
||||
s_number.digits[1] = Some(2);
|
||||
assert_eq!(s_number.range_comp_sum(), (21, 47));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dominant_digit() {
|
||||
let mut s_number = SNumber::new(&vec![1, 1]);
|
||||
assert_eq!(s_number.dominant_digit(), 0);
|
||||
|
||||
s_number.digits[0] = Some(2);
|
||||
assert_eq!(s_number.dominant_digit(), 1);
|
||||
|
||||
s_number.digits[0] = None;
|
||||
s_number.digits[1] = Some(2);
|
||||
assert_eq!(s_number.dominant_digit(), 0);
|
||||
|
||||
let mut s_number = SNumber::new(&vec![2, 3, 1]);
|
||||
assert_eq!(s_number.dominant_digit(), 2);
|
||||
|
||||
s_number.digits[2] = Some(2);
|
||||
assert_eq!(s_number.dominant_digit(), 0);
|
||||
|
||||
s_number.digits[0] = Some(2);
|
||||
assert_eq!(s_number.dominant_digit(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_has_common_range() {
|
||||
let mut s_number = SNumber::new(&vec![1, 1]);
|
||||
assert!(s_number.has_common_range());
|
||||
|
||||
for i in 1..=9 {
|
||||
s_number.digits[0] = Some(i);
|
||||
assert!(s_number.has_common_range());
|
||||
}
|
||||
|
||||
let mut s_number = SNumber::new(&vec![1, 1, 1, 1]);
|
||||
assert!(s_number.has_common_range());
|
||||
|
||||
for i in 1..=9 {
|
||||
s_number.digits[0] = Some(i);
|
||||
assert!(!s_number.has_common_range());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_solution() {
|
||||
let mut s_number = SNumber::new(&vec![1, 1]);
|
||||
assert_eq!(s_number.find_solution(), HashSet::from([81]));
|
||||
|
||||
let mut s_number = SNumber::new(&vec![2, 1]);
|
||||
assert_eq!(s_number.find_solution(), HashSet::from([100]));
|
||||
|
||||
let mut s_number = SNumber::new(&vec![1, 2, 1]);
|
||||
assert_eq!(s_number.find_solution(), HashSet::from([1296, 6724]));
|
||||
|
||||
let mut s_number = SNumber::new(&vec![2, 1, 1]);
|
||||
assert_eq!(s_number.find_solution(), HashSet::from([8281, 9801]));
|
||||
|
||||
let mut s_number = SNumber::new(&vec![2, 2]);
|
||||
assert_eq!(s_number.find_solution(), HashSet::from([2025, 3025, 9801]));
|
||||
|
||||
let mut s_number = SNumber::new(&vec![3, 2]);
|
||||
assert_eq!(s_number.find_solution(), HashSet::from([10000]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_list_of_s_numbers_final() {
|
||||
assert_eq!(
|
||||
list_of_s_numbers_final(6),
|
||||
HashSet::from([
|
||||
81, 100, 1296, 2025, 3025, 6724, 8281, 9801, 10000, 55225, 88209, 136161, 136900,
|
||||
143641, 171396, 431649, 455625, 494209, 571536, 627264, 826281, 842724, 893025,
|
||||
929296, 980100, 982081, 998001
|
||||
])
|
||||
);
|
||||
|
||||
assert_eq!(list_of_s_numbers_final(4).iter().sum::<usize>(), 31333);
|
||||
// assert_eq!(list_of_s_numbers_final(12).iter().sum::<usize>(), 41333);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sum_s_number_answer() {
|
||||
assert_eq!(sum_of_s_numbers_answer(10000), 41333);
|
||||
assert_eq!(sum_of_s_numbers_answer(1_000_000_000_000), 128088830547982);
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn factorial(n: usize) -> usize {
|
||||
assert!(
|
||||
n <= 20,
|
||||
@@ -16,3 +19,195 @@ pub fn combination(n: usize, k: usize) -> usize {
|
||||
pub fn combination_with_repetition(n: usize, k: usize) -> usize {
|
||||
combination(n + k - 1, k)
|
||||
}
|
||||
|
||||
pub fn composition_of_length(n: usize, length: usize) -> Vec<Vec<usize>> {
|
||||
fn dfs(
|
||||
n: usize,
|
||||
length: usize,
|
||||
idx: usize,
|
||||
current: &mut Vec<usize>,
|
||||
result: &mut Vec<Vec<usize>>,
|
||||
) {
|
||||
let remain = n - current.iter().sum::<usize>();
|
||||
let value_limit = if idx == 0 { n - 1 } else { current[idx - 1] };
|
||||
if remain == 0 && idx == length {
|
||||
result.push(current.clone());
|
||||
return;
|
||||
} else if remain > (length - idx) * value_limit || idx == length {
|
||||
return;
|
||||
}
|
||||
|
||||
let max_value = remain.min(value_limit);
|
||||
for i in (1..=max_value).rev() {
|
||||
current[idx] = i;
|
||||
dfs(n, length, idx + 1, current, result);
|
||||
}
|
||||
current[idx] = 0;
|
||||
}
|
||||
|
||||
let mut result = Vec::new();
|
||||
let mut current = vec![0; length];
|
||||
dfs(n, length, 0, &mut current, &mut result);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn composition(n: usize) -> Vec<Vec<usize>> {
|
||||
let mut result = Vec::new();
|
||||
for length in 2..=n {
|
||||
result.extend(composition_of_length(n, length));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn permuted_composition(n: usize) -> HashSet<Vec<usize>> {
|
||||
let mut visited = HashSet::new();
|
||||
for length in 2..=n {
|
||||
for comp in composition_of_length(n, length) {
|
||||
for perm in comp.iter().permutations(length) {
|
||||
visited.insert(perm.iter().map(|d| **d).collect::<Vec<_>>());
|
||||
}
|
||||
}
|
||||
}
|
||||
visited
|
||||
}
|
||||
|
||||
pub fn composition_of_length_with_min_max(
|
||||
n: usize,
|
||||
length: usize,
|
||||
minmax: usize,
|
||||
) -> Vec<Vec<usize>> {
|
||||
fn dfs(
|
||||
n: usize,
|
||||
length: usize,
|
||||
idx: usize,
|
||||
current: &mut Vec<usize>,
|
||||
result: &mut Vec<Vec<usize>>,
|
||||
) {
|
||||
let remain = n - current.iter().sum::<usize>();
|
||||
let value_limit = if idx == 0 { n - 1 } else { current[idx - 1] };
|
||||
if remain == 0 && idx == length {
|
||||
result.push(current.clone());
|
||||
return;
|
||||
} else if remain > (length - idx) * value_limit || idx == length {
|
||||
return;
|
||||
}
|
||||
|
||||
let max_value = remain.min(value_limit);
|
||||
for i in (1..=max_value).rev() {
|
||||
current[idx] = i;
|
||||
dfs(n, length, idx + 1, current, result);
|
||||
}
|
||||
current[idx] = 0;
|
||||
}
|
||||
|
||||
let mut result = Vec::new();
|
||||
let mut current = vec![0; length];
|
||||
for i in (minmax..n).rev() {
|
||||
current[0] = i;
|
||||
dfs(n, length, 1, &mut current, &mut result);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn composition_with_min_max(n: usize, minmax: usize) -> Vec<Vec<usize>> {
|
||||
let mut result = Vec::new();
|
||||
for length in 2..=(n - minmax + 1) {
|
||||
result.extend(composition_of_length_with_min_max(n, length, minmax));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn permuted_composition_with_min_max(n: usize, minmax: usize) -> HashSet<Vec<usize>> {
|
||||
let mut visited = HashSet::new();
|
||||
for length in 2..=(n - minmax + 1) {
|
||||
for comp in composition_of_length_with_min_max(n, length, minmax) {
|
||||
for perm in comp.iter().permutations(length) {
|
||||
visited.insert(perm.iter().map(|d| **d).collect::<Vec<_>>());
|
||||
}
|
||||
}
|
||||
}
|
||||
visited
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_composition_of_length() {
|
||||
let result = composition_of_length(10, 2);
|
||||
assert_eq!(
|
||||
result,
|
||||
vec![vec![9, 1], vec![8, 2], vec![7, 3], vec![6, 4], vec![5, 5]]
|
||||
);
|
||||
|
||||
let result = composition_of_length(8, 3);
|
||||
assert_eq!(
|
||||
result,
|
||||
vec![
|
||||
vec![6, 1, 1],
|
||||
vec![5, 2, 1],
|
||||
vec![4, 3, 1],
|
||||
vec![4, 2, 2],
|
||||
vec![3, 3, 2]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_composition() {
|
||||
let result = composition(5);
|
||||
assert_eq!(
|
||||
result,
|
||||
vec![
|
||||
vec![4, 1],
|
||||
vec![3, 2],
|
||||
vec![3, 1, 1],
|
||||
vec![2, 2, 1],
|
||||
vec![2, 1, 1, 1],
|
||||
vec![1, 1, 1, 1, 1]
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_permuted_composition() {
|
||||
let result = permuted_composition(3);
|
||||
assert_eq!(
|
||||
result,
|
||||
HashSet::from([vec![1, 2], vec![2, 1], vec![1, 1, 1]])
|
||||
);
|
||||
|
||||
let result = permuted_composition(4);
|
||||
assert_eq!(
|
||||
result,
|
||||
HashSet::from([
|
||||
vec![1, 3],
|
||||
vec![3, 1],
|
||||
vec![2, 2],
|
||||
vec![1, 1, 2],
|
||||
vec![1, 2, 1],
|
||||
vec![2, 1, 1],
|
||||
vec![1, 1, 1, 1]
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_composition_of_length_with_min_max() {
|
||||
let result = composition_of_length_with_min_max(2, 2, 1);
|
||||
assert_eq!(result, vec![vec![1, 1]]);
|
||||
|
||||
let result = composition_of_length_with_min_max(8, 2, 6);
|
||||
assert_eq!(result, vec![vec![7, 1], vec![6, 2]]);
|
||||
|
||||
let result = composition_of_length_with_min_max(8, 3, 6);
|
||||
assert_eq!(result, vec![vec![6, 1, 1]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_composition_with_min_max() {
|
||||
let result = composition_with_min_max(8, 6);
|
||||
assert_eq!(result, vec![vec![7, 1], vec![6, 2], vec![6, 1, 1]]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user