Theory Derangements
section ‹Derangements›
theory Derangements
imports
Complex_Main
"HOL-Combinatorics.Permutations"
begin
subsection ‹Preliminaries›
subsubsection ‹Additions to @{theory HOL.Finite_Set} Theory›
lemma card_product_dependent:
assumes "finite S" "∀x ∈ S. finite (T x)"
shows "card {(x, y). x ∈ S ∧ y ∈ T x} = (∑x ∈ S. card (T x))"
using card_SigmaI[OF assms, symmetric] by (auto intro!: arg_cong[where f=card] simp add: Sigma_def)
subsubsection ‹Additions to @{theory "HOL-Combinatorics.Permutations"} Theory›
lemma permutes_imp_bij':
assumes "p permutes S"
shows "bij p"
using assms by (fact permutes_bij)
lemma permutesE:
assumes "p permutes S"
obtains "bij p" "∀x. x ∉ S ⟶ p x = x"
using assms by (simp add: permutes_def permutes_imp_bij')
lemma bij_imp_permutes':
assumes "bij p" "∀x. x ∉ A ⟶ p x = x"
shows "p permutes A"
using assms bij_imp_permutes permutes_superset by force
lemma permutes_swap:
assumes "p permutes S"
shows "Fun.swap x y p permutes (insert x (insert y S))"
by (rule permutes_compose) (use assms in ‹simp_all add: assms permutes_imp_permutes_insert permutes_swap_id›)
lemma bij_extends:
"bij p ⟹ p x = x ⟹ bij (p(x := y, inv p y := x))"
unfolding bij_def
proof (rule conjI; erule conjE)
assume a: "inj p" "p x = x"
show "inj (p(x := y, inv p y := x))"
proof (intro injI)
fix z z'
assume "(p(x := y, inv p y := x)) z = (p(x := y, inv p y := x)) z'"
from this a show "z = z'"
by (auto split: if_split_asm simp add: inv_f_eq inj_eq)
qed
next
assume a: "inj p" "surj p" "p x = x"
{
fix x'
from a have "(p(x := y, inv p y := x)) (((inv p)(y := x, x := inv p y)) x') = x'"
by (auto split: if_split_asm) (metis surj_f_inv_f)+
}
from this show "surj (p(x := y, inv p y := x))" by (metis surjI)
qed
lemma permutes_add_one:
assumes "p permutes S" "x ∉ S" "y ∈ S"
shows "p(x := y, inv p y := x) permutes (insert x S)"
proof (rule bij_imp_permutes')
from assms show "bij (p(x := y, inv p y := x))"
by (meson bij_extends permutes_def permutes_imp_bij')
from assms show "∀z. z ∉ insert x S ⟶ (p(x := y, inv p y := x)) z = z"
by (metis fun_upd_apply insertCI permutes_def permutes_inverses(1))
qed
lemma permutations_skip_one:
assumes "p permutes S" "x : S"
shows "p(x := x, inv p x := p x) permutes (S - {x})"
proof (rule bij_imp_permutes')
from assms show "∀z. z ∉ S - {x} ⟶ (p(x := x, inv p x := p x)) z = z"
by (auto elim: permutesE simp add: bij_inv_eq_iff)
(simp add: assms(1) permutes_in_image permutes_inv)
from assms have "inj (p(x := x, inv p x := p x))"
by (intro injI) (auto split: if_split_asm; metis permutes_inverses(2))+
moreover have "surj (p(x := x, inv p x := p x))"
using assms UNIV_I bij_betw_swap_iff permutes_inj permutes_surj surj_f_inv_f
by (metis (no_types, opaque_lifting) Fun.swap_def bij_betw_def)
ultimately show "bij (p(x := x, inv p x := p x))"
by (rule bijI)
qed
lemma permutes_drop_cycle_size_two:
‹p ∘ Transposition.transpose x (p x) permutes (S - {x, p x})›
if ‹p permutes S› ‹p (p x) = x›
proof (rule bij_imp_permutes')
from ‹p permutes S› have ‹bij p›
by (rule permutes_imp_bij')
then show ‹bij (p ∘ Transposition.transpose x (p x))›
by (simp add: bij_swap_iff)
from ‹p (p x) = x› ‹p permutes S› have ‹(p ∘ Transposition.transpose x (p x)) y = y›
if ‹y ∉ S - {x, p x}› for y
using that by (cases ‹y ∈ {x, p x}›) (auto simp add: permutes_not_in)
then show ‹∀y. y ∉ S - {x, p x} ⟶
(p ∘ Transposition.transpose x (p x)) y = y›
by blast
qed
subsection ‹Fixpoint-Free Permutations›
definition derangements :: "nat set ⇒ (nat ⇒ nat) set"
where
"derangements S = {p. p permutes S ∧ (∀x ∈ S. p x ≠ x)}"
lemma derangementsI:
assumes "p permutes S" "⋀x. x ∈ S ⟹ p x ≠ x"
shows "p ∈ derangements S"
using assms unfolding derangements_def by auto
lemma derangementsE:
assumes "d : derangements S"
obtains "d permutes S" "∀x∈S. d x ≠ x"
using assms unfolding derangements_def by auto
subsection ‹Properties of Derangements›
lemma derangements_inv:
assumes d: "d ∈ derangements S"
shows "inv d ∈ derangements S"
using assms
by (auto intro!: derangementsI elim!: derangementsE simp add: permutes_inv permutes_inv_eq)
lemma derangements_in_image:
assumes "d ∈ derangements A" "x ∈ A"
shows "d x ∈ A"
using assms by (auto elim: derangementsE simp add: permutes_in_image)
lemma derangements_in_image_strong:
assumes "d ∈ derangements A" "x ∈ A"
shows "d x ∈ A - {x}"
using assms by (auto elim: derangementsE simp add: permutes_in_image)
lemma derangements_inverse_in_image:
assumes "d ∈ derangements A" "x ∈ A"
shows "inv d x ∈ A"
using assms by (auto intro: derangements_in_image derangements_inv)
lemma derangements_fixpoint:
assumes "d ∈ derangements A" "x ∉ A"
shows "d x = x"
using assms by (auto elim!: derangementsE simp add: permutes_def)
lemma derangements_no_fixpoint:
assumes "d ∈ derangements A" "x ∈ A"
shows "d x ≠ x"
using assms by (auto elim: derangementsE)
lemma finite_derangements:
assumes "finite A"
shows "finite (derangements A)"
using assms unfolding derangements_def
by (auto simp add: finite_permutations)
subsection ‹Construction of Derangements›
lemma derangements_empty[simp]:
"derangements {} = {id}"
unfolding derangements_def by auto
lemma derangements_singleton[simp]:
"derangements {x} = {}"
unfolding derangements_def by auto
lemma derangements_swap:
assumes "d ∈ derangements S" "x ∉ S" "y ∉ S" "x ≠ y"
shows "Fun.swap x y d ∈ derangements (insert x (insert y S))"
proof (rule derangementsI)
from assms show "Fun.swap x y d permutes (insert x (insert y S))"
by (auto intro: permutes_swap elim: derangementsE)
from assms have s: "d x = x" "d y = y"
by (auto intro: derangements_fixpoint)
{
fix x'
assume "x' : insert x (insert y S)"
from s assms ‹x ≠ y› this show "Fun.swap x y d x' ≠ x'"
by (cases "x' = x"; cases "x' = y") (auto dest: derangements_no_fixpoint)
}
qed
lemma derangements_skip_one:
assumes d: "d ∈ derangements S" and "x ∈ S" "d (d x) ≠ x"
shows "d(x := x, inv d x := d x) ∈ derangements (S - {x})"
proof -
from d have bij: "bij d"
by (auto elim: derangementsE simp add: permutes_imp_bij')
from d ‹x : S› have that: "d x : S - {x}"
by (auto dest: derangements_in_image derangements_no_fixpoint)
from d ‹d (d x) ≠ x› bij have "∀x'∈S - {x}. (d(x := x, inv d x := d x)) x' ≠ x'"
by (auto elim!: derangementsE simp add: bij_inv_eq_iff)
from d ‹x : S› this show derangements: "d(x:=x, inv d x:= d x) : derangements (S - {x})"
by (meson derangementsE derangementsI permutations_skip_one)
qed
lemma derangements_add_one:
assumes "d ∈ derangements S" "x ∉ S" "y ∈ S"
shows "d(x := y, inv d y := x) ∈ derangements (insert x S)"
proof (rule derangementsI)
from assms show "d(x := y, inv d y := x) permutes (insert x S)"
by (auto intro: permutes_add_one elim: derangementsE)
next
fix z
assume "z : insert x S"
from assms this derangements_inverse_in_image[OF assms(1), of y]
show "(d(x := y, inv d y := x)) z ≠ z" by (auto elim: derangementsE)
qed
lemma derangements_drop_minimal_cycle:
assumes "d ∈ derangements S" "d (d x) = x"
shows "Fun.swap x (d x) d ∈ derangements (S - {x, d x})"
proof (rule derangementsI)
from assms show "Fun.swap x (d x) d permutes (S - {x, d x})"
by (meson derangementsE permutes_drop_cycle_size_two)
next
fix y
assume "y ∈ S - {x, d x}"
from assms this show "Fun.swap x (d x) d y ≠ y"
by (auto elim: derangementsE)
qed
subsection ‹Cardinality of Derangements›
subsubsection ‹Recursive Characterization›
fun count_derangements :: "nat ⇒ nat"
where
"count_derangements 0 = 1"
| "count_derangements (Suc 0) = 0"
| "count_derangements (Suc (Suc n)) = (n + 1) * (count_derangements (Suc n) + count_derangements n)"
lemma card_derangements:
assumes "finite S" "card S = n"
shows "card (derangements S) = count_derangements n"
using assms
proof (induct n arbitrary: S rule: count_derangements.induct)
case 1
from this show ?case by auto
next
case 2
from this derangements_singleton finite_derangements show ?case
using Finite_Set.card_0_eq card_eq_SucD count_derangements.simps(2) by fastforce
next
case (3 n)
from 3(4) obtain x where "x ∈ S" using card_eq_SucD insertI1 by auto
let ?D1 = "(λ(y, d). Fun.swap x y d) ` {(y, d). y ∈ S & y ≠ x & d : derangements (S - {x, y})}"
let ?D2 = "(λ(y, f). f(x:=y, inv f y := x)) ` ((S - {x}) × derangements (S - {x}))"
from ‹x ∈ S› have subset1: "?D1 ⊆ derangements S"
proof (auto)
fix y d
assume "y ∈ S" "y ≠ x"
assume d: "d ∈ derangements (S - {x, y})"
from ‹x : S› ‹y : S› have S: "S = insert x (insert y (S - {x, y}))" by auto
from d ‹x : S› ‹y : S› ‹y ≠ x› show "Fun.swap x y d ∈ derangements S"
by (subst S) (auto intro!: derangements_swap)
qed
have subset2: "?D2 ⊆ derangements S"
proof (rule subsetI, erule imageE, simp split: prod.split_asm, (erule conjE)+)
fix d y
assume "d : derangements (S - {x})" "y : S" "y ≠ x"
from this have "d(x := y, inv d y := x) ∈ derangements (insert x (S - {x}))"
by (intro derangements_add_one) auto
from this ‹x : S› show "d(x := y, inv d y := x) ∈ derangements S"
using insert_Diff by fastforce
qed
have split: "derangements S = ?D1 ∪ ?D2"
proof
from subset1 subset2 show "?D1 ∪ ?D2 ⊆ derangements S" by simp
next
show "derangements S ⊆ ?D1 ∪ ?D2"
proof
fix d
assume d: "d : derangements S"
show "d : ?D1 ∪ ?D2"
proof (cases "d (d x) = x")
case True
from ‹x : S› d have "d x ∈ S" "d x ≠ x"
by (auto simp add: derangements_in_image derangements_no_fixpoint)
from d True have "Fun.swap x (d x) d ∈ derangements (S - {x, d x})"
by (rule derangements_drop_minimal_cycle)
with ‹d x ∈ S› ‹d x ≠ x› have ‹d ∈ ?D1›
by (auto intro!: image_eqI [where x = ‹(d x, Fun.swap x (d x) d)›])
from this show ?thesis by auto
next
case False
from d have bij: "bij d"
by (auto elim: derangementsE simp add: permutes_imp_bij')
from d ‹x : S› have that: "d x : S - {x}"
by (intro derangements_in_image_strong)
from d ‹x : S› False have derangements: "d(x:=x, inv d x:= d x) : derangements (S - {x})"
by (auto intro: derangements_skip_one)
from this have "bij (d(x := x, inv d x:= d x))"
by (metis derangementsE permutes_imp_bij')+
from this have a: "inv (d(x := x, inv d x := d x)) (d x) = inv d x"
by (metis bij_inv_eq_iff fun_upd_same)
from bij have x: "d (inv d x) = x" by (meson bij_inv_eq_iff)
from d derangements_inv[of d] ‹x : S› have "inv d x ≠ x" "d x ≠ x"
by (auto dest: derangements_no_fixpoint)
from this a x have d_eq: "d = d(inv d x := d x, x := d x, inv (d(x := x, inv d x := d x)) (d x) := x)"
by auto
from derangements that have "(d x, d(x:=x, inv d x:=d x)) : ((S - {x}) × derangements (S - {x}))" by auto
from d_eq this have "d : ?D2"
by (auto intro: image_eqI[where x = "(d x, d(x:=x, inv d x:=d x))"])
from this show ?thesis by auto
qed
qed
qed
have no_intersect: "?D1 ∩ ?D2 = {}"
proof -
have that: "⋀d. d ∈ ?D1 ⟹ d (d x) = x"
using Diff_iff Diff_insert2 derangements_fixpoint insertI1 swap_apply(2) by fastforce
have "⋀d. d ∈ ?D2 ⟹ d (d x) ≠ x"
proof -
fix d
assume a: "d ∈ ?D2"
from a obtain y d' where d: "d = d'(x := y, inv d' y := x)"
"d' ∈ derangements (S - {x})" "y ∈ S - {x}"
by auto
from d(2) have inv: "inv d' ∈ derangements (S - {x})"
by (rule derangements_inv)
from d have inv_x: "inv d' y ≠ x"
by (auto dest: derangements_inverse_in_image)
from inv have inv_y: "inv d' y ≠ y"
using d(3) derangements_no_fixpoint by blast
from d inv_x have 1: "d x = y" by auto
from d inv_y have 2: "d y = d' y" by auto
from d(2, 3) have 3: "d' y ∈ S - {x}"
by (auto dest: derangements_in_image)
from 1 2 3 show "d (d x) ≠ x" by auto
qed
from this that show ?thesis by blast
qed
have inj: "inj_on (λ(y, f). Fun.swap x y f) {(y, f). y ∈ S & y ≠ x & f : derangements (S - {x, y})}"
unfolding inj_on_def
by (clarify; metis DiffD2 derangements_fixpoint insertI1 insert_commute swap_apply(1) swap_nilpotent)
have eq: "{(y, f). y ∈ S & y ≠ x & f : derangements (S - {x, y})} = {(y, f). y ∈ S - {x} & f : derangements (S - {x, y})}"
by simp
have eq': "{(y, f). y ∈ S & y ≠ x & f : derangements (S - {x, y})} = Sigma (S - {x}) (%y. derangements (S - {x, y}))"
unfolding Sigma_def by auto
have card: "⋀ y. y ∈ S - {x} ⟹ card (derangements (S - {x, y})) = count_derangements n"
proof -
fix y
assume "y ∈ S - {x}"
from 3(3, 4) ‹x ∈ S› this have "card (S - {x, y}) = n"
by (metis Diff_insert2 card_Diff_singleton diff_Suc_1 finite_Diff)
from 3(3) 3(2)[OF _ this] show "card (derangements (S - {x, y})) = count_derangements n" by auto
qed
from 3(3, 4) ‹x : S› have card2: "card (S - {x}) = Suc n" by (simp add: card.insert_remove insert_absorb)
from inj have "card ?D1 = card {(y, f). y ∈ S - {x} ∧ f ∈ derangements (S - {x, y})}"
by (simp add: card_image)
also from 3(3) have "... = (∑y∈S - {x}. card (derangements (S - {x, y})))"
by (subst card_product_dependent) (simp add: finite_derangements)+
finally have card_1: "card ?D1 =(Suc n) * count_derangements n"
using card card2 by auto
have inj_2: "inj_on (λ(y, f). f(x := y, inv f y := x)) ((S - {x}) × derangements (S - {x}))"
proof -
{
fix d d' y y'
assume 1: "d ∈ derangements (S - {x})" "d' ∈ derangements (S - {x})"
assume 2: "y ∈ S" "y ≠ x" "y' ∈ S" "y' ≠ x"
assume eq: "d(x := y, inv d y := x) = d'(x := y', inv d' y' := x)"
from 1 2 eq ‹x ∈ S› have y: "y = y'"
by (metis Diff_insert_absorb derangements_in_image derangements_inv
fun_upd_same fun_upd_twist insert_iff mk_disjoint_insert)
have "d = d'"
proof
fix z
from 1 have d_x: "d x = d' x"
by (auto dest!: derangements_fixpoint)
from 1 have bij: "bij d" "bij d'"
by (metis derangementsE permutes_imp_bij')+
from this have d_d: "d (inv d y) = y" "d' (inv d' y') = y'"
by (simp add: bij_is_surj surj_f_inv_f)+
from 1 2 bij have neq: "d (inv d' y') ≠ x ∧ d' (inv d y) ≠ x"
by (metis Diff_iff bij_inv_eq_iff derangements_fixpoint singletonI)
from eq have "(d(x := y, inv d y := x)) z = (d'(x := y', inv d' y' := x)) z" by auto
from y d_x d_d neq this show "d z = d' z" by (auto split: if_split_asm)
qed
from y this have "y = y' & d = d'" by auto
}
from this show ?thesis
unfolding inj_on_def by auto
qed
from 3(3) 3(1)[OF _ card2] have card3: "card (derangements (S - {x})) = count_derangements (Suc n)"
by auto
from 3(3) inj_2 have card_2: "card ?D2 = (Suc n) * count_derangements (Suc n)"
by (simp only: card_image) (auto simp add: card_cartesian_product card3 card2)
from ‹finite S›have finite1: "finite ?D1"
unfolding eq' by (auto intro: finite_derangements)
from ‹finite S› have finite2: "finite ?D2"
by (auto intro: finite_cartesian_product finite_derangements)
show ?case
by (simp add: split card_Un_disjoint finite1 finite2 no_intersect card_1 card_2 field_simps)
qed
subsubsection ‹Closed-Form Characterization›
lemma count_derangements:
"real (count_derangements n) = fact n * (∑k ∈ {0..n}. (-1) ^ k / fact k)"
proof (induct n rule: count_derangements.induct)
case (3 n)
let ?f = "λn. fact n * (∑k = 0..n. (- 1) ^ k / fact k)"
have "real (count_derangements (Suc (Suc n))) = (n + 1) * (count_derangements (n + 1) + count_derangements n)"
unfolding count_derangements.simps by simp
also have "... = real (n + 1) * (real (count_derangements (n + 1)) + real (count_derangements n))"
by (simp only: of_nat_mult of_nat_add)
also have "... = (n + 1) * (?f (n + 1) + ?f n)"
unfolding 3(2) 3(1)[unfolded Suc_eq_plus1] ..
also have "(n + 1) * (?f (n + 1) + ?f n) = ?f (n + 2)"
proof -
define f where "f n = ((fact n) :: real) * (∑k = 0..n. (- 1) ^ k / fact k)" for n
have f_eq: "⋀n. f (n + 1) = (n + 1) * f n + (- 1) ^ (n + 1)"
proof -
fix n
have "?f (n + 1) = (n + 1) * fact n * (∑k = 0..n. (- 1) ^ k / fact k) + fact (n + 1) * ((- 1) ^ (n + 1) / fact (n + 1))"
by (simp add: field_simps)
also have "... = (n + 1) * ?f n + (- 1) ^ (n + 1)" by (simp del: One_nat_def)
finally show "?thesis n" unfolding f_def by simp
qed
from this have f_eq': "⋀ n. (n + 1) * f n = f (n + 1) + (- 1) ^ n" by auto
from f_eq'[of n] have "(n + 1) * (f (n + 1) + f n) = ((n + 1) * f (n + 1)) + f (n + 1) + (- 1) ^ n"
by (simp only: distrib_left of_nat_add of_nat_1)
also have "... = (n + 2) * f (n + 1) + (- 1) ^ (n + 2)"
by (simp del: One_nat_def add_2_eq_Suc' add: algebra_simps) simp
also from f_eq[of "n + 1"] have "... = f (n + 2)" by simp
finally show ?thesis unfolding f_def by simp
qed
finally show ?case by simp
qed (auto)
subsubsection ‹Approximation of Cardinality›
lemma two_power_fact_le_fact:
assumes "n ≥ 1"
shows "2^k * fact n ≤ (fact (n + k) :: 'a :: {semiring_char_0,linordered_semidom})"
proof (induction k)
case (Suc k)
have "2 ^ Suc k * fact n = 2 * (2 ^ k * fact n)" by (simp add: algebra_simps)
also note Suc.IH
also from assms have "of_nat 1 + of_nat 1 ≤ of_nat n + (of_nat (Suc k) :: 'a)"
by (intro add_mono) (unfold of_nat_le_iff, simp_all)
hence "2 * (fact (n + k) :: 'a) ≤ of_nat (n + Suc k) * fact (n + k)"
by (intro mult_right_mono) (simp_all add: add_ac)
also have "… = fact (n + Suc k)" by simp
finally show ?case by - (simp add: mult_left_mono)
qed simp_all
lemma exp1_approx:
assumes "n > 0"
shows "exp (1::real) - (∑k<n. 1 / fact k) ∈ {0..2 / fact n}"
proof (unfold atLeastAtMost_iff, safe)
have "(λk. 1^k / fact k) sums exp (1::real)"
using exp_converges[of "1::real"] by (simp add: field_simps)
from sums_split_initial_segment[OF this, of n]
have sums: "(λk. 1 / fact (n+k)) sums (exp 1 - (∑k<n. 1 / fact k :: real))"
by (simp add: algebra_simps power_add)
from assms show "(exp 1 - (∑k<n. 1 / fact k :: real)) ≥ 0"
by (intro sums_le[OF _ sums_zero sums]) auto
show "(exp 1 - (∑k<n. 1 / fact k :: real)) ≤ 2 / fact n"
proof (rule sums_le)
from assms have "(λk. (1 / fact n) * (1 / 2)^k :: real) sums ((1 / fact n) * (1 / (1 - 1 / 2)))"
by (intro sums_mult geometric_sums) simp_all
also have "… = 2 / fact n" by simp
finally show "(λk. 1 / fact n * (1 / 2) ^ k) sums (2 / fact n :: real)" .
next
fix k show "(1 / fact (n+k)) ≤ (1 / fact n) * (1 / 2 :: real)^k" for k
using two_power_fact_le_fact[of n k] assms by (auto simp: field_simps)
qed fact+
qed
lemma exp1_bounds: "exp 1 ∈ {8 / 3..11 / 4 :: real}"
using exp1_approx[of 4] by (simp add: eval_nat_numeral)
lemma count_derangements_approximation:
assumes "n ≠ 0"
shows "abs(real (count_derangements n) - fact n / exp 1) < 1 / 2"
proof (cases "n ≥ 5")
case False
from assms this have n: "n = 1 ∨ n = 2 ∨ n = 3 ∨ n = 4" by auto
from exp1_bounds have 1: "abs(real (count_derangements 1) - fact 1 / exp 1) < 1 / 2"
by simp
from exp1_bounds have 2: "abs(real (count_derangements 2) - fact 2 / exp 1) < 1 / 2"
by (auto simp add: eval_nat_numeral abs_real_def)
from exp1_bounds have 3: "abs(real (count_derangements 3) - fact 3 / exp 1) < 1 / 2"
by (auto simp add: eval_nat_numeral abs_if field_simps)
from exp1_bounds have 4: "abs(real (count_derangements 4) - fact 4 / exp 1) < 1 / 2"
by (auto simp: abs_if field_simps eval_nat_numeral)
from 1 2 3 4 n show ?thesis by auto
next
case True
from Maclaurin_exp_le[of "- 1" "n + 1"]
obtain t where t: "abs (t :: real) ≤ abs (- 1)"
and exp: "exp (- 1) = (∑m<n + 1. (- 1) ^ m / fact m) + exp t / fact (n + 1) * (- 1) ^ (n + 1)"
by blast
from exp have sum_eq_exp:
"(∑k = 0..n. (- 1) ^ k / fact k) = exp (- 1) - exp t / fact (n + 1) * (- 1) ^ (n + 1)"
by (simp add: atLeast0AtMost ivl_disj_un(2)[symmetric])
have fact_plus1: "fact (n + 1) = (n + 1) * fact n" by simp
have eq: "¦real (count_derangements n) - fact n / exp 1¦ = exp t / (n + 1)"
by (simp del: One_nat_def
add: count_derangements sum_eq_exp exp_minus inverse_eq_divide algebra_simps abs_mult)
(simp add: fact_plus1)
from t have "exp t ≤ exp 1" by simp
also from exp1_bounds have "… < 3" by simp
finally show ?thesis using ‹n ≥ 5› by (simp add: eq)
qed
theorem derangements_formula:
assumes "n ≠ 0" "finite S" "card S = n"
shows "int (card (derangements S)) = round (fact n / exp 1 :: real)"
using count_derangements_approximation[of n] assms
by (intro round_unique' [symmetric]) (auto simp: card_derangements abs_minus_commute)
theorem derangements_formula':
assumes "n ≠ 0" "finite S" "card S = n"
shows "card (derangements S) = nat (round (fact n / exp 1 :: real))"
using derangements_formula[OF assms] by simp
end