-
Notifications
You must be signed in to change notification settings - Fork 259
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(Data/Nat/Choose): Lucas' theorem #8612
base: master
Are you sure you want to change the base?
Changes from all commits
104a9a9
3d824f7
8040787
a9838e2
a5c023b
f715fbe
2aa16f3
edc6706
923f398
6c1c5a0
33aa024
165eeb6
ce6cae3
189904b
ec6f322
834f203
7b07f3e
9cd40dd
b4e1665
597d75b
cc7451d
784377b
b48564b
d77a2ed
c208c61
fc2553c
2647738
d0c1632
af85b80
5e9fc30
41ec0b3
9a33f0a
72d5597
bbe5853
56cf870
a76ae48
d710bc0
1f6e187
1d7ae5b
e82d217
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/- | ||
Copyright (c) 2023 Gareth Ma. All rights reserved. | ||
Released under Apache 2.0 license as described in the file LICENSE. | ||
Authors: Gareth Ma | ||
-/ | ||
import Mathlib.Data.ZMod.Basic | ||
import Mathlib.RingTheory.Polynomial.Basic | ||
|
||
/-! | ||
# Lucas's theorem | ||
|
||
This file contains a proof of [Lucas's theorem](https://en.wikipedia.org/wiki/Lucas's_theorem) about | ||
binomial coefficients, which says that for primes `p`, `n` choose `k` is congruent to product of | ||
`n_i` choose `k_i` modulo `p`, where `n_i` and `k_i` are the base-`p` digits of `n` and `k`, | ||
respectively. | ||
|
||
## Main statements | ||
|
||
* `lucas_theorem`: the binomial coefficient `n choose k` is congruent to the product of `n_i choose | ||
k_i` modulo `p`, where `n_i` and `k_i` are the base-`p` digits of `n` and `k`, respectively. | ||
-/ | ||
|
||
open Finset hiding choose | ||
|
||
open Nat BigOperators Polynomial | ||
|
||
namespace Choose | ||
|
||
variable {n k p : ℕ} [Fact p.Prime] | ||
|
||
/-- For primes `p`, `choose n k` is congruent to `choose (n % p) (k % p) * choose (n / p) (k / p)` | ||
modulo `p`. Also see `choose_modEq_choose_mod_mul_choose_div_nat` for the version with `MOD`. -/ | ||
theorem choose_modEq_choose_mod_mul_choose_div : | ||
grhkm21 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
choose n k ≡ choose (n % p) (k % p) * choose (n / p) (k / p) [ZMOD p] := by | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Both sides of this are nats, right? So maybe it'd be nice to have versions using MOD p as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with Bhavik here we should have the Nat version. @grhkm21, would you mind either adding this, or simply saying "sorry, can someone do it in a subsequent PR", and then you can merge? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It should be a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How should I name the theorems? I will add some There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
have decompose : ((X : (ZMod p)[X]) + 1) ^ n = (X + 1) ^ (n % p) * (X ^ p + 1) ^ (n / p) := by | ||
simpa using add_pow_eq_add_pow_mod_mul_pow_add_pow_div _ (X : (ZMod p)[X]) 1 | ||
simp only [← ZMod.intCast_eq_intCast_iff, Int.cast_mul, Int.cast_ofNat, | ||
← coeff_X_add_one_pow _ n k, ← eq_intCast (Int.castRingHom (ZMod p)), ← coeff_map, | ||
Polynomial.map_pow, Polynomial.map_add, Polynomial.map_one, map_X, decompose] | ||
simp only [add_pow, one_pow, mul_one, ← pow_mul, sum_mul_sum] | ||
conv_lhs => | ||
enter [1, 2, k, 2, k'] | ||
rw [← mul_assoc, mul_right_comm _ _ (X ^ (p * k')), ← pow_add, mul_assoc, ← cast_mul] | ||
have h_iff : ∀ x ∈ range (n % p + 1) ×ˢ range (n / p + 1), | ||
k = x.1 + p * x.2 ↔ (k % p, k / p) = x := by | ||
intro ⟨x₁, x₂⟩ hx | ||
rw [Prod.mk.injEq] | ||
constructor <;> intro h | ||
· simp only [mem_product, mem_range] at hx | ||
have h' : x₁ < p := lt_of_lt_of_le hx.left $ mod_lt _ Fin.size_pos' | ||
rw [h, add_mul_mod_self_left, add_mul_div_left _ _ Fin.size_pos', eq_comm (b := x₂)] | ||
exact ⟨mod_eq_of_lt h', self_eq_add_left.mpr (div_eq_of_lt h')⟩ | ||
· rw [← h.left, ← h.right, mod_add_div] | ||
simp only [finset_sum_coeff, coeff_mul_natCast, coeff_X_pow, ite_mul, zero_mul, ← cast_mul] | ||
rw [← sum_product', sum_congr rfl (fun a ha ↦ if_congr (h_iff a ha) rfl rfl), sum_ite_eq] | ||
split_ifs with h | ||
· simp | ||
· rw [mem_product, mem_range, mem_range, not_and_or, lt_succ, not_le, not_lt] at h | ||
cases h <;> simp [choose_eq_zero_of_lt (by tauto)] | ||
|
||
/-- For primes `p`, `choose n k` is congruent to `choose (n % p) (k % p) * choose (n / p) (k / p)` | ||
modulo `p`. Also see `choose_modEq_choose_mod_mul_choose_div` for the version with `ZMOD`. -/ | ||
theorem choose_modEq_choose_mod_mul_choose_div_nat : | ||
choose n k ≡ choose (n % p) (k % p) * choose (n / p) (k / p) [MOD p] := by | ||
rw [← Int.natCast_modEq_iff] | ||
exact_mod_cast choose_modEq_choose_mod_mul_choose_div | ||
|
||
/-- For primes `p`, `choose n k` is congruent to the product of `choose (⌊n / p ^ i⌋ % p) | ||
(⌊k / p ^ i⌋ % p)` over i < a, multiplied by `choose (⌊n / p ^ a⌋) (⌊k / p ^ a⌋)`, modulo `p`. -/ | ||
theorem choose_modEq_choose_mul_prod_range_choose (a : ℕ) : | ||
choose n k ≡ choose (n / p ^ a) (k / p ^ a) * | ||
∏ i in range a, choose (n / p ^ i % p) (k / p ^ i % p) [ZMOD p] := | ||
match a with | ||
| Nat.zero => by simp | ||
| Nat.succ a => (choose_modEq_choose_mul_prod_range_choose a).trans <| by | ||
rw [prod_range_succ, cast_mul, ← mul_assoc, mul_right_comm] | ||
gcongr | ||
apply choose_modEq_choose_mod_mul_choose_div.trans | ||
simp_rw [pow_succ, Nat.div_div_eq_div_mul, mul_comm] | ||
rfl | ||
|
||
/-- **Lucas's Theorem**: For primes `p`, `choose n k` is congruent to the product of | ||
`choose (⌊n / p ^ i⌋ % p) (⌊k / p ^ i⌋ % p)` over `i` modulo `p`. -/ | ||
theorem choose_modEq_prod_range_choose {a : ℕ} (ha₁ : n < p ^ a) (ha₂ : k < p ^ a) : | ||
choose n k ≡ ∏ i in range a, choose (n / p ^ i % p) (k / p ^ i % p) [ZMOD p] := by | ||
apply (choose_modEq_choose_mul_prod_range_choose a).trans | ||
simp_rw [Nat.div_eq_of_lt ha₁, Nat.div_eq_of_lt ha₂, choose, cast_one, one_mul, cast_prod, | ||
Int.ModEq.refl] | ||
|
||
/-- **Lucas's Theorem**: For primes `p`, `choose n k` is congruent to the product of | ||
`choose (⌊n / p ^ i⌋ % p) (⌊k / p ^ i⌋ % p)` over `i` modulo `p`. -/ | ||
theorem choose_modEq_prod_range_choose_nat {a : ℕ} (ha₁ : n < p ^ a) (ha₂ : k < p ^ a) : | ||
choose n k ≡ ∏ i in range a, choose (n / p ^ i % p) (k / p ^ i % p) [MOD p] := by | ||
rw [← Int.natCast_modEq_iff] | ||
exact_mod_cast choose_modEq_prod_range_choose ha₁ ha₂ | ||
|
||
alias lucas_theorem := choose_modEq_prod_range_choose | ||
alias lucas_theorem_nat := choose_modEq_prod_range_choose_nat | ||
|
||
end Choose |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like
n
andp
should be explicit. If you're willing to fix it, can you open a separate PR to fix it in the entire file (to avoid scope creep here)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops I missed this, I will do it yes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@YaelDillies I think
p
doesn't have to be explicit sincex
andy
would inferR
which should uniquely inferp
- at least mathematically the second part is true?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mathematically, yes. In practice, not really 😢
Try it for yourself by rewriting using your lemmas with
R
instantiated to specific charp
rings, likeZMod p
orFin p
.