More on Fibonacci numbers
authoreberlm <eberlm@in.tum.de>
Thu, 20 Oct 2016 13:53:36 +0200
changeset 64317 029e6247210e
parent 64316 96fef7745c68
child 64318 1e92b5c35615
child 64324 416f4d031afd
More on Fibonacci numbers
src/HOL/Archimedean_Field.thy
src/HOL/Number_Theory/Fib.thy
src/HOL/Number_Theory/document/root.tex
--- a/src/HOL/Archimedean_Field.thy	Thu Oct 20 11:05:16 2016 +0200
+++ b/src/HOL/Archimedean_Field.thy	Thu Oct 20 13:53:36 2016 +0200
@@ -736,6 +736,9 @@
     by simp
 qed
 
+lemma round_unique': "\<bar>x - of_int n\<bar> < 1/2 \<Longrightarrow> round x = n"
+  by (subst (asm) abs_less_iff, rule round_unique) (simp_all add: field_simps)
+
 lemma round_altdef: "round x = (if frac x \<ge> 1/2 then \<lceil>x\<rceil> else \<lfloor>x\<rfloor>)"
   by (cases "frac x \<ge> 1/2")
     (rule round_unique, ((simp add: frac_def field_simps ceiling_altdef; linarith)+)[2])+
--- a/src/HOL/Number_Theory/Fib.thy	Thu Oct 20 11:05:16 2016 +0200
+++ b/src/HOL/Number_Theory/Fib.thy	Thu Oct 20 13:53:36 2016 +0200
@@ -1,12 +1,13 @@
 (*  Title:      HOL/Number_Theory/Fib.thy
     Author:     Lawrence C. Paulson
     Author:     Jeremy Avigad
+    Author:     Manuel Eberl
 *)
 
 section \<open>The fibonacci function\<close>
 
 theory Fib
-imports Main GCD Binomial
+  imports Complex_Main
 begin
 
 
@@ -38,6 +39,34 @@
   by (induct n rule: fib.induct) (auto simp add: )
 
 
+subsection \<open>More efficient code\<close>
+
+text \<open>
+  The naive approach is very inefficient since the branching recursion leads to many
+  values of @{term fib} being computed multiple times. We can avoid this by ``remembering''
+  the last two values in the sequence, yielding a tail-recursive version.
+  This is far from optimal (it takes roughly $O(n\cdot M(n))$ time where $M(n)$ is the 
+  time required to multiply two $n$-bit integers), but much better than the naive version,
+  which is exponential.
+\<close>
+
+fun gen_fib :: "nat \<Rightarrow> nat \<Rightarrow> nat \<Rightarrow> nat" where
+  "gen_fib a b 0 = a"
+| "gen_fib a b (Suc 0) = b"
+| "gen_fib a b (Suc (Suc n)) = gen_fib b (a + b) (Suc n)"
+
+lemma gen_fib_recurrence: "gen_fib a b (Suc (Suc n)) = gen_fib a b n + gen_fib a b (Suc n)"
+  by (induction a b n rule: gen_fib.induct) simp_all
+  
+lemma gen_fib_fib: "gen_fib (fib n) (fib (Suc n)) m = fib (n + m)"
+  by (induction m rule: fib.induct) (simp_all del: gen_fib.simps(3) add: gen_fib_recurrence)
+
+lemma fib_conv_gen_fib: "fib n = gen_fib 0 1 n"
+  using gen_fib_fib[of 0 n] by simp
+
+declare fib_conv_gen_fib [code]
+
+
 subsection \<open>A Few Elementary Results\<close>
 
 text \<open>
@@ -104,6 +133,114 @@
   by (induct n rule: nat.induct) (auto simp add:  field_simps)
 
 
