From f69321c69ba16f49da45bd78ea4fd0b4b8a9df27 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sat, 11 Jun 2022 17:35:14 +0100 Subject: [PATCH 1/4] Minor optimization for Fractions.limit_denominator When we construct the upper and lower candidates in limit_denominator, the numerator and denominator are already relatively prime (and the denominator positive) by construction, so there's no need to go through the usual normalisation in the constructor. This saves a couple of potentially expensive gcd calls. Suggested by Michael Scott Asato Cuthbert in GH-93477. --- Lib/fractions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index f9ac882ec002fa..7b533bf98cbd9b 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -247,8 +247,8 @@ def limit_denominator(self, max_denominator=1000000): n, d = d, n-a*d k = (max_denominator-q0)//q1 - bound1 = Fraction(p0+k*p1, q0+k*q1) - bound2 = Fraction(p1, q1) + bound1 = Fraction(p0+k*p1, q0+k*q1, _normalize=False) + bound2 = Fraction(p1, q1, _normalize=False) if abs(bound2 - self) <= abs(bound1-self): return bound2 else: From 9ac09a1c7d6e8774a4b0c0233fb638f3c4afd11c Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 12 Jun 2022 08:05:47 +0100 Subject: [PATCH 2/4] More efficient comparison of candidates --- Lib/fractions.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index 7b533bf98cbd9b..cd4de4a87d678f 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -245,14 +245,17 @@ def limit_denominator(self, max_denominator=1000000): break p0, q0, p1, q1 = p1, q1, p0+a*p1, q2 n, d = d, n-a*d - k = (max_denominator-q0)//q1 - bound1 = Fraction(p0+k*p1, q0+k*q1, _normalize=False) - bound2 = Fraction(p1, q1, _normalize=False) - if abs(bound2 - self) <= abs(bound1-self): - return bound2 + + # Determine which of the candidate fractions (p0+k*p1)/(q0+k*q1) and + # p1/q1 is closer to self. The distance between the two candidates is + # 1/(q1*(q0+k*q1)), while the distance from p1/q1 to self is + # 1/(q1*(q0+n/d*q1)). So we need to compare q0+k*q1 with 2*(q0+n/d*q1). + # That translates to the following comparison in integers. + if (q0+2*k*q1)*d <= q1*n: + return Fraction(p1, q1, _normalize=False) else: - return bound1 + return Fraction(p0+k*p1, q0+k*q1, _normalize=False) @property def numerator(a): From be8c3327ef1988b731381e75446c58d6dfabab2c Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 12 Jun 2022 08:12:12 +0100 Subject: [PATCH 3/4] Fix typo in comment --- Lib/fractions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index cd4de4a87d678f..379a55c68ef919 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -250,7 +250,7 @@ def limit_denominator(self, max_denominator=1000000): # Determine which of the candidate fractions (p0+k*p1)/(q0+k*q1) and # p1/q1 is closer to self. The distance between the two candidates is # 1/(q1*(q0+k*q1)), while the distance from p1/q1 to self is - # 1/(q1*(q0+n/d*q1)). So we need to compare q0+k*q1 with 2*(q0+n/d*q1). + # 1/(q1*(q0+n/d*q1)). So we need to compare 2*(q0+k*q1) with q0+n/d*q1. # That translates to the following comparison in integers. if (q0+2*k*q1)*d <= q1*n: return Fraction(p1, q1, _normalize=False) From 6f70f586783b7c3ff6c23af1ac32f791332ebd00 Mon Sep 17 00:00:00 2001 From: Mark Dickinson Date: Sun, 12 Jun 2022 11:19:04 +0100 Subject: [PATCH 4/4] Tweak to remove an extra operation --- Lib/fractions.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Lib/fractions.py b/Lib/fractions.py index 379a55c68ef919..738a0d4c301d43 100644 --- a/Lib/fractions.py +++ b/Lib/fractions.py @@ -247,12 +247,11 @@ def limit_denominator(self, max_denominator=1000000): n, d = d, n-a*d k = (max_denominator-q0)//q1 - # Determine which of the candidate fractions (p0+k*p1)/(q0+k*q1) and - # p1/q1 is closer to self. The distance between the two candidates is - # 1/(q1*(q0+k*q1)), while the distance from p1/q1 to self is - # 1/(q1*(q0+n/d*q1)). So we need to compare 2*(q0+k*q1) with q0+n/d*q1. - # That translates to the following comparison in integers. - if (q0+2*k*q1)*d <= q1*n: + # Determine which of the candidates (p0+k*p1)/(q0+k*q1) and p1/q1 is + # closer to self. The distance between them is 1/(q1*(q0+k*q1)), while + # the distance from p1/q1 to self is d/(q1*self._denominator). So we + # need to compare 2*(q0+k*q1) with self._denominator/d. + if 2*d*(q0+k*q1) <= self._denominator: return Fraction(p1, q1, _normalize=False) else: return Fraction(p0+k*p1, q0+k*q1, _normalize=False)