pub fn partition_count(n: usize) -> u64 {
partition_count_table(n)
.last()
.copied()
.unwrap_or(1)
}
pub fn partition_count_table(n_max: usize) -> Vec<u64> {
let mut p = vec![0u64; n_max + 1];
p[0] = 1;
for m in 1..=n_max {
let mut acc = 0i128;
let mut k: i64 = 1;
let mut sign: i64 = 1;
loop {
let w_pos = (k * (3 * k - 1) / 2) as usize;
let w_neg = (k * (3 * k + 1) / 2) as usize;
if w_pos > m {
break;
}
acc += sign * p[m - w_pos] as i128;
if w_neg <= m {
acc += sign * p[m - w_neg] as i128;
}
sign = -sign;
k += 1;
}
p[m] = acc.max(0) as u64;
}
p
}
pub fn list_partitions(n: usize) -> Vec<Vec<usize>> {
let mut result = Vec::new();
if n == 0 {
result.push(Vec::new());
return result;
}
let mut current = Vec::new();
enumerate_partitions(n, n, &mut current, &mut result);
result
}
fn enumerate_partitions(
remaining: usize,
max_part: usize,
current: &mut Vec<usize>,
result: &mut Vec<Vec<usize>>,
) {
if remaining == 0 {
result.push(current.clone());
return;
}
let limit = max_part.min(remaining);
for part in (1..=limit).rev() {
current.push(part);
enumerate_partitions(remaining - part, part, current, result);
current.pop();
}
}
pub fn compositions(n: usize, k: usize) -> Vec<Vec<usize>> {
if k == 0 {
if n == 0 {
return vec![Vec::new()];
} else {
return Vec::new();
}
}
let mut result = Vec::new();
let mut current = vec![0usize; k];
enumerate_compositions(n, k, 0, &mut current, &mut result);
result
}
fn enumerate_compositions(
remaining: usize,
k: usize,
pos: usize,
current: &mut Vec<usize>,
result: &mut Vec<Vec<usize>>,
) {
if pos == k - 1 {
current[pos] = remaining;
result.push(current.clone());
return;
}
for a in 0..=remaining {
current[pos] = a;
enumerate_compositions(remaining - a, k, pos + 1, current, result);
}
}
pub fn partition_into_distinct(n: usize) -> Vec<Vec<usize>> {
let mut result = Vec::new();
if n == 0 {
result.push(Vec::new());
return result;
}
let mut current = Vec::new();
enumerate_distinct_partitions(n, n, &mut current, &mut result);
result
}
fn enumerate_distinct_partitions(
remaining: usize,
max_part: usize,
current: &mut Vec<usize>,
result: &mut Vec<Vec<usize>>,
) {
if remaining == 0 {
result.push(current.clone());
return;
}
let limit = max_part.min(remaining);
for part in (1..=limit).rev() {
current.push(part);
enumerate_distinct_partitions(remaining - part, part - 1, current, result);
current.pop();
}
}
pub fn young_tableau(shape: &[usize]) -> Vec<Vec<Vec<usize>>> {
let n: usize = shape.iter().sum();
if n == 0 {
return vec![vec![]];
}
for w in shape.windows(2) {
debug_assert!(w[0] >= w[1], "shape must be non-increasing");
}
let rows = shape.len();
let mut tableau: Vec<Vec<usize>> = shape.iter().map(|&c| vec![0usize; c]).collect();
let mut fill_count = vec![0usize; rows];
let mut results: Vec<Vec<Vec<usize>>> = Vec::new();
fill_young_tableau(
n,
1,
shape,
&mut tableau,
&mut fill_count,
&mut results,
);
results
}
fn fill_young_tableau(
n: usize,
num: usize,
shape: &[usize],
tableau: &mut Vec<Vec<usize>>,
fill_count: &mut Vec<usize>,
results: &mut Vec<Vec<Vec<usize>>>,
) {
if num > n {
results.push(tableau.clone());
return;
}
let rows = shape.len();
for r in 0..rows {
let c = fill_count[r];
if c >= shape[r] {
continue; }
if r > 0 && fill_count[r - 1] <= c {
continue; }
tableau[r][c] = num;
fill_count[r] += 1;
fill_young_tableau(n, num + 1, shape, tableau, fill_count, results);
fill_count[r] -= 1;
tableau[r][c] = 0;
}
}
pub fn hook_length_formula(shape: &[usize]) -> u64 {
let n: usize = shape.iter().sum();
if n == 0 {
return 1;
}
let rows = shape.len();
let max_col = shape[0];
let mut col_len = vec![0usize; max_col];
for (r, &row_len) in shape.iter().enumerate() {
for c in 0..row_len {
if r < col_len.len() {
let _ = r; }
col_len[c] += 1;
}
}
let log_n_factorial: f64 = (1..=n).map(|k| (k as f64).ln()).sum();
let log_hooks: f64 = (0..rows)
.flat_map(|r| (0..shape[r]).map(move |c| (r, c)))
.map(|(r, c)| {
let arm = shape[r] - c - 1; let leg = col_len[c] - r - 1; let hook = arm + leg + 1;
(hook as f64).ln()
})
.sum();
let log_result = log_n_factorial - log_hooks;
let result_f64 = log_result.exp();
if result_f64 >= u64::MAX as f64 {
u64::MAX
} else {
result_f64.round() as u64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partition_count_small() {
let expected = [1u64, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42];
for (n, &exp) in expected.iter().enumerate() {
assert_eq!(partition_count(n), exp, "p({n})");
}
}
#[test]
fn test_partition_count_table() {
let t = partition_count_table(10);
let expected = [1u64, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42];
assert_eq!(t, expected);
}
#[test]
fn test_partition_count_medium() {
assert_eq!(partition_count(20), 627);
assert_eq!(partition_count(30), 5604);
}
#[test]
fn test_list_partitions_small() {
assert_eq!(list_partitions(0), vec![vec![] as Vec<usize>]);
assert_eq!(list_partitions(1).len(), 1);
assert_eq!(list_partitions(4).len(), 5);
assert_eq!(list_partitions(10).len(), 42);
for p in list_partitions(7) {
assert_eq!(p.iter().sum::<usize>(), 7);
}
}
#[test]
fn test_list_partitions_non_increasing() {
for partition in list_partitions(6) {
for w in partition.windows(2) {
assert!(w[0] >= w[1], "partition not non-increasing: {partition:?}");
}
}
}
#[test]
fn test_compositions_count() {
assert_eq!(compositions(3, 2).len(), 4);
assert_eq!(compositions(4, 3).len(), 15);
assert_eq!(compositions(0, 0).len(), 1);
assert_eq!(compositions(0, 3).len(), 1);
}
#[test]
fn test_compositions_sum() {
for comp in compositions(5, 3) {
assert_eq!(comp.iter().sum::<usize>(), 5);
assert_eq!(comp.len(), 3);
}
}
#[test]
fn test_partition_into_distinct() {
let expected_counts = [1usize, 1, 1, 2, 2, 4];
for (n, &expected) in expected_counts.iter().enumerate() {
let parts = partition_into_distinct(n);
assert_eq!(
parts.len(),
expected,
"partition_into_distinct({n}) should have {expected} partitions"
);
}
for part in partition_into_distinct(10) {
let sum: usize = part.iter().sum();
assert_eq!(sum, 10);
for w in part.windows(2) {
assert!(w[0] > w[1], "parts not strictly decreasing: {part:?}");
}
}
}
#[test]
fn test_young_tableau_count() {
assert_eq!(young_tableau(&[3]).len(), 1);
assert_eq!(young_tableau(&[1, 1, 1]).len(), 1);
assert_eq!(young_tableau(&[2, 1]).len(), 2);
assert_eq!(young_tableau(&[3, 2]).len(), 5);
assert_eq!(young_tableau(&[2, 2]).len(), 2);
}
#[test]
fn test_young_tableau_validity() {
for shape in [&[3, 2][..], &[2, 2, 1][..], &[3, 1, 1][..]] {
for tab in young_tableau(shape) {
let n: usize = shape.iter().sum();
let mut values: Vec<usize> = tab.iter().flatten().copied().collect();
values.sort_unstable();
assert_eq!(values, (1..=n).collect::<Vec<_>>());
for row in &tab {
for w in row.windows(2) {
assert!(w[0] < w[1], "row not increasing in {tab:?}");
}
}
for c in 0..tab[0].len() {
for r in 1..tab.len() {
if c < tab[r].len() {
assert!(tab[r - 1][c] < tab[r][c], "col not increasing in {tab:?}");
}
}
}
}
}
}
#[test]
fn test_hook_length_formula() {
assert_eq!(hook_length_formula(&[]), 1);
assert_eq!(hook_length_formula(&[1]), 1);
assert_eq!(hook_length_formula(&[3]), 1);
assert_eq!(hook_length_formula(&[1, 1, 1]), 1);
assert_eq!(hook_length_formula(&[2, 1]), 2);
assert_eq!(hook_length_formula(&[3, 2]), 5);
assert_eq!(hook_length_formula(&[2, 2]), 2);
assert_eq!(hook_length_formula(&[3, 2, 1]), 16);
assert_eq!(hook_length_formula(&[4, 3, 2, 1]), 768);
}
#[test]
fn test_hook_matches_tableau_count() {
for shape in [
&[2, 1][..],
&[3, 2][..],
&[2, 2][..],
&[3, 1][..],
&[3, 2, 1][..],
] {
let count = young_tableau(shape).len() as u64;
let hook = hook_length_formula(shape);
assert_eq!(count, hook, "mismatch for shape {shape:?}");
}
}
}