Theory Erdos_Ginzburg_Ziv
theory Erdos_Ginzburg_Ziv
imports
EGZ_Prime
"HOL-Computational_Algebra.Primes"
begin
section ‹The full Erd\H{o}s-Ginzburg-Ziv theorem›
text ‹
The composite-modulus case is obtained from the prime case by strong
induction. Writing @{term n} as @{text "m * p"} with @{term p} prime, we
repeatedly extract @{term p}-element zero-sum blocks, divide their sums by
@{term p}, and apply the induction hypothesis to the resulting multiset of
quotients.
›
subsection ‹Auxiliary multiset decompositions›
lemma exists_submultiset_of_size:
fixes M :: "'a::linorder multiset"
assumes n_le: "n ≤ size M"
shows "∃N. N ⊆# M ∧ size N = n"
proof -
let ?xs = "sorted_list_of_multiset M"
let ?N = "mset (take n ?xs)"
have N_sub: "?N ⊆# M"
by (metis mset_nths_subseteq mset_sorted_list_of_multiset nths_upt_eq_take)
have N_size: "size ?N = n"
proof -
have xs_len: "size M = length ?xs"
by (metis mset_sorted_list_of_multiset size_mset)
then show ?thesis
using n_le by auto
qed
show ?thesis
using N_sub N_size by (intro exI[of _ ?N]) simp
qed
lemma union_blocks_size:
fixes Bs :: "'a multiset multiset"
assumes blocks_size: "∀B∈#Bs. size B = n"
shows "size (sum_mset Bs) = size Bs * n"
using blocks_size
by (induction Bs) auto
lemma union_blocks_div_sum:
assumes blocks_div: "∀B∈#Bs. sum_mset B mod int p = 0"
shows "sum_mset (sum_mset Bs) = int p * sum_mset (image_mset (λB. sum_mset B div int p) Bs)"
using blocks_div
proof (induction Bs)
case empty
then show ?case
by simp
next
case (add B Bs)
have B_div: "sum_mset B mod int p = 0"
using add.prems by simp
have rest_div: "∀C∈#Bs. sum_mset C mod int p = 0"
using add.prems by auto
have B_eq: "sum_mset B = int p * (sum_mset B div int p)"
using div_mult_mod_eq[of "sum_mset B" "int p"] B_div by (simp add: mult.commute)
have "sum_mset (sum_mset (add_mset B Bs)) = sum_mset B + sum_mset (sum_mset Bs)"
by simp
also have "… = int p * (sum_mset B div int p) + int p * sum_mset (image_mset (λB. sum_mset B div int p) Bs)"
using B_eq add.IH[OF rest_div] by simp
also have "… = int p * (sum_mset B div int p + sum_mset (image_mset (λB. sum_mset B div int p) Bs))"
by (simp add: algebra_simps)
also have "… = int p * sum_mset (image_mset (λB. sum_mset B div int p) (add_mset B Bs))"
by simp
finally show ?case .
qed
lemma :
assumes prime_p: "prime p"
assumes size_M: "size M = k * p + (p - 1)"
shows "∃Bs R. M = sum_mset Bs + R ∧ size Bs = k ∧ size R = p - 1 ∧
(∀B∈#Bs. size B = p ∧ sum_mset B mod int p = 0)"
using assms
proof (induction k arbitrary: M)
case 0
then show ?case
by (intro exI[of _ "{#}"] exI[of _ M]) simp
next
case (Suc k)
have p_pos: "0 < p"
using prime_gt_0_nat[OF Suc.prems(1)] .
have size_ge: "2 * p - 1 ≤ size M"
proof -
have p_le: "p ≤ Suc k * p"
using p_pos by simp
have "2 * p - 1 = p + (p - 1)"
using p_pos by arith
then show ?thesis
using Suc.prems(2) p_le by arith
qed
from exists_submultiset_of_size[OF size_ge]
obtain M0 where M0_sub: "M0 ⊆# M" and size_M0: "size M0 = 2 * p - 1"
by auto
from prime_egz[OF Suc.prems(1) size_M0]
obtain B where B_sub0: "B ⊆# M0" and B_size: "size B = p" and B_sum: "sum_mset B mod int p = 0"
by auto
have B_sub: "B ⊆# M"
using B_sub0 M0_sub by auto
have rem_size: "size (M - B) = k * p + (p - 1)"
proof -
have MB: "M = B + (M - B)"
using B_sub by simp
have "size M = size (B + (M - B))"
using MB by simp
also have "… = size B + size (M - B)"
by simp
finally have "size M = size B + size (M - B)" .
then have "size (M - B) = size M - size B"
using B_sub by simp
then show ?thesis
using Suc.prems(2) B_size by simp
qed
from Suc.IH[OF Suc.prems(1) rem_size]
obtain Bs R where rem_split: "M - B = sum_mset Bs + R"
and Bs_size: "size Bs = k"
and R_size: "size R = p - 1"
and Bs_good: "∀C∈#Bs. size C = p ∧ sum_mset C mod int p = 0"
by auto
let ?Bs = "add_mset B Bs"
have M_split: "M = sum_mset ?Bs + R"
using B_sub rem_split subset_mset.add_diff_inverse by fastforce
have Bs_all_good: "∀C∈#?Bs. size C = p ∧ sum_mset C mod int p = 0"
using B_size B_sum Bs_good by auto
show ?case
using M_split Bs_size R_size Bs_all_good
by (intro exI[of _ ?Bs] exI[of _ R]) simp
qed
subsection ‹Strong induction on the modulus›
text ‹
The prime branch is handled directly by @{thm [source] prime_egz}. For a
composite modulus @{term n}, the previous lemma extracts @{text "2 * m - 1"}
prime-sized blocks with sums divisible by @{term p}. Applying the induction
hypothesis to the block sums divided by @{term p} selects enough blocks whose
union has size @{term n} and total sum divisible by @{term n}.
›
theorem erdos_ginzburg_ziv_exact:
assumes n_pos: "0 < n"
assumes size_M: "size M = 2 * n - 1"
shows "∃N. N ⊆# M ∧ size N = n ∧ sum_mset N mod int n = 0"
using n_pos size_M
proof (induction n arbitrary: M rule: less_induct)
case (less n)
have n_pos: "0 < n"
using less.prems(1) .
have size_M: "size M = 2 * n - 1"
using less.prems(2) .
show ?case
proof (cases "n = 1")
case True
then show ?thesis
using n_pos size_M by (intro exI[of _ M]) auto
next
case False
then have n_gt1: "1 < n"
using n_pos by simp
show ?thesis
proof (cases "prime n")
case True
then show ?thesis
using prime_egz[OF True size_M] by simp
next
case False
from prime_factor_nat[of n] n_gt1
obtain p where prime_p: "prime p" and p_dvd: "p dvd n"
by auto
have p_pos: "0 < p"
using prime_gt_0_nat[OF prime_p] .
define m where "m = n div p"
have n_eq: "n = m * p"
using p_dvd p_pos by (simp add: m_def mult.commute)
have m_pos: "0 < m"
using n_eq n_pos by auto
have m_lt_n: "m < n"
by (simp add: m_pos n_eq prime_gt_Suc_0_nat prime_p)
have blocks_size_eq: "size M = (2 * m - 1) * p + (p - 1)"
proof -
have "size M = 2 * (m * p) - 1"
using size_M n_eq by simp
also have "… = ((2 * m - 1) + 1) * p - 1"
using m_pos by (simp add: algebra_simps)
also have "… = (2 * m - 1) * p + (p - 1)"
using p_pos by (simp add: algebra_simps)
finally show ?thesis .
qed
from extract_prime_blocks[OF prime_p blocks_size_eq]
obtain Bs R where M_split: "M = sum_mset Bs + R"
and Bs_size: "size Bs = 2 * m - 1"
and R_size: "size R = p - 1"
and Bs_good: "∀B∈#Bs. size B = p ∧ sum_mset B mod int p = 0"
by auto
let ?f = "λB. sum_mset B div int p"
let ?Sums = "image_mset ?f Bs"
have sums_size: "size ?Sums = 2 * m - 1"
using Bs_size by simp
from less.IH[OF m_lt_n, of ?Sums] m_pos sums_size
obtain S where S_sub: "S ⊆# ?Sums"
and S_size: "size S = m"
and S_sum: "sum_mset S mod int m = 0"
by auto
obtain C where sums_split: "?Sums = S + C"
using S_sub by (auto simp: subset_mset.le_iff_add)
from image_mset_eq_plusD[OF sums_split]
obtain Bs1 Bs2 where Bs_split: "Bs = Bs1 + Bs2"
and S_image: "S = image_mset ?f Bs1"
and C_image: "C = image_mset ?f Bs2"
by auto
have Bs1_size: "size Bs1 = m"
using S_size S_image by simp
have Bs1_good: "∀B∈#Bs1. size B = p ∧ sum_mset B mod int p = 0"
using Bs_good Bs_split by auto
have N_sub: "sum_mset Bs1 ⊆# M"
by (simp add: Bs_split M_split)
have N_size: "size (sum_mset Bs1) = n"
using Bs1_good Bs1_size n_eq union_blocks_size by blast
have N_sum: "sum_mset (sum_mset Bs1) mod int n = 0"
proof -
have Bs1_div: "∀B∈#Bs1. sum_mset B mod int p = 0"
using Bs1_good by auto
have sums_mod: "sum_mset (image_mset ?f Bs1) mod int m = 0"
using S_sum S_image by simp
have total_eq: "sum_mset (sum_mset Bs1) = int p * sum_mset (image_mset ?f Bs1)"
by (rule union_blocks_div_sum[OF Bs1_div])
have m_dvd: "int m dvd sum_mset (image_mset ?f Bs1)"
using sums_mod by (simp add: dvd_eq_mod_eq_0)
then obtain t where t_eq: "sum_mset (image_mset ?f Bs1) = int m * t"
by auto
have "sum_mset (sum_mset Bs1) = int n * t"
using total_eq t_eq n_eq by (simp add: algebra_simps)
then show ?thesis
by simp
qed
show ?thesis
using N_sub N_size N_sum by (intro exI[of _ "sum_mset Bs1"]) simp
qed
qed
qed
subsection ‹The monotone cardinality form›
theorem erdos_ginzburg_ziv:
assumes n_pos: "0 < n"
assumes size_M: "size M ≥ 2 * n - 1"
shows "∃N. N ⊆# M ∧ size N = n ∧ sum_mset N mod int n = 0"
proof -
from exists_submultiset_of_size[of "2 * n - 1" M] size_M
obtain M0 where M0_sub: "M0 ⊆# M" and M0_size: "size M0 = 2 * n - 1"
by auto
from erdos_ginzburg_ziv_exact[OF n_pos M0_size]
obtain N where N_sub0: "N ⊆# M0" and N_size: "size N = n" and N_sum: "sum_mset N mod int n = 0"
by auto
have N_sub: "N ⊆# M"
using N_sub0 M0_sub by auto
show ?thesis
using N_sub N_size N_sum by (intro exI[of _ N]) simp
qed
end