Skip to content

Commit 4e56e21

Browse files
committed
feat(Data/Nat): define Nat.nthRoot (leanprover-community#28768)
Define `Nat.nthRoot n a` to be the `n`th root of a natural number `a`.
1 parent 4b0359c commit 4e56e21

3 files changed

Lines changed: 205 additions & 0 deletions

File tree

Mathlib.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2019,6 +2019,7 @@ import Mathlib.Analysis.SpecialFunctions.Pow.Continuity
20192019
import Mathlib.Analysis.SpecialFunctions.Pow.Deriv
20202020
import Mathlib.Analysis.SpecialFunctions.Pow.Integral
20212021
import Mathlib.Analysis.SpecialFunctions.Pow.NNReal
2022+
import Mathlib.Analysis.SpecialFunctions.Pow.NthRootLemmas
20222023
import Mathlib.Analysis.SpecialFunctions.Pow.Real
20232024
import Mathlib.Analysis.SpecialFunctions.Sigmoid
20242025
import Mathlib.Analysis.SpecialFunctions.SmoothTransition
@@ -3561,6 +3562,7 @@ import Mathlib.Data.Nat.ModEq
35613562
import Mathlib.Data.Nat.Multiplicity
35623563
import Mathlib.Data.Nat.Notation
35633564
import Mathlib.Data.Nat.Nth
3565+
import Mathlib.Data.Nat.NthRoot.Defs
35643566
import Mathlib.Data.Nat.Order.Lemmas
35653567
import Mathlib.Data.Nat.PSub
35663568
import Mathlib.Data.Nat.Pairing
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/-
2+
Copyright (c) 2025 Concordance Inc. dba Harmonic. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Yury Kudryashov
5+
-/
6+
import Mathlib.Algebra.Order.Floor.Semifield
7+
import Mathlib.Analysis.MeanInequalities
8+
import Mathlib.Data.Nat.NthRoot.Defs
9+
import Mathlib.Tactic.Rify
10+
11+
/-!
12+
# Lemmas about `Nat.nthRoot`
13+
14+
In this file we prove that `Nat.nthRoot n a` is indeed the floor of `ⁿ√a`.
15+
16+
## TODO
17+
18+
Rewrite the proof of `Nat.nthRoot.lt_pow_go_succ_aux` to avoid dependencies on real numbers,
19+
so that we can move this file to `Mathlib/Data/Nat/NthRoot`, then to Batteries.
20+
-/
21+
22+
namespace Nat
23+
24+
variable {m n a b guess fuel : ℕ}
25+
26+
@[simp] theorem nthRoot_zero_left (a : ℕ) : nthRoot 0 a = 1 := rfl
27+
28+
@[simp] theorem nthRoot_one_left : nthRoot 1 = id := rfl
29+
30+
@[simp]
31+
theorem nthRoot_zero_right (h : n ≠ 0) : nthRoot n 0 = 0 := by
32+
rcases n with _|_|_ <;> grind [nthRoot, nthRoot.go]
33+
34+
@[simp]
35+
theorem nthRoot_one_right : nthRoot n 1 = 1 := by
36+
rcases n with _|_|_ <;> simp [nthRoot, nthRoot.go, Nat.add_comm 1]
37+
38+
private theorem nthRoot.pow_go_le (hle : guess ≤ fuel) (n a : ℕ) :
39+
go n a fuel guess ^ (n + 2) ≤ a := by
40+
induction fuel generalizing guess with
41+
| zero =>
42+
obtain rfl : guess = 0 := by grind
43+
simp [go]
44+
| succ fuel ih =>
45+
rw [go]
46+
split_ifs with h
47+
case pos =>
48+
grind
49+
case neg =>
50+
have : guess ≤ a / guess ^ (n + 1) := by
51+
linarith only [Nat.mul_le_of_le_div _ _ _ (not_lt.1 h)]
52+
replace := Nat.mul_le_of_le_div _ _ _ this
53+
grind
54+
55+
/-- `nthRoot n a ^ n ≤ a` unless both `n` and `a` are zeros. -/
56+
@[simp]
57+
theorem pow_nthRoot_le_iff : nthRoot n a ^ n ≤ a ↔ n ≠ 0 ∨ a ≠ 0 := by
58+
rcases n with _|_|_ <;> first | grind | simp [nthRoot, nthRoot.pow_go_le]
59+
60+
alias ⟨_, pow_nthRoot_le⟩ := pow_nthRoot_le_iff
61+
62+
/--
63+
An auxiliary lemma saying that if `b ≠ 0`,
64+
then `(a / b ^ n + n * b) / (n + 1) + 1` is a strict upper estimate on `√[n + 1] a`.
65+
66+
Currently, the proof relies on the weighted AM-GM inequality,
67+
which increases the dependency closure of this file by a lot.
68+
69+
A PR proving this inequality by more elementary means is very welcome.
70+
-/
71+
theorem nthRoot.lt_pow_go_succ_aux (hb : b ≠ 0) :
72+
a < ((a / b ^ n + n * b) / (n + 1) + 1) ^ (n + 1) := by
73+
rcases Nat.eq_zero_or_pos n with rfl | hn; · simp
74+
rw [← Nat.add_mul_div_left a, Nat.div_div_eq_div_mul] <;> try positivity
75+
rify
76+
calc
77+
(a : ℝ) = ((a / b ^ n) ^ (1 / (n + 1) : ℝ) * b ^ (n / (n + 1) : ℝ)) ^ (n + 1) := by
78+
rw [mul_pow, ← Real.rpow_mul_natCast, ← Real.rpow_mul_natCast] <;> try positivity
79+
simp (disch := positivity) [div_mul_cancel₀]
80+
_ ≤ ((1 / (n + 1)) * (a / b ^ n) + (n / (n + 1)) * b) ^ (n + 1) := by
81+
gcongr
82+
apply Real.geom_mean_le_arith_mean2_weighted <;> try positivity
83+
simp [field, add_comm]
84+
_ = ((a + b ^ n * (n * b)) / (b ^ n * (n + 1))) ^ (n + 1) := by
85+
congr 1
86+
field_simp
87+
_ < _ := by
88+
gcongr ?_ ^ _
89+
convert lt_floor_add_one (R := ℝ) _ using 1
90+
norm_cast
91+
rw [Nat.floor_div_natCast, Nat.floor_natCast]
92+
93+
private theorem nthRoot.lt_pow_go_succ (hlt : a < (guess + 1) ^ (n + 2)) :
94+
a < (go n a fuel guess + 1) ^ (n + 2) := by
95+
induction fuel generalizing guess with
96+
| zero => simpa [go]
97+
| succ fuel ih =>
98+
rw [go]
99+
split_ifs with h
100+
case pos =>
101+
rcases eq_or_ne guess 0 with rfl | hguess
102+
· grind
103+
· exact ih <| Nat.nthRoot.lt_pow_go_succ_aux hguess
104+
case neg =>
105+
assumption
106+
107+
theorem lt_pow_nthRoot_add_one (hn : n ≠ 0) (a : ℕ) : a < (nthRoot n a + 1) ^ n := by
108+
match n, hn with
109+
| 1, _ => simp
110+
| n + 2, hn =>
111+
simp only [nthRoot]
112+
apply nthRoot.lt_pow_go_succ
113+
exact a.lt_succ_self.trans_le (Nat.le_self_pow hn _)
114+
115+
@[simp]
116+
theorem le_nthRoot_iff (hn : n ≠ 0) : a ≤ nthRoot n b ↔ a ^ n ≤ b := by
117+
cases le_or_gt a (nthRoot n b) with
118+
| inl hle =>
119+
simp only [hle, true_iff]
120+
refine le_trans ?_ (pow_nthRoot_le (.inl hn))
121+
gcongr
122+
| inr hlt =>
123+
simp only [hlt.not_ge, false_iff, not_le]
124+
refine (lt_pow_nthRoot_add_one hn b).trans_le ?_
125+
gcongr
126+
assumption
127+
128+
@[simp]
129+
theorem nthRoot_lt_iff (hn : n ≠ 0) : nthRoot n a < b ↔ a < b ^ n := by
130+
simp only [← not_le, le_nthRoot_iff hn]
131+
132+
@[simp]
133+
theorem nthRoot_pow (hn : n ≠ 0) (a : ℕ) : nthRoot n (a ^ n) = a := by
134+
refine eq_of_forall_le_iff fun b ↦ ?_
135+
rw [le_nthRoot_iff hn]
136+
exact (Nat.pow_left_strictMono hn).le_iff_le
137+
138+
/-- If `a ^ n ≤ b < (a + 1) ^ n`, then `n` root of `b` equals `a`. -/
139+
theorem nthRoot_eq_of_le_of_lt (h₁ : a ^ n ≤ b) (h₂ : b < (a + 1) ^ n) :
140+
nthRoot n b = a := by
141+
rcases eq_or_ne n 0 with rfl | hn
142+
· grind
143+
simp only [← le_nthRoot_iff hn, ← nthRoot_lt_iff hn] at h₁ h₂
144+
grind
145+
146+
theorem exists_pow_eq_iff' (hn : n ≠ 0) : (∃ x, x ^ n = a) ↔ (nthRoot n a) ^ n = a := by
147+
constructor
148+
· rintro ⟨x, rfl⟩
149+
rw [nthRoot_pow hn]
150+
· grind
151+
152+
theorem exists_pow_eq_iff :
153+
(∃ x, x ^ n = a) ↔ ((n = 0 ∧ a = 1) ∨ (n ≠ 0 ∧ (nthRoot n a) ^ n = a)) := by
154+
rcases eq_or_ne n 0 with rfl | _ <;> grind [exists_pow_eq_iff']
155+
156+
instance instDecidableExistsPowEq : Decidable (∃ x, x ^ n = a) :=
157+
decidable_of_iff' _ exists_pow_eq_iff
158+
159+
end Nat

Mathlib/Data/Nat/NthRoot/Defs.lean

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/-
2+
Copyright (c) 2025 Concordance Inc. dba Harmonic. All rights reserved.
3+
Released under Apache 2.0 license as described in the file LICENSE.
4+
Authors: Yury Kudryashov
5+
-/
6+
import Mathlib.Init
7+
8+
/-!
9+
# Definition of `Nat.nthRoot`
10+
11+
In this file we define `Nat.nthRoot n a` to be the floor of the `n`th root of `a`.
12+
The function is defined in terms of natural numbers with no dependencies outside of prelude.
13+
-/
14+
15+
/-- `Nat.nthRoot n a = ⌊(a : ℝ) ^ (1 / n : ℝ)⌋₊` defined in terms of natural numbers.
16+
17+
We use Newton's method to find a root of $x^n = a$,
18+
so it converges superexponentially fast. -/
19+
def Nat.nthRoot : Nat → Nat → Nat
20+
| 0, _ => 1
21+
| 1, a => a
22+
| n + 2, a =>
23+
go n a a a
24+
where
25+
/-- Auxiliary definition for `Nat.nthRoot`.
26+
27+
Given natural numbers `n`, `a`, `fuel`, `guess`
28+
such that `⌊(a : ℝ) ^ (1 / (n + 2) : ℝ)⌋₊ ≤ guess ≤ fuel`,
29+
returns `⌊(a : ℝ) ^ (1 / (n + 2) : ℝ)⌋₊`.
30+
31+
The auxiliary number `guess` is the current approximation in Newton's method,
32+
tracked in the arguments so that the definition uses a tail recursion
33+
which is unfolded into a loop by the compiler.
34+
35+
The auxiliary number `fuel` is an upper estimate on the number of steps in Newton's method.
36+
Any number `fuel ≥ guess` is guaranteed to work, but smaller numbers may work as well.
37+
If `fuel` is too small, then `Nat.nthRoot.go` returns the result of the `fuel`th step
38+
of Newton's method.
39+
-/
40+
go (n a : Nat)
41+
| 0, guess => guess
42+
| fuel + 1, guess =>
43+
let next := (a / guess^(n + 1) + (n + 1) * guess) / (n + 2)
44+
if next < guess then go n a fuel next else guess

0 commit comments

Comments
 (0)