+subsection \<open>Closed form\<close>
+
+lemma fib_closed_form:
+  defines "\<phi> \<equiv> (1 + sqrt 5) / (2::real)" and "\<psi> \<equiv> (1 - sqrt 5) / (2::real)"
+  shows   "of_nat (fib n) = (\<phi> ^ n - \<psi> ^ n) / sqrt 5"
+proof (induction n rule: fib.induct)
+  fix n :: nat
+  assume IH1: "of_nat (fib n) = (\<phi> ^ n - \<psi> ^ n) / sqrt 5"
+  assume IH2: "of_nat (fib (Suc n)) = (\<phi> ^ Suc n - \<psi> ^ Suc n) / sqrt 5"
+  have "of_nat (fib (Suc (Suc n))) = of_nat (fib (Suc n)) + of_nat (fib n)" by simp
+  also have "... = (\<phi>^n*(\<phi> + 1) - \<psi>^n*(\<psi> + 1)) / sqrt 5"
+    by (simp add: IH1 IH2 field_simps)
+  also have "\<phi> + 1 = \<phi>\<^sup>2" by (simp add: \<phi>_def field_simps power2_eq_square)
+  also have "\<psi> + 1 = \<psi>\<^sup>2" by (simp add: \<psi>_def field_simps power2_eq_square)
+  also have "\<phi>^n * \<phi>\<^sup>2 - \<psi>^n * \<psi>\<^sup>2 = \<phi> ^ Suc (Suc n) - \<psi> ^ Suc (Suc n)"  
+    by (simp add: power2_eq_square)
+  finally show "of_nat (fib (Suc (Suc n))) = (\<phi> ^ Suc (Suc n) - \<psi> ^ Suc (Suc n)) / sqrt 5" .
+qed (simp_all add: \<phi>_def \<psi>_def field_simps)
+
+lemma fib_closed_form':
+  defines "\<phi> \<equiv> (1 + sqrt 5) / (2 :: real)" and "\<psi> \<equiv> (1 - sqrt 5) / (2 :: real)"
+  assumes "n > 0"
+  shows   "fib n = round (\<phi> ^ n / sqrt 5)"
+proof (rule sym, rule round_unique')
+  have "\<bar>\<phi> ^ n / sqrt 5 - of_int (int (fib n))\<bar> = \<bar>\<psi>\<bar> ^ n / sqrt 5"
+    by (simp add: fib_closed_form[folded \<phi>_def \<psi>_def] field_simps power_abs)
+  also {
+    from assms have "\<bar>\<psi>\<bar>^n \<le> \<bar>\<psi>\<bar>^1"
+      by (intro power_decreasing) (simp_all add: algebra_simps real_le_lsqrt)
+    also have "... < sqrt 5 / 2" by (simp add: \<psi>_def field_simps)
+    finally have "\<bar>\<psi>\<bar>^n / sqrt 5 < 1/2" by (simp add: field_simps)
+  }
+  finally show "\<bar>\<phi> ^ n / sqrt 5 - of_int (int (fib n))\<bar> < 1/2" .
+qed
+
+lemma fib_asymptotics:
+  defines "\<phi> \<equiv> (1 + sqrt 5) / (2 :: real)"
+  shows   "(\<lambda>n. real (fib n) / (\<phi> ^ n / sqrt 5)) \<longlonglongrightarrow> 1"
+proof -
+  define \<psi> where "\<psi> \<equiv> (1 - sqrt 5) / (2 :: real)"
+  have "\<phi> > 1" by (simp add: \<phi>_def)
+  hence A: "\<phi> \<noteq> 0" by auto
+  have "(\<lambda>n. (\<psi> / \<phi>) ^ n) \<longlonglongrightarrow> 0"
+    by (rule LIMSEQ_power_zero) (simp_all add: \<phi>_def \<psi>_def field_simps add_pos_pos)
+  hence "(\<lambda>n. 1 - (\<psi> / \<phi>) ^ n) \<longlonglongrightarrow> 1 - 0" by (intro tendsto_diff tendsto_const)
+  with A show ?thesis
+    by (simp add: divide_simps fib_closed_form [folded \<phi>_def \<psi>_def])
+qed
+
+
+subsection \<open>Divide-and-Conquer recurrence\<close>
+
+text \<open>
+  The following divide-and-conquer recurrence allows for a more efficient computation 
+  of Fibonacci numbers; however, it requires memoisation of values to be reasonably 
+  efficient, cutting the number of values to be computed to logarithmically many instead of
+  linearly many. The vast majority of the computation time is then actually spent on the 
+  multiplication, since the output number is exponential in the input number.
+\<close>
+
+lemma fib_rec_odd:
+  defines "\<phi> \<equiv> (1 + sqrt 5) / (2 :: real)" and "\<psi> \<equiv> (1 - sqrt 5) / (2 :: real)"
+  shows   "fib (Suc (2*n)) = fib n^2 + fib (Suc n)^2"
+proof -
+  have "of_nat (fib n^2 + fib (Suc n)^2) = ((\<phi> ^ n - \<psi> ^ n)\<^sup>2 + (\<phi> * \<phi> ^ n - \<psi> * \<psi> ^ n)\<^sup>2)/5"
+    by (simp add: fib_closed_form[folded \<phi>_def \<psi>_def] field_simps power2_eq_square)
+  also have "(\<phi> ^ n - \<psi> ^ n)\<^sup>2 + (\<phi> * \<phi> ^ n - \<psi> * \<psi> ^ n)\<^sup>2 = 
+    \<phi>^(2*n) + \<psi>^(2*n) - 2*(\<phi>*\<psi>)^n + \<phi>^(2*n+2) + \<psi>^(2*n+2) - 2*(\<phi>*\<psi>)^(n+1)" (is "_ = ?A")
+      by (simp add: power2_eq_square algebra_simps power_mult power_mult_distrib)
+  also have "\<phi> * \<psi> = -1" by (simp add: \<phi>_def \<psi>_def field_simps)
+  hence "?A = \<phi>^(2*n+1) * (\<phi> + inverse \<phi>) + \<psi>^(2*n+1) * (\<psi> + inverse \<psi>)" 
+    by (auto simp: field_simps power2_eq_square)
+  also have "1 + sqrt 5 > 0" by (auto intro: add_pos_pos)
+  hence "\<phi> + inverse \<phi> = sqrt 5" by (simp add: \<phi>_def field_simps)
+  also have "\<psi> + inverse \<psi> = -sqrt 5" by (simp add: \<psi>_def field_simps)
+  also have "(\<phi> ^ (2*n+1) * sqrt 5 + \<psi> ^ (2*n+1)* - sqrt 5) / 5 =
+               (\<phi> ^ (2*n+1) - \<psi> ^ (2*n+1)) * (sqrt 5 / 5)" by (simp add: field_simps)
+  also have "sqrt 5 / 5 = inverse (sqrt 5)" by (simp add: field_simps)
+  also have "(\<phi> ^ (2*n+1) - \<psi> ^ (2*n+1)) * ... = of_nat (fib (Suc (2*n)))"
+    by (simp add: fib_closed_form[folded \<phi>_def \<psi>_def] divide_inverse)
+  finally show ?thesis by (simp only: of_nat_eq_iff)
+qed
+
+lemma fib_rec_even: "fib (2*n) = (fib (n - 1) + fib (n + 1)) * fib n"
+proof (induction n)
+  case (Suc n)
+  let ?rfib = "\<lambda>x. real (fib x)"
+  have "2 * (Suc n) = Suc (Suc (2*n))" by simp
+  also have "real (fib ...) = ?rfib n^2 + ?rfib (Suc n)^2 + (?rfib (n - 1) + ?rfib (n + 1)) * ?rfib n" 
+    by (simp add: fib_rec_odd Suc)
+  also have "(?rfib (n - 1) + ?rfib (n + 1)) * ?rfib n = (2 * ?rfib (n + 1) - ?rfib n) * ?rfib n"
+    by (cases n) simp_all
+  also have "?rfib n^2 + ?rfib (Suc n)^2 + ... = (?rfib (Suc n) + 2 * ?rfib n) * ?rfib (Suc n)"
+    by (simp add: algebra_simps power2_eq_square)
+  also have "... = real ((fib (Suc n - 1) + fib (Suc n + 1)) * fib (Suc n))" by simp
+  finally show ?case by (simp only: of_nat_eq_iff)
+qed simp
+
+lemma fib_rec_even': "fib (2*n) = (2*fib (n - 1) + fib n) * fib n"
+  by (subst fib_rec_even, cases n) simp_all
+
+lemma fib_rec:
+  "fib n = (if n = 0 then 0 else if n = 1 then 1 else
+            if even n then let n' = n div 2; fn = fib n' in (2 * fib (n' - 1) + fn) * fn
+            else let n' = n div 2 in fib n' ^ 2 + fib (Suc n') ^ 2)"
+  by (auto elim: evenE oddE simp: fib_rec_odd fib_rec_even' Let_def)
+
+
 subsection \<open>Fibonacci and Binomial Coefficients\<close>
 
 lemma sum_drop_zero: "(\<Sum>k = 0..Suc n. if 0<k then (f (k - 1)) else 0) = (\<Sum>j = 0..n. f j)"
--- a/src/HOL/Number_Theory/document/root.tex	Thu Oct 20 11:05:16 2016 +0200
+++ b/src/HOL/Number_Theory/document/root.tex	Thu Oct 20 13:53:36 2016 +0200
@@ -1,6 +1,8 @@
 \documentclass[11pt,a4paper]{article}
 \usepackage{graphicx}
 \usepackage{isabelle,isabellesym}
+\usepackage{amssymb}
+\usepackage{amsmath}
 \usepackage{pdfsetup}
 
 \urlstyle{rm}