Skip to content

Commit 52cc3b4

Browse files
AntoineChambert-Loirpre-commit-ci-lite[bot]joneugster
committed
feat(Data/Nat/Choose/Multinomial): multinomial coefficients (leanprover-community#35830)
Define the multinomial coefficients, a variant of `Nat.multinomial`. - redefine `Multiset.multinomial`. Given a multiset `m` of natural numbers, `m.multinomial` is the multinomial coefficient defined by (m.sum) ! / ∏ i ∈ m, m i !. As an example, `Multiset.multinomial {1, 2, 2} = 30`. This is the exponent of $x y^2 z^2$ in $(x+y+z)^5$. This should not be confused with the existing `Multiset.multinomial` which gives a different answer, for example, `Multiset.multinomial {1, 2, 2} = 3`. This function is renamed as `Multiset.countPerms`. - `Multiset.multinomial_cons` proves that `(x ::ₘ m).multinomial = Nat.choose (x + m.sum) x * m.multinomial` - `Multiset.multinomial_add` proves that `(m + m').multinomial = Nat.choose (m + m').sum m.sum * m.multinomial * m'.multinomial` co-authored with @mariainesdff Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: Jon Eugster <eugster.jon@gmail.com>
1 parent b446ba6 commit 52cc3b4

2 files changed

Lines changed: 122 additions & 1 deletion

File tree

Mathlib/Data/List/ToFinsupp.lean

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ theorem toFinsupp_support :
6767
l.toFinsupp.support = {i ∈ Finset.range l.length | getD l i 00} :=
6868
rfl
6969

70+
lemma toFinsupp_support_subset :
71+
l.toFinsupp.support ⊆ Finset.range l.length := by
72+
simp [List.toFinsupp_support]
73+
7074
theorem toFinsupp_apply_lt (hn : n < l.length) : l.toFinsupp n = l[n] :=
7175
getD_eq_getElem _ _ hn
7276

Mathlib/Data/Nat/Choose/Multinomial.lean

Lines changed: 118 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ module
77

88
public import Mathlib.Algebra.Order.Antidiag.Pi
99
public import Mathlib.Data.Finsupp.Multiset
10+
public import Mathlib.Data.List.ToFinsupp
1011
public import Mathlib.Data.Nat.Choose.Sum
1112
public import Mathlib.Data.Nat.Factorial.BigOperators
1213
public import Mathlib.Data.Nat.Factorial.DoubleFactorial
13-
1414
/-!
1515
# Multinomial
1616
@@ -27,6 +27,29 @@ This file defines the multinomial coefficients and several small lemmas for mani
2727
2828
- `Finset.sum_pow`: The expansion of `(s.sum x) ^ n` using multinomial coefficients
2929
30+
- `Multiset.multinomial`.
31+
Given a multiset `m` of natural numbers, `m.multinomial` is the
32+
multinomial coefficient defined by `(m.sum) ! / ∏ i ∈ m, m i !`.
33+
34+
This should not be confused with `m.countPerms` which
35+
is defined as `m.toFinsupp.multinomial`.
36+
37+
As an example, one has `Multiset.multinomial {1, 2, 2} = 30`,
38+
while `Multiset.countPerms {1, 2, 2} = 3`.
39+
40+
- `Multiset.multinomial_cons` proves that
41+
`(x ::ₘ m).multinomial = Nat.choose (x + m.sum) x * m.multinomial`
42+
- `Multiset.multinomial_add` proves that
43+
`(m + m').multinomial = Nat.choose (m + m').sum m.sum * m.multinomial * m'.multinomial`
44+
45+
## Implementation note for `Multiset.multinomial`.
46+
47+
To avoid the definition of `Multiset.multinomial` as a quotient given above,
48+
we define it in terms of `Finsupp.multinomial`, via lists:
49+
If `m : Multiset ℕ` is the multiset associated with a list `l : List ℕ`,
50+
then `m.multinomial = l.toFinsupp.multinomial`.
51+
Then we prove its invariance under permutation.
52+
3053
-/
3154

3255
@[expose] public section
@@ -385,3 +408,97 @@ theorem Finsupp.multinomial_of_support_subset {σ : Type*} {d : σ →₀ ℕ} {
385408
rw [Nat.multinomial, Finsupp.multinomial,
386409
sum_of_support_subset _ h _ (by simp), prod_of_support_subset _ h _ (by simp)]
387410
simp
411+
412+
namespace List
413+
414+
open Nat
415+
416+
lemma toFinsupp_sum {α : Type*} [AddCommMonoid α] [DecidableEq α] (l : List α) :
417+
l.toFinsupp.sum (fun _ a ↦ a) = l.sum := by
418+
match l with
419+
| nil => simp
420+
| x :: l =>
421+
simp only [toFinsupp_cons_eq_single_add_embDomain, sum_cons]
422+
rw [Finsupp.sum_add_index (by simp) (by simp)]
423+
simp [Finsupp.sum_embDomain, l.toFinsupp_sum]
424+
425+
/-- The multinomial coefficients given by a list of natural numbers.
426+
427+
See also `Multiset.multinomial` -/
428+
abbrev multinomial (l : List ℕ) : ℕ := l.toFinsupp.multinomial
429+
430+
theorem multinomial_cons (x : ℕ) (l : List ℕ) :
431+
(x :: l).multinomial = Nat.choose (x + l.sum) x * l.multinomial := by
432+
simp only [multinomial]
433+
rw [Finsupp.multinomial_update 0 (x :: l).toFinsupp]
434+
congr 1
435+
· congr
436+
exact List.toFinsupp_sum (x :: l)
437+
let succEmb : ℕ ↪ ℕ := { toFun := Nat.succ, inj' := Nat.succ_injective }
438+
simp only [toFinsupp_cons_eq_single_add_embDomain, Finsupp.multinomial_eq]
439+
have : (Finsupp.single 0 x + l.toFinsupp.embDomain succEmb).update 0 0 =
440+
(l.toFinsupp.embDomain succEmb).update 0 0 := by
441+
ext i
442+
by_cases hi : i = 0
443+
· simp [hi]
444+
· simp [Finsupp.update_apply, if_neg hi, Finsupp.single_eq_of_ne hi]
445+
have h (x) : (l.toFinsupp.embDomain succEmb) (x + 1) = l[x]?.getD 0 := by
446+
rw [Finsupp.embDomain_apply, dif_pos ⟨x, by simp [succEmb]⟩]
447+
simp [succEmb]
448+
simp [succEmb, this, Nat.multinomial, h]
449+
450+
end List
451+
452+
namespace Multiset
453+
454+
/-- The `multinomial` coefficients on `Multiset ℕ`. -/
455+
def multinomial (m : Multiset ℕ) : ℕ := Quot.liftOn m List.multinomial <| fun l l' h ↦ by
456+
induction h with
457+
| nil => simp
458+
| @cons x l l' hl hl' => simp [List.multinomial_cons, hl', hl.sum_nat]
459+
| @swap x y l =>
460+
simp only [List.multinomial_cons, ← mul_assoc, List.sum_cons]
461+
rw [← Nat.choose_symm (Nat.le_add_right y _), add_tsub_cancel_left]
462+
rw [add_left_comm, Nat.choose_mul (Nat.le_add_right _ _), add_tsub_cancel_left]
463+
simp [← Nat.choose_symm (Nat.le_add_right _ _), add_tsub_cancel_left]
464+
| @trans l l' l'' h h' ih ih' => rw [ih, ih']
465+
466+
theorem multinomial_cons (x : ℕ) (m : Multiset ℕ) :
467+
(x ::ₘ m).multinomial = Nat.choose (x + m.sum) x * m.multinomial := by
468+
obtain ⟨l, rfl⟩ := Quotient.exists_rep m
469+
exact List.multinomial_cons x l
470+
471+
@[simp]
472+
theorem multinomial_zero : Multiset.multinomial 0 = 1 := rfl
473+
474+
@[simp]
475+
theorem multinomial_singleton (n : ℕ) :
476+
Multiset.multinomial {n} = 1 := by
477+
simp [← cons_zero, multinomial_cons]
478+
479+
theorem multinomial_add (m m' : Multiset ℕ) :
480+
(m + m').multinomial = Nat.choose (m + m').sum m.sum * m.multinomial * m'.multinomial := by
481+
induction m using Multiset.induction_on with
482+
| empty => simp
483+
| cons x m hind =>
484+
simp only [cons_add, sum_cons, sum_add, multinomial_cons, hind, ← mul_assoc]
485+
congr 2
486+
rw [← Nat.choose_symm (Nat.le_add_right _ _), add_tsub_cancel_left, eq_comm,
487+
Nat.choose_mul (Nat.le_add_right _ _), ← Nat.choose_symm (Nat.le_add_right x _)]
488+
simp [add_tsub_cancel_left]
489+
490+
theorem multinomial_nsmul (k : ℕ) (m : Multiset ℕ) :
491+
(k • m).multinomial = Nat.multinomial (Finset.range k) (fun _ ↦ m.sum) * m.multinomial ^ k := by
492+
induction k with
493+
| zero => simp
494+
| succ k hk =>
495+
rw [succ_nsmul', multinomial_add, hk, Finset.range_add_one,
496+
Nat.multinomial_insert (by simp), sum_add, sum_nsmul, pow_succ']
497+
simp [smul_eq_mul, Finset.sum_const, Finset.card_range]
498+
ring
499+
500+
theorem multinomial_nsmul_singleton (k n : ℕ) :
501+
(k • {n} : Multiset ℕ).multinomial = Nat.multinomial (Finset.range k) (fun _ ↦ n) := by
502+
simp [multinomial_nsmul]
503+
504+
end Multiset

0 commit comments

Comments
 (0)