diff --git a/thys/Priority_Queue_Braun/Priority_Queue_Braun.thy b/thys/Priority_Queue_Braun/Priority_Queue_Braun.thy --- a/thys/Priority_Queue_Braun/Priority_Queue_Braun.thy +++ b/thys/Priority_Queue_Braun/Priority_Queue_Braun.thy @@ -1,292 +1,292 @@ section "Priority Queues Based on Braun Trees" theory Priority_Queue_Braun imports "HOL-Library.Tree_Multiset" "HOL-Library.Pattern_Aliases" "HOL-Data_Structures.Priority_Queue_Specs" "HOL-Data_Structures.Braun_Tree" "HOL-Data_Structures.Define_Time_Function" begin subsection "Introduction" text\Braun, Rem and Hoogerwoord \<^cite>\"BraunRem" and "Hoogerwoord"\ used specific balanced binary trees, often called Braun trees (where in each node with subtrees $l$ and $r$, $size(r) \le size(l) \le size(r)+1$), to implement flexible arrays. Paulson \<^cite>\"Paulson"\ (based on code supplied by Okasaki) implemented priority queues via Braun trees. This theory verifies Paulsons's implementation, with small simplifications.\ text \Direct proof of logarithmic height. Also follows from the fact that Braun trees are balanced (proved in the base theory).\ lemma height_size_braun: "braun t \ 2 ^ (height t) \ 2 * size t + 1" proof(induction t) case (Node t1) show ?case proof (cases "height t1") case 0 thus ?thesis using Node by simp next case (Suc n) hence "2 ^ n \ size t1" using Node by simp thus ?thesis using Suc Node by(auto simp: max_def) qed qed simp subsection "Get Minimum" fun get_min :: "'a::linorder tree \ 'a" where "get_min (Node l a r) = a" lemma get_min: "\ heap t; t \ Leaf \ \ get_min t = Min_mset (mset_tree t)" by (auto simp add: eq_Min_iff neq_Leaf_iff) subsection \Insertion\ hide_const (open) insert fun insert :: "'a::linorder \ 'a tree \ 'a tree" where "insert a Leaf = Node Leaf a Leaf" | "insert a (Node l x r) = (if a < x then Node (insert x r) a l else Node (insert a r) x l)" lemma size_insert[simp]: "size(insert x t) = size t + 1" by(induction t arbitrary: x) auto lemma mset_insert: "mset_tree(insert x t) = {#x#} + mset_tree t" by(induction t arbitrary: x) (auto simp: ac_simps) lemma set_insert[simp]: "set_tree(insert x t) = {x} \ (set_tree t)" by(simp add: mset_insert flip: set_mset_tree) lemma braun_insert: "braun t \ braun(insert x t)" by(induction t arbitrary: x) auto lemma heap_insert: "heap t \ heap(insert x t)" by(induction t arbitrary: x) (auto simp add: ball_Un) subsection \Deletion\ text \Slightly simpler definition of @{text del_left} which avoids the need to appeal to the Braun invariant.\ fun del_left :: "'a tree \ 'a * 'a tree" where "del_left (Node Leaf x r) = (x,r)" | "del_left (Node l x r) = (let (y,l') = del_left l in (y,Node r x l'))" lemma del_left_mset_plus: "del_left t = (x,t') \ t \ Leaf \ mset_tree t = {#x#} + mset_tree t'" by (induction t arbitrary: x t' rule: del_left.induct; auto split: prod.splits) lemma del_left_mset: "del_left t = (x,t') \ t \ Leaf \ x \# mset_tree t \ mset_tree t' = mset_tree t - {#x#}" by (simp add: del_left_mset_plus) lemma del_left_set: "del_left t = (x,t') \ t \ Leaf \ set_tree t = {x} \ set_tree t'" by(simp add: del_left_mset_plus flip: set_mset_tree) lemma del_left_heap: "del_left t = (x,t') \ t \ Leaf \ heap t \ heap t'" by (induction t arbitrary: x t' rule: del_left.induct; fastforce split: prod.splits dest: del_left_set[THEN equalityD2]) lemma del_left_size: "del_left t = (x,t') \ t \ Leaf \ size t = size t' + 1" by(induction t arbitrary: x t' rule: del_left.induct; auto split: prod.splits) lemma del_left_braun: "del_left t = (x,t') \ t \ Leaf \ braun t \ braun t'" by(induction t arbitrary: x t' rule: del_left.induct; auto split: prod.splits dest: del_left_size) context includes pattern_aliases begin text \Slightly simpler definition: \_\ instead of @{const Leaf} because of Braun invariant.\ function (sequential) sift_down :: "'a::linorder tree \ 'a \ 'a tree \ 'a tree" where "sift_down Leaf a _ = Node Leaf a Leaf" | "sift_down (Node Leaf x _) a Leaf = (if a \ x then Node (Node Leaf x Leaf) a Leaf else Node (Node Leaf a Leaf) x Leaf)" | "sift_down (Node l1 x1 r1 =: t1) a (Node l2 x2 r2 =: t2) = (if a \ x1 \ a \ x2 then Node t1 a t2 else if x1 \ x2 then Node (sift_down l1 a r1) x1 t2 else Node t1 x2 (sift_down l2 a r2))" by pat_completeness auto termination by (relation "measure (%(l,a,r). max(height l) (height r))") (auto simp: max_def) (* An alternative: by (relation "measure (%(l,a,r). size l + size r)") auto *) end lemma size_sift_down: "braun(Node l a r) \ size(sift_down l a r) = size l + size r + 1" by(induction l a r rule: sift_down.induct) (auto simp: Let_def) lemma braun_sift_down: "braun(Node l a r) \ braun(sift_down l a r)" by(induction l a r rule: sift_down.induct) (auto simp: size_sift_down Let_def) lemma mset_sift_down: "braun(Node l a r) \ mset_tree(sift_down l a r) = {#a#} + (mset_tree l + mset_tree r)" by(induction l a r rule: sift_down.induct) (auto simp: ac_simps Let_def) lemma set_sift_down: "braun(Node l a r) \ set_tree(sift_down l a r) = {a} \ (set_tree l \ set_tree r)" by(drule arg_cong[where f=set_mset, OF mset_sift_down]) (simp) lemma heap_sift_down: "braun(Node l a r) \ heap l \ heap r \ heap(sift_down l a r)" by (induction l a r rule: sift_down.induct) (auto simp: set_sift_down ball_Un Let_def) fun del_min :: "'a::linorder tree \ 'a tree" where "del_min Leaf = Leaf" | "del_min (Node Leaf x r) = Leaf" | "del_min (Node l x r) = (let (y,l') = del_left l in sift_down r y l')" lemma braun_del_min: "braun t \ braun(del_min t)" apply(cases t rule: del_min.cases) apply simp apply simp apply (fastforce split: prod.split intro!: braun_sift_down dest: del_left_size del_left_braun) done lemma heap_del_min: "heap t \ braun t \ heap(del_min t)" apply(cases t rule: del_min.cases) apply simp apply simp apply (fastforce split: prod.split intro!: heap_sift_down dest: del_left_size del_left_braun del_left_heap) done lemma size_del_min: assumes "braun t" shows "size(del_min t) = size t - 1" proof(cases t rule: del_min.cases) case [simp]: (3 ll b lr a r) { fix y l' assume "del_left (Node ll b lr) = (y,l')" hence "size(sift_down r y l') = size t - 1" using assms by(subst size_sift_down) (auto dest: del_left_size del_left_braun) } thus ?thesis by(auto split: prod.split) qed (insert assms, auto) lemma mset_del_min: assumes "braun t" "t \ Leaf" shows "mset_tree(del_min t) = mset_tree t - {#get_min t#}" proof(cases t rule: del_min.cases) case 1 with assms show ?thesis by simp next case 2 with assms show ?thesis by (simp) next case [simp]: (3 ll b lr a r) have "mset_tree(sift_down r y l') = mset_tree t - {#a#}" if del: "del_left (Node ll b lr) = (y,l')" for y l' using assms del_left_mset[OF del] del_left_size[OF del] del_left_braun[OF del] del_left_mset_plus[OF del] apply (subst mset_sift_down) apply (auto simp: ac_simps del_left_mset_plus[OF del]) done thus ?thesis by(auto split: prod.split) qed text \Last step: prove all axioms of the priority queue specification:\ interpretation braun: Priority_Queue where empty = Leaf and is_empty = "\h. h = Leaf" and insert = insert and del_min = del_min and get_min = get_min and invar = "\h. braun h \ heap h" and mset = mset_tree proof(standard, goal_cases) case 1 show ?case by simp next case 2 show ?case by simp next case 3 show ?case by(simp add: mset_insert) next case 4 thus ?case by(simp add: mset_del_min) next case 5 thus ?case using get_min mset_tree.simps(1) by blast next case 6 thus ?case by(simp) next case 7 thus ?case by(simp add: heap_insert braun_insert) next case 8 thus ?case by(simp add: heap_del_min braun_del_min) qed subsection "Running Time Analysis" -define_time_fun insert +time_fun insert lemma T_insert: "T_insert a t \ height t + 1" apply(induction t arbitrary: a) by (auto simp: max_def not_less_eq_eq intro: order.trans le_SucI) -define_time_fun del_left +time_fun del_left lemma T_del_left_height: "t \ Leaf \ T_del_left t \ height t" by(induction t rule: T_del_left.induct)auto -define_time_function sift_down +time_function sift_down termination apply (relation "measure (%(l,a,r). max(height l) (height r))") apply (auto simp: max_def) done lemma T_sift_down_height: "braun(Node l a r) \ T_sift_down l x r \ max(height l) (height r) + 1" apply(induction l x r rule: T_sift_down.induct) apply(auto) done -define_time_fun del_min +time_fun del_min lemma del_left_height: "\ del_left t = (x, t'); t \ \\ \ \ height t' \ height t" by(induction t arbitrary: x t' rule: del_left.induct) (auto split: prod.splits) lemma T_del_min_neq_Leaf: "l \ Leaf \ T_del_min (Node l x r) = T_del_left l + (let (y,l') = del_left l in T_sift_down r y l')" by (auto simp add: neq_Leaf_iff) lemma T_del_min: assumes "braun t" shows "T_del_min t \ 2*height t" proof(cases t) case Leaf then show ?thesis by simp next case [simp]: (Node l x r) show ?thesis proof (cases) assume "l = Leaf" then show ?thesis by simp next assume "l \ Leaf" obtain y l' where [simp]: "del_left l = (y,l')" by fastforce have 1: "height l' \ height l" by (simp add: \l \ \\\ del_left_height) have "braun \r, y, l'\" using del_left_braun[of l y l'] \l \ \\\ assms del_left_size[of l] by auto have "T_del_min t = T_del_left l + T_sift_down r y l'" using \l \ Leaf\ by(simp add: T_del_min_neq_Leaf) also have "\ \ height l + T_sift_down r y l'" using T_del_left_height[OF \l \ Leaf\] by linarith also have "\ \ height l + max(height r) (height l') + 1" using T_sift_down_height[OF \braun \r, y, l'\\, of y] by linarith also have "\ \ height l + max(height r) (height l) + 1" using 1 by linarith also have "\ \ 2 * max(height r) (height l) + 1" by simp also have "\ \ 2 * height t" by simp finally show ?thesis . qed qed end diff --git a/thys/Stirling_Formula/Stirling_Formula.thy b/thys/Stirling_Formula/Stirling_Formula.thy --- a/thys/Stirling_Formula/Stirling_Formula.thy +++ b/thys/Stirling_Formula/Stirling_Formula.thy @@ -1,733 +1,774 @@ (* File: Stirling_Formula.thy Author: Manuel Eberl A proof of Stirling's formula, i.e. an asymptotic approximation of the Gamma function and the factorial. *) section \Stirling's Formula\ theory Stirling_Formula imports "HOL-Analysis.Analysis" "Landau_Symbols.Landau_More" begin context begin text \ First, we define the $S_n^*$ from Jameson's article: \ -private definition S' :: "nat \ real \ real" where +qualified definition S' :: "nat \ real \ real" where "S' n x = 1/(2*x) + (\r=1.. Next, the trapezium (also called $T$ in Jameson's article): \ -private definition T :: "real \ real" where +qualified definition T :: "real \ real" where "T x = 1/(2*x) + 1/(2*(x+1))" text \ Now we define The difference $\Delta(x)$: \ -private definition D :: "real \ real" where +qualified definition D :: "real \ real" where "D x = T x - ln (x + 1) + ln x" -private lemma S'_telescope_trapezium: +qualified lemma S'_telescope_trapezium: assumes "n > 0" shows "S' n x = (\rrrrrrrr=0..r=0..r=Suc 0.. = (\r=1.. + ?a + ?b = S' n x" by (simp add: S'_def Suc) finally show ?thesis .. qed (insert assms, simp_all) -private lemma stirling_trapezium: +qualified lemma stirling_trapezium: assumes x: "(x::real) > 0" shows "D x \ {0 .. 1/(12*x^2) - 1/(12 * (x+1)^2)}" proof - define y where "y = 1 / (2*x + 1)" from x have y: "y > 0" "y < 1" by (simp_all add: divide_simps y_def) from x have "D x = T x - ln ((x + 1) / x)" by (subst ln_div) (simp_all add: D_def) also from x have "(x + 1) / x = 1 + 1 / x" by (simp add: field_simps) finally have D: "D x = T x - ln (1 + 1/x)" . from y have "(\n. y * y^n) sums (y * (1 / (1 - y)))" by (intro geometric_sums sums_mult) simp_all hence "(\n. y ^ Suc n) sums (y / (1 - y))" by simp also from x have "y / (1 - y) = 1 / (2*x)" by (simp add: y_def divide_simps) finally have *: "(\n. y ^ Suc n) sums (1 / (2*x))" . from y have "(\n. (-y) * (-y)^n) sums ((-y) * (1 / (1 - (-y))))" by (intro geometric_sums sums_mult) simp_all hence "(\n. (-y) ^ Suc n) sums (-(y / (1 + y)))" by simp also from x have "y / (1 + y) = 1 / (2*(x+1))" by (simp add: y_def divide_simps) finally have **: "(\n. (-y) ^ Suc n) sums (-(1 / (2*(x+1))))" . from sums_diff[OF * **] have sum1: "(\n. y ^ Suc n - (-y) ^ Suc n) sums T x" by (simp add: T_def) from y have "abs y < 1" "abs (-y) < 1" by simp_all from sums_diff[OF this[THEN ln_series']] have "(\n. y ^ n / real n - (-y) ^ n / real n) sums (ln (1 + y) - ln (1 - y))" by simp also from y have "ln (1 + y) - ln (1 - y) = ln ((1 + y) / (1 - y))" by (simp add: ln_div) also from x have "(1 + y) / (1 - y) = 1 + 1/x" by (simp add: divide_simps y_def) finally have "(\n. y^n / real n - (-y)^n / real n) sums ln (1 + 1/x)" . hence sum2: "(\n. y^Suc n / real (Suc n) - (-y)^Suc n / real (Suc n)) sums ln (1 + 1/x)" by (subst sums_Suc_iff) simp have "ln (1 + 1/x) \ T x" proof (rule sums_le [OF _ sum2 sum1]) fix n :: nat show "y ^ Suc n / real (Suc n) - (-y) ^ Suc n / real (Suc n) \ y^Suc n - (-y) ^ Suc n" proof (cases "even n") case True hence eq: "A - (-y) ^ Suc n / B = A + y ^ Suc n / B" "A - (-y) ^ Suc n = A + y ^ Suc n" for A B by simp_all from y show ?thesis unfolding eq by (intro add_mono) (auto simp: divide_simps) qed simp_all qed hence "D x \ 0" by (simp add: D) define c where "c = (\n. if even n then 2 * (1 - 1 / real (Suc n)) else 0)" note sums_diff[OF sum1 sum2] also have "(\n. y ^ Suc n - (-y) ^ Suc n - (y ^ Suc n / real (Suc n) - (-y) ^ Suc n / real (Suc n))) = (\n. c n * y ^ Suc n)" by (intro ext) (simp add: c_def algebra_simps) finally have sum3: "(\n. c n * y ^ Suc n) sums D x" by (simp add: D) from y have "(\n. y^2 * (of_nat (Suc n) * y^n)) sums (y^2 * (1 / (1 - y)^2))" by (intro sums_mult geometric_deriv_sums) simp_all hence "(\n. of_nat (Suc n) * y^(n+2)) sums (y^2 / (1 - y)^2)" by (simp add: algebra_simps power2_eq_square) also from x have "y^2 / (1 - y)^2 = 1 / (4*x^2)" by (simp add: y_def divide_simps) finally have *: "(\n. real (Suc n) * y ^ (Suc (Suc n))) sums (1 / (4 * x\<^sup>2))" by simp from y have "(\n. y^2 * (of_nat (Suc n) * (-y)^n)) sums (y^2 * (1 / (1 - -y)^2))" by (intro sums_mult geometric_deriv_sums) simp_all hence "(\n. of_nat (Suc n) * (-y)^(n+2)) sums (y^2 / (1 + y)^2)" by (simp add: algebra_simps power2_eq_square) also from x have "y^2 / (1 + y)^2 = 1 / (2^2*(x+1)^2)" unfolding power_mult_distrib [symmetric] by (simp add: y_def divide_simps add_ac) finally have **: "(\n. real (Suc n) * (- y) ^ (Suc (Suc n))) sums (1 / (4 * (x + 1)\<^sup>2))" by simp define d where "d = (\n. if even n then 2 * real n else 0)" note sums_diff[OF * **] also have "(\n. real (Suc n) * y^(Suc (Suc n)) - real (Suc n) * (-y)^(Suc (Suc n))) = (\n. d (Suc n) * y ^ Suc (Suc n))" by (intro ext) (simp_all add: d_def) finally have "(\n. d n * y ^ Suc n) sums (1 / (4 * x\<^sup>2) - 1 / (4 * (x + 1)\<^sup>2))" by (subst (asm) sums_Suc_iff) (simp add: d_def) from sums_mult[OF this, of "1/3"] x have sum4: "(\n. d n / 3 * y ^ Suc n) sums (1 / (12 * x^2) - 1 / (12 * (x + 1)^2))" by (simp add: field_simps) have "D x \ (1 / (12 * x^2) - 1 / (12 * (x + 1)^2))" proof (intro sums_le [OF _ sum3 sum4] allI) fix n :: nat define c' :: "nat \ real" where "c' = (\n. if odd n \ n = 0 then 0 else if n = 2 then 4/3 else 2)" show "c n * y ^ Suc n \ d n / 3 * y ^ Suc n" proof (intro mult_right_mono) have "c n \ c' n" by (simp add: c_def c'_def) also consider "n = 0" | "n = 1" | "n = 2" | "n \ 3" by force hence "c' n \ d n / 3" by cases (simp_all add: c'_def d_def) finally show "c n \ d n / 3" . qed (insert y, simp) qed with \D x \ 0\ show ?thesis by simp qed text \ The following functions correspond to the $p_n(x)$ from the article. The special case $n = 0$ would not, strictly speaking, be necessary, but it allows some theorems to work even without the precondition $n \neq 0$: \ -private definition p :: "nat \ real \ real" where +qualified definition p :: "nat \ real \ real" where "p n x = (if n = 0 then 1/x else (\r We can write the Digamma function in terms of @{term S'}: \ -private lemma S'_LIMSEQ_Digamma: +qualified lemma S'_LIMSEQ_Digamma: assumes x: "x \ 0" shows "(\n. ln (real n) - S' n x - 1/(2*x)) \ Digamma x" proof - define c where "c = (\n. ln (real n) - (\rn. 1 / (2 * (x + real n)) = c n - (ln (real n) - S' n x - 1/(2*x))) at_top" using eventually_gt_at_top[of "0::nat"] proof eventually_elim fix n :: nat assume n: "n > 0" have "c n - (ln (real n) - S' n x - 1/(2*x)) = -(\rr=1..r=1..rn. 1 / (2 * (x + real n))) \ 0" by (rule real_tendsto_divide_at_top tendsto_const filterlim_tendsto_pos_mult_at_top filterlim_tendsto_add_at_top filterlim_real_sequentially | simp)+ ultimately have "(\n. c n - (ln (real n) - S' n x - 1/(2*x))) \ 0" by (blast intro: Lim_transform_eventually) from tendsto_minus[OF this] have "(\n. (ln (real n) - S' n x - 1/(2*x)) - c n) \ 0" by simp moreover from Digamma_LIMSEQ[OF x] have "c \ Digamma x" by (simp add: c_def) ultimately show "(\n. ln (real n) - S' n x - 1/(2*x)) \ Digamma x" by (rule Lim_transform [rotated]) qed text \ Moreover, we can give an expansion of @{term S'} with the @{term p} as variation terms. \ -private lemma S'_approx: +qualified lemma S'_approx: "S' n x = ln (real n + x) - ln x + p n x" proof (cases "n = 0") case True thus ?thesis by (simp add: p_def S'_def) next case False hence "S' n x = (\r = (\r = (\rr We define the limit of the @{term p} (simply called $p(x)$ in Jameson's article): \ -private definition P :: "real \ real" where +qualified definition P :: "real \ real" where "P x = (\n. D (real n + x))" -private lemma D_summable: +qualified lemma D_summable: assumes x: "x > 0" shows "summable (\n. D (real n + x))" proof - have *: "summable (\n. 1 / (12 * (x + real n)\<^sup>2) - 1 / (12 * (x + real (Suc n))\<^sup>2))" by (rule telescope_summable' real_tendsto_divide_at_top tendsto_const filterlim_tendsto_pos_mult_at_top filterlim_pow_at_top filterlim_tendsto_add_at_top filterlim_real_sequentially | simp)+ show "summable (\n. D (real n + x))" proof (rule summable_comparison_test[OF _ *], rule exI[of _ 2], safe) fix n :: nat assume "n \ 2" show "norm (D (real n + x)) \ 1 / (12 * (x + real n)^2) - 1 / (12 * (x + real (Suc n))^2)" using stirling_trapezium[of "real n + x"] x by (auto simp: algebra_simps) qed qed -private lemma p_LIMSEQ: +qualified lemma p_LIMSEQ: assumes x: "x > 0" shows "(\n. p n x) \ P x" proof (rule Lim_transform_eventually) from D_summable[OF x] have "(\n. D (real n + x)) sums P x" unfolding P_def by (simp add: sums_iff) then show "(\n. \r P x" by (simp add: sums_def) moreover from eventually_gt_at_top[of 1] show "eventually (\n. (\r This gives us an expansion of the Digamma function: \ lemma Digamma_approx: assumes x: "(x :: real) > 0" shows "Digamma x = ln x - 1 / (2 * x) - P x" proof - have "eventually (\n. ln (inverse (1 + x / real n)) + ln x - 1/(2*x) - p n x = ln (real n) - S' n x - 1/(2*x)) at_top" using eventually_gt_at_top[of "1::nat"] proof eventually_elim fix n :: nat assume n: "n > 1" have "ln (real n) - S' n x = ln ((real n) / (real n + x)) + ln x - p n x" using assms n unfolding S'_approx by (subst ln_div) (auto simp: algebra_simps) also from n have "real n / (real n + x) = inverse (1 + x / real n)" by (simp add: field_simps) finally show "ln (inverse (1 + x / real n)) + ln x - 1/(2*x) - p n x = ln (real n) - S' n x - 1/(2*x)" by simp qed moreover have "(\n. ln (inverse (1 + x / real n)) + ln x - 1/(2*x) - p n x) \ ln (inverse (1 + 0)) + ln x - 1/(2*x) - P x" by (rule tendsto_intros p_LIMSEQ x real_tendsto_divide_at_top filterlim_real_sequentially | simp)+ hence "(\n. ln (inverse (1 + x / real n)) + ln x - 1/(2*x) - p n x) \ ln x - 1/(2*x) - P x" by simp ultimately have "(\n. ln (real n) - S' n x - 1 / (2 * x)) \ ln x - 1/(2*x) - P x" by (blast intro: Lim_transform_eventually) moreover from x have "(\n. ln (real n) - S' n x - 1 / (2 * x)) \ Digamma x" by (intro S'_LIMSEQ_Digamma) simp_all ultimately show "Digamma x = ln x - 1 / (2 * x) - P x" by (rule LIMSEQ_unique [rotated]) qed - + text \ Next, we derive some bounds on @{term "P"}: \ -private lemma p_ge_0: "x > 0 \ p n x \ 0" +qualified lemma p_ge_0: "x > 0 \ p n x \ 0" using stirling_trapezium[of "real n + x" for n] by (auto simp add: p_def intro!: sum_nonneg) -private lemma P_ge_0: "x > 0 \ P x \ 0" +qualified lemma P_ge_0: "x > 0 \ P x \ 0" by (rule tendsto_lowerbound[OF p_LIMSEQ]) (insert p_ge_0[of x], simp_all) -private lemma p_upper_bound: +qualified lemma p_upper_bound: assumes "x > 0" "n > 0" shows "p n x \ 1/(12*x^2)" proof - from assms have "p n x = (\r \ (\r = 1 / (12 * x\<^sup>2) - 1 / (12 * (real n + x)\<^sup>2)" by (subst sum_lessThan_telescope') simp also have "\ \ 1 / (12 * x^2)" by simp finally show ?thesis . qed -private lemma P_upper_bound: +qualified lemma P_upper_bound: assumes "x > 0" shows "P x \ 1/(12*x^2)" proof (rule tendsto_upperbound) show "eventually (\n. p n x \ 1 / (12 * x^2)) at_top" using eventually_gt_at_top[of 0] by eventually_elim (use p_upper_bound[of x] assms in auto) show "(\n. p n x) \ P x" by (simp add: assms p_LIMSEQ) qed auto text \ We can now use this approximation of the Digamma function to approximate @{term ln_Gamma} (since the former is the derivative of the latter). We therefore define the function $g$ from Jameson's article, which measures the difference between @{term ln_Gamma} and its approximation: \ -private definition g :: "real \ real" where +qualified definition g :: "real \ real" where "g x = ln_Gamma x - (x - 1/2) * ln x + x" -private lemma DERIV_g: "x > 0 \ (g has_field_derivative -P x) (at x)" +qualified lemma DERIV_g: "x > 0 \ (g has_field_derivative -P x) (at x)" unfolding g_def [abs_def] using Digamma_approx[of x] by (auto intro!: derivative_eq_intros simp: field_simps) -private lemma isCont_P: +qualified lemma isCont_P: assumes "x > 0" shows "isCont P x" proof - define D' :: "real \ real" where "D' = (\x. - 1 / (2 * x^2 * (x+1)^2))" have DERIV_D: "(D has_field_derivative D' x) (at x)" if "x > 0" for x unfolding D_def [abs_def] D'_def T_def by (insert that, (rule derivative_eq_intros refl | simp)+) (simp add: power2_eq_square divide_simps, (simp add: algebra_simps)?) note this [THEN DERIV_chain2, derivative_intros] have "(P has_field_derivative (\n. D' (real n + x))) (at x)" unfolding P_def [abs_def] proof (rule has_field_derivative_series') show "convex {x/2<..}" by simp next fix n :: nat and y :: real assume y: "y \ {x/2<..}" with assms have "y > 0" by simp thus "((\a. D (real n + a)) has_real_derivative D' (real n + y)) (at y within {x/2<..})" by (auto intro!: derivative_eq_intros) next from assms D_summable[of x] show "summable (\n. D (real n + x))" by simp next show "uniformly_convergent_on {x/2<..} (\n x. \i {x/2<..}" with assms have "y > 0" by auto have "norm (D' (real n + y)) = (1 / (2 * (y + real n)^2)) * (1 / (y + real (Suc n))^2)" by (simp add: D'_def add_ac) also from y assms have "\ \ (1 / (2 * (x/2)^2)) * (1 / (real (Suc n))^2)" by (intro mult_mono divide_left_mono power_mono) simp_all also have "1 / (real (Suc n))^2 = inverse ((real (Suc n))^2)" by (simp add: field_simps) finally show "norm (D' (real n + y)) \ (1 / (2 * (x/2)^2)) * inverse ((real (Suc n))^2)" . next show "summable (\n. (1 / (2 * (x/2)^2)) * inverse ((real (Suc n))^2))" by (subst summable_Suc_iff, intro summable_mult inverse_power_summable) simp_all qed qed (insert assms, simp_all add: interior_open) thus ?thesis by (rule DERIV_isCont) qed -private lemma P_continuous_on [THEN continuous_on_subset]: "continuous_on {0<..} P" +qualified lemma P_continuous_on [THEN continuous_on_subset]: "continuous_on {0<..} P" by (intro continuous_at_imp_continuous_on ballI isCont_P) auto -private lemma P_integrable: +qualified lemma P_integrable: assumes a: "a > 0" shows "P integrable_on {a..}" proof - define f where "f = (\n x. if x \ {a..real n} then P x else 0)" show "P integrable_on {a..}" proof (rule dominated_convergence) fix n :: nat from a have "P integrable_on {a..real n}" by (intro integrable_continuous_real P_continuous_on) auto hence "f n integrable_on {a..real n}" by (rule integrable_eq) (simp add: f_def) thus "f n integrable_on {a..}" by (rule integrable_on_superset) (auto simp: f_def) next fix n :: nat show "norm (f n x) \ of_real (1/12) * (1 / x^2)" if "x \ {a..}" for x using a P_ge_0 P_upper_bound by (auto simp: f_def) next show "(\x::real. of_real (1/12) * (1 / x^2)) integrable_on {a..}" using has_integral_inverse_power_to_inf[of 2 a] a by (intro integrable_on_cmult_left) auto next show "(\n. f n x) \ P x" if "x\{a..}" for x proof - have "eventually (\n. real n \ x) at_top" using filterlim_real_sequentially by (simp add: filterlim_at_top) with that have "eventually (\n. f n x = P x) at_top" by (auto elim!: eventually_mono simp: f_def) thus "(\n. f n x) \ P x" by (simp add: tendsto_eventually) qed qed qed -private definition c :: real where "c = integral {1..} (\x. -P x) + g 1" +qualified definition c :: real where "c = integral {1..} (\x. -P x) + g 1" text \ We can now give bounds on @{term g}: \ -private lemma g_bounds: +qualified lemma g_bounds: assumes x: "x \ 1" shows "g x \ {c..c + 1/(12*x)}" proof - from assms have int_nonneg: "integral {x..} P \ 0" by (intro Henstock_Kurzweil_Integration.integral_nonneg P_integrable) (auto simp: P_ge_0) have int_upper_bound: "integral {x..} P \ 1/(12*x)" proof (rule has_integral_le) from x show "(P has_integral integral {x..} P) {x..}" by (intro integrable_integral P_integrable) simp_all from x has_integral_mult_right[OF has_integral_inverse_power_to_inf[of 2 x], of "1/12"] show "((\x. 1/(12*x^2)) has_integral (1/(12*x))) {x..}" by (simp add: field_simps) qed (insert P_upper_bound x, simp_all) note DERIV_g [THEN DERIV_chain2, derivative_intros] from assms have int1: "((\x. -P x) has_integral (g x - g 1)) {1..x}" by (intro fundamental_theorem_of_calculus) (auto simp: has_real_derivative_iff_has_vector_derivative [symmetric] intro!: derivative_eq_intros) from x have int2: "((\x. -P x) has_integral integral {x..} (\x. -P x)) {x..}" by (intro integrable_integral integrable_neg P_integrable) simp_all from has_integral_Un[OF int1 int2] x have "((\x. - P x) has_integral g x - g 1 - integral {x..} P) ({1..x} \ {x..})" by (simp add: max_def) also from x have "{1..x} \ {x..} = {1..}" by auto finally have "((\x. -P x) has_integral g x - g 1 - integral {x..} P) {1..}" . moreover have "((\x. -P x) has_integral integral {1..} (\x. -P x)) {1..}" by (intro integrable_integral integrable_neg P_integrable) simp_all ultimately have "g x - g 1 - integral {x..} P = integral {1..} (\x. -P x)" by (simp add: has_integral_unique) hence "g x = c + integral {x..} P" by (simp add: c_def algebra_simps) with int_upper_bound int_nonneg show "g x \ {c..c + 1/(12*x)}" by simp qed text \ Finally, we have bounds on @{term ln_Gamma}, @{term Gamma}, and @{term fact}. \ -private lemma ln_Gamma_bounds_aux: +qualified lemma ln_Gamma_bounds_aux: "x \ 1 \ ln_Gamma x \ c + (x - 1/2) * ln x - x" "x \ 1 \ ln_Gamma x \ c + (x - 1/2) * ln x - x + 1/(12*x)" using g_bounds[of x] by (simp_all add: g_def) -private lemma Gamma_bounds_aux: +qualified lemma Gamma_bounds_aux: assumes x: "x \ 1" shows "Gamma x \ exp c * x powr (x - 1/2) / exp x" "Gamma x \ exp c * x powr (x - 1/2) / exp x * exp (1/(12*x))" proof - have "exp (ln_Gamma x) \ exp (c + (x - 1/2) * ln x - x)" by (subst exp_le_cancel_iff, rule ln_Gamma_bounds_aux) (simp add: x) with x show "Gamma x \ exp c * x powr (x - 1/2) / exp x" by (simp add: Gamma_real_pos_exp exp_add exp_diff powr_def del: exp_le_cancel_iff) next have "exp (ln_Gamma x) \ exp (c + (x - 1/2) * ln x - x + 1/(12*x))" by (subst exp_le_cancel_iff, rule ln_Gamma_bounds_aux) (simp add: x) with x show "Gamma x \ exp c * x powr (x - 1/2) / exp x * exp (1/(12*x))" by (simp add: Gamma_real_pos_exp exp_add exp_diff powr_def del: exp_le_cancel_iff) qed -private lemma Gamma_asymp_equiv_aux: +qualified lemma Gamma_asymp_equiv_aux: "Gamma \[at_top] (\x. exp c * x powr (x - 1/2) / exp x)" proof (rule asymp_equiv_sandwich) include asymp_equiv_notation show "eventually (\x. exp c * x powr (x - 1/2) / exp x \ Gamma x) at_top" "eventually (\x. exp c * x powr (x - 1/2) / exp x * exp (1/(12*x)) \ Gamma x) at_top" using eventually_ge_at_top[of "1::real"] by (eventually_elim; use Gamma_bounds_aux in force)+ have "((\x::real. exp (1 / (12 * x))) \ exp 0) at_top" by (rule tendsto_intros real_tendsto_divide_at_top filterlim_tendsto_pos_mult_at_top)+ (simp_all add: filterlim_ident) hence "(\x. exp (1 / (12 * x))) \ (\x. 1 :: real)" by (intro asymp_equivI') simp_all hence "(\x. exp c * x powr (x - 1 / 2) / exp x * 1) \ (\x. exp c * x powr (x - 1 / 2) / exp x * exp (1 / (12 * x)))" by (intro asymp_equiv_mult asymp_equiv_refl) (simp add: asymp_equiv_sym) thus "(\x. exp c * x powr (x - 1 / 2) / exp x) \ (\x. exp c * x powr (x - 1 / 2) / exp x * exp (1 / (12 * x)))" by simp qed simp_all -private lemma exp_1_powr_real [simp]: "exp (1::real) powr x = exp x" +qualified lemma exp_1_powr_real [simp]: "exp (1::real) powr x = exp x" by (simp add: powr_def) -private lemma fact_asymp_equiv_aux: +qualified lemma fact_asymp_equiv_aux: "fact \[at_top] (\x. exp c * sqrt (real x) * (real x / exp 1) powr real x)" proof - include asymp_equiv_notation have "fact \ (\n. Gamma (real (Suc n)))" by (simp add: Gamma_fact) also have "eventually (\n. Gamma (real (Suc n)) = real n * Gamma (real n)) at_top" using eventually_gt_at_top[of "0::nat"] by eventually_elim (insert Gamma_plus1[of "real n" for n], auto simp: add_ac of_nat_in_nonpos_Ints_iff) also have "(\n. Gamma (real n)) \ (\n. exp c * real n powr (real n - 1/2) / exp (real n))" by (rule asymp_equiv_compose'[OF Gamma_asymp_equiv_aux] filterlim_real_sequentially)+ also have "eventually (\n. real n * (exp c * real n powr (real n - 1 / 2) / exp (real n)) = exp c * sqrt (real n) * (real n / exp 1) powr real n) at_top" using eventually_gt_at_top[of "0::nat"] proof eventually_elim fix n :: nat assume n: "n > 0" thus "real n * (exp c * real n powr (real n - 1 / 2) / exp (real n)) = exp c * sqrt (real n) * (real n / exp 1) powr real n" by (subst powr_diff) (simp_all add: powr_divide powr_half_sqrt field_simps) qed finally show ?thesis by - (simp_all add: asymp_equiv_mult) qed text \ + We cal also bound @{term Digamma} above and below. +\ + +lemma Digamma_plus_1_gt_ln: + assumes x: "x > (0 :: real)" + shows "Digamma (x + 1) > ln x" +proof - + have "0 < (17 :: real)" + by simp + also have "17 \ 12 * x ^ 2 + 28 * x + 17" + using x by auto + finally have "0 < (12 * x ^ 2 + 28 * x + 17) / (12 * (x + 1) ^ 2 * (1 + 2 * x))" + using x by (intro divide_pos_pos mult_pos_pos zero_less_power) auto + also have "\ = 2 / (2 * x + 1) - 1 / (2 * (x + 1)) - 1 / (12 * (x + 1) ^ 2)" + using x by (simp add: divide_simps) (auto simp: field_simps power2_eq_square add_eq_0_iff) + also have "2 / (2 * x + 1) \ ln (x + 1) - ln x" + using ln_inverse_approx_ge[of x "x + 1"] x by simp + also have "\ - 1 / (2 * (x + 1)) - 1 / (12 * (x + 1) ^ 2) \ + ln (x + 1) - ln x - 1 / (2 * (x + 1)) - P (x + 1)" + using P_upper_bound[of "x + 1"] x by (intro diff_mono) auto + also have "\ = Digamma (x + 1) - ln x" + by (subst Digamma_approx) (use x in auto) + finally show "Digamma (x + 1) > ln x" + by simp +qed + +lemma Digamma_less_ln: + assumes x: "x > (0 :: real)" + shows "Digamma x < ln x" +proof - + have "Digamma x - ln x = - (1 / (2 * x)) - P x" + by (subst Digamma_approx) (use x in auto) + also have "\ < 0 - P x" + using x by (intro diff_strict_right_mono) auto + also have "\ \ 0" + using P_ge_0[of x] x by simp + finally show "Digamma x < ln x" + by simp +qed + +text \ We still need to determine the constant term @{term c}, which we do using Wallis' product formula: $$\prod_{n=1}^\infty \frac{4n^2}{4n^2-1} = \frac{\pi}{2}$$ \ -private lemma powr_mult_2: "(x::real) > 0 \ x powr (y * 2) = (x^2) powr y" +qualified lemma powr_mult_2: "(x::real) > 0 \ x powr (y * 2) = (x^2) powr y" by (subst mult.commute, subst powr_powr [symmetric]) (simp add: powr_numeral) -private lemma exp_mult_2: "exp (y * 2 :: real) = exp y * exp y" +qualified lemma exp_mult_2: "exp (y * 2 :: real) = exp y * exp y" by (subst exp_add [symmetric]) simp -private lemma exp_c: "exp c = sqrt (2*pi)" +qualified lemma exp_c: "exp c = sqrt (2*pi)" proof - include asymp_equiv_notation define p where "p = (\n. \k=1..n. (4*real k^2) / (4*real k^2 - 1))" have p_0 [simp]: "p 0 = 1" by (simp add: p_def) have p_Suc: "p (Suc n) = p n * (4 * real (Suc n)^2) / (4 * real (Suc n)^2 - 1)" for n unfolding p_def by (subst prod.nat_ivl_Suc') simp_all have p: "p = (\n. 16 ^ n * fact n ^ 4 / (fact (2 * n))\<^sup>2 / (2 * real n + 1))" proof fix n :: nat have "p n = (\k=1..n. (2*real k)^2 / (2*real k - 1) / (2 * real k + 1))" unfolding p_def by (intro prod.cong refl) (simp add: field_simps power2_eq_square) also have "\ = (\k=1..n. (2*real k)^2 / (2*real k - 1)) / (\k=1..n. (2 * real (Suc k) - 1))" by (simp add: prod_dividef prod.distrib add_ac) also have "(\k=1..n. (2 * real (Suc k) - 1)) = (\k=Suc 1..Suc n. (2 * real k - 1))" by (subst prod.atLeast_Suc_atMost_Suc_shift) simp_all also have "\ = (\k=1..n. (2 * real k - 1)) * (2 * real n + 1)" by (induction n) (simp_all add: prod.nat_ivl_Suc') also have "(\k = 1..n. (2 * real k)\<^sup>2 / (2 * real k - 1)) / \ = (\k = 1..n. (2 * real k)^2 / (2 * real k - 1)^2) / (2 * real n + 1)" unfolding power2_eq_square by (simp add: prod.distrib prod_dividef) also have "(\k = 1..n. (2 * real k)^2 / (2 * real k - 1)^2) = (\k = 1..n. (2 * real k)^4 / ((2*real k)*(2 * real k - 1))^2)" by (rule prod.cong) (simp_all add: power2_eq_square eval_nat_numeral) also have "\ = 16^n * fact n^4 / (\k=1..n. (2*real k) * (2*real k - 1))^2" by (simp add: prod.distrib prod_dividef fact_prod prod_power_distrib [symmetric] prod_constant) also have "(\k=1..n. (2*real k) * (2*real k - 1)) = fact (2*n)" by (induction n) (simp_all add: algebra_simps prod.nat_ivl_Suc') finally show "p n = 16 ^ n * fact n ^ 4 / (fact (2 * n))\<^sup>2 / (2 * real n + 1)" . qed have "p \ (\n. 16 ^ n * fact n ^ 4 / (fact (2 * n))\<^sup>2 / (2 * real n + 1))" by (simp add: p) also have "\ \ (\n. 16^n * (exp c * sqrt (real n) * (real n / exp 1) powr real n)^4 / (exp c * sqrt (real (2*n)) * (real (2*n) / exp 1) powr real (2*n))^2 / (2 * real n + 1))" (is "_ \ ?f") by (intro asymp_equiv_mult asymp_equiv_divide asymp_equiv_refl mult_nat_left_at_top fact_asymp_equiv_aux asymp_equiv_power asymp_equiv_compose'[OF fact_asymp_equiv_aux]) simp_all also have "eventually (\n. \ n = exp c ^ 2 / (4 + 2/n)) at_top" using eventually_gt_at_top[of "0::nat"] proof eventually_elim fix n :: nat assume n: "n > 0" have [simp]: "16^n = 4^n * (4^n :: real)" by (simp add: power_mult_distrib [symmetric]) from n have "?f n = exp c ^ 2 * (n / (2*(2*n+1)))" by (simp add: power_mult_distrib divide_simps powr_mult real_sqrt_power_even) (simp add: field_simps power2_eq_square eval_nat_numeral powr_mult_2 exp_mult_2 powr_realpow) also from n have "\ = exp c ^ 2 / (4 + 2/n)" by (simp add: field_simps) finally show "?f n = \" . qed also have "(\x. 4 + 2 / real x) \ (\x. 4)" by (subst asymp_equiv_add_right) auto finally have "p \ exp c ^ 2 / 4" by (rule asymp_equivD_const) (simp_all add: asymp_equiv_divide) moreover have "p \ pi / 2" unfolding p_def by (rule wallis) ultimately have "exp c ^ 2 / 4 = pi / 2" by (rule LIMSEQ_unique) hence "2 * pi = exp c ^ 2" by simp also have "sqrt (exp c ^ 2) = exp c" by simp finally show "exp c = sqrt (2 * pi)" .. qed -private lemma c: "c = ln (2*pi) / 2" +qualified lemma c: "c = ln (2*pi) / 2" proof - note exp_c [symmetric] also have "ln (exp c) = c" by simp finally show ?thesis by (simp add: ln_sqrt) qed text \ This gives us the final bounds: \ theorem Gamma_bounds: assumes "x \ 1" shows "Gamma x \ sqrt (2*pi/x) * (x / exp 1) powr x" (is ?th1) "Gamma x \ sqrt (2*pi/x) * (x / exp 1) powr x * exp (1 / (12 * x))" (is ?th2) proof - from assms have "exp c * x powr (x - 1/2) / exp x = sqrt (2*pi/x) * (x / exp 1) powr x" by (subst powr_diff) (simp add: exp_c real_sqrt_divide powr_divide powr_half_sqrt field_simps) with Gamma_bounds_aux[OF assms] show ?th1 ?th2 by simp_all qed theorem ln_Gamma_bounds: assumes "x \ 1" shows "ln_Gamma x \ ln (2*pi/x) / 2 + x * ln x - x" (is ?th1) "ln_Gamma x \ ln (2*pi/x) / 2 + x * ln x - x + 1/(12*x)" (is ?th2) proof - from ln_Gamma_bounds_aux[OF assms] assms show ?th1 ?th2 by (simp_all add: c field_simps ln_div) from assms have "exp c * x powr (x - 1/2) / exp x = sqrt (2*pi/x) * (x / exp 1) powr x" by (subst powr_diff) (simp add: exp_c real_sqrt_divide powr_divide powr_half_sqrt field_simps) qed theorem fact_bounds: assumes "n > 0" shows "(fact n :: real) \ sqrt (2*pi*n) * (n / exp 1) ^ n" (is ?th1) "(fact n :: real) \ sqrt (2*pi*n) * (n / exp 1) ^ n * exp (1 / (12 * n))" (is ?th2) proof - from assms have n: "real n \ 1" by simp from assms Gamma_plus1[of "real n"] have "real n * Gamma (real n) = Gamma (real (Suc n))" by (simp add: of_nat_in_nonpos_Ints_iff add_ac) also have "Gamma (real (Suc n)) = fact n" by (subst Gamma_fact [symmetric]) simp finally have *: "fact n = real n * Gamma (real n)" by simp have "2*pi/n = 2*pi*n / n^2" by (simp add: power2_eq_square) also have "sqrt \ = sqrt (2*pi*n) / n" by (subst real_sqrt_divide) simp_all also have "real n * \ = sqrt (2*pi*n)" by simp finally have **: "real n * sqrt (2*pi/real n) = sqrt (2*pi*real n)" . note * also note Gamma_bounds(2)[OF n] also have "real n * (sqrt (2 * pi / real n) * (real n / exp 1) powr real n * exp (1 / (12 * real n))) = (real n * sqrt (2*pi/n)) * (n / exp 1) powr n * exp (1 / (12 * n))" by (simp add: algebra_simps) also from n have "(real n / exp 1) powr real n = (real n / exp 1) ^ n" by (subst powr_realpow) simp_all also note ** finally show ?th2 by - (insert assms, simp_all) have "sqrt (2*pi*n) * (n / exp 1) powr n = n * (sqrt (2*pi/n) * (n / exp 1) powr n)" by (subst ** [symmetric]) (simp add: field_simps) also from assms have "\ \ real n * Gamma (real n)" by (intro mult_left_mono Gamma_bounds(1)) simp_all also from n have "(real n / exp 1) powr real n = (real n / exp 1) ^ n" by (subst powr_realpow) simp_all also note * [symmetric] finally show ?th1 . qed theorem ln_fact_bounds: assumes "n > 0" shows "ln (fact n :: real) \ ln (2*pi*n)/2 + n * ln n - n" (is ?th1) "ln (fact n :: real) \ ln (2*pi*n)/2 + n * ln n - n + 1/(12*real n)" (is ?th2) proof - have "ln (fact n :: real) \ ln (sqrt (2*pi*real n)*(real n/exp 1)^n)" using fact_bounds(1)[OF assms] assms by (subst ln_le_cancel_iff) auto also from assms have "ln (sqrt (2*pi*real n)*(real n/exp 1)^n) = ln (2*pi*n)/2 + n * ln n - n" by (simp add: ln_mult ln_div ln_realpow ln_sqrt field_simps) finally show ?th1 . next have "ln (fact n :: real) \ ln (sqrt (2*pi*real n) * (real n/exp 1)^n * exp (1/(12*real n)))" using fact_bounds(2)[OF assms] assms by (subst ln_le_cancel_iff) auto also from assms have "\ = ln (2*pi*n)/2 + n * ln n - n + 1/(12*real n)" by (simp add: ln_mult ln_div ln_realpow ln_sqrt field_simps) finally show ?th2 . qed theorem Gamma_asymp_equiv: "Gamma \[at_top] (\x. sqrt (2*pi/x) * (x / exp 1) powr x :: real)" proof - note Gamma_asymp_equiv_aux also have "eventually (\x. exp c * x powr (x - 1/2) / exp x = sqrt (2*pi/x) * (x / exp 1) powr x) at_top" using eventually_gt_at_top[of "0::real"] proof eventually_elim fix x :: real assume x: "x > 0" thus "exp c * x powr (x - 1/2) / exp x = sqrt (2*pi/x) * (x / exp 1) powr x" by (subst powr_diff) (simp add: exp_c powr_half_sqrt powr_divide field_simps real_sqrt_divide) qed finally show ?thesis . qed theorem fact_asymp_equiv: "fact \[at_top] (\n. sqrt (2*pi*n) * (n / exp 1) ^ n :: real)" proof - note fact_asymp_equiv_aux also have "eventually (\n. exp c * sqrt (real n) = sqrt (2 * pi * real n)) at_top" using eventually_gt_at_top[of "0::nat"] by eventually_elim (simp add: exp_c real_sqrt_mult) also have "eventually (\n. (n / exp 1) powr n = (n / exp 1) ^ n) at_top" using eventually_gt_at_top[of "0::nat"] by eventually_elim (simp add: powr_realpow) finally show ?thesis . qed corollary stirling_tendsto_sqrt_pi: "(\n. fact n / (sqrt (2 * n) * (n / exp 1) ^ n)) \ sqrt pi" proof - have *: "(\n. fact n / (sqrt (2 * pi * n) * (n / exp 1) ^ n)) \ 1" using fact_asymp_equiv by (simp add: asymp_equiv_def) have "(\n. sqrt pi * fact n / (sqrt (2 * pi * real n) * (real n / exp 1) ^ n)) = (\n. fact n / (sqrt (real (2 * n)) * (real n / exp 1) ^ n))" by (force simp add: divide_simps powr_realpow real_sqrt_mult) with tendsto_mult_left[OF *, of "sqrt pi"] show ?thesis by simp qed end end