@@ -380,32 +380,139 @@ def reverse(b, a):
380
380
381
381
return forward , reverse
382
382
383
+ # Rational arithmetic algorithms: Knuth, TAOCP, Volume 2, 4.5.1.
384
+ #
385
+ # Assume input fractions a and b are normalized.
386
+ #
387
+ # 1) Consider addition/subtraction.
388
+ #
389
+ # Let g = gcd(da, db). Then
390
+ #
391
+ # na nb na*db ± nb*da
392
+ # a ± b == -- ± -- == ------------- ==
393
+ # da db da*db
394
+ #
395
+ # na*(db//g) ± nb*(da//g) t
396
+ # == ----------------------- == -
397
+ # (da*db)//g d
398
+ #
399
+ # Now, if g > 1, we're working with smaller integers.
400
+ #
401
+ # Note, that t, (da//g) and (db//g) are pairwise coprime.
402
+ #
403
+ # Indeed, (da//g) and (db//g) share no common factors (they were
404
+ # removed) and da is coprime with na (since input fractions are
405
+ # normalized), hence (da//g) and na are coprime. By symmetry,
406
+ # (db//g) and nb are coprime too. Then,
407
+ #
408
+ # gcd(t, da//g) == gcd(na*(db//g), da//g) == 1
409
+ # gcd(t, db//g) == gcd(nb*(da//g), db//g) == 1
410
+ #
411
+ # Above allows us optimize reduction of the result to lowest
412
+ # terms. Indeed,
413
+ #
414
+ # g2 = gcd(t, d) == gcd(t, (da//g)*(db//g)*g) == gcd(t, g)
415
+ #
416
+ # t//g2 t//g2
417
+ # a ± b == ----------------------- == ----------------
418
+ # (da//g)*(db//g)*(g//g2) (da//g)*(db//g2)
419
+ #
420
+ # is a normalized fraction. This is useful because the unnormalized
421
+ # denominator d could be much larger than g.
422
+ #
423
+ # We should special-case g == 1 (and g2 == 1), since 60.8% of
424
+ # randomly-chosen integers are coprime:
425
+ # https://en.wikipedia.org/wiki/Coprime_integers#Probability_of_coprimality
426
+ # Note, that g2 == 1 always for fractions, obtained from floats: here
427
+ # g is a power of 2 and the unnormalized numerator t is an odd integer.
428
+ #
429
+ # 2) Consider multiplication
430
+ #
431
+ # Let g1 = gcd(na, db) and g2 = gcd(nb, da), then
432
+ #
433
+ # na*nb na*nb (na//g1)*(nb//g2)
434
+ # a*b == ----- == ----- == -----------------
435
+ # da*db db*da (db//g1)*(da//g2)
436
+ #
437
+ # Note, that after divisions we're multiplying smaller integers.
438
+ #
439
+ # Also, the resulting fraction is normalized, because each of
440
+ # two factors in the numerator is coprime to each of the two factors
441
+ # in the denominator.
442
+ #
443
+ # Indeed, pick (na//g1). It's coprime with (da//g2), because input
444
+ # fractions are normalized. It's also coprime with (db//g1), because
445
+ # common factors are removed by g1 == gcd(na, db).
446
+ #
447
+ # As for addition/subtraction, we should special-case g1 == 1
448
+ # and g2 == 1 for same reason. That happens also for multiplying
449
+ # rationals, obtained from floats.
450
+
383
451
def _add (a , b ):
384
452
"""a + b"""
385
- da , db = a .denominator , b .denominator
386
- return Fraction (a .numerator * db + b .numerator * da ,
387
- da * db )
453
+ na , da = a .numerator , a .denominator
454
+ nb , db = b .numerator , b .denominator
455
+ g = math .gcd (da , db )
456
+ if g == 1 :
457
+ return Fraction (na * db + da * nb , da * db , _normalize = False )
458
+ s = da // g
459
+ t = na * (db // g ) + nb * s
460
+ g2 = math .gcd (t , g )
461
+ if g2 == 1 :
462
+ return Fraction (t , s * db , _normalize = False )
463
+ return Fraction (t // g2 , s * (db // g2 ), _normalize = False )
388
464
389
465
__add__ , __radd__ = _operator_fallbacks (_add , operator .add )
390
466
391
467
def _sub (a , b ):
392
468
"""a - b"""
393
- da , db = a .denominator , b .denominator
394
- return Fraction (a .numerator * db - b .numerator * da ,
395
- da * db )
469
+ na , da = a .numerator , a .denominator
470
+ nb , db = b .numerator , b .denominator
471
+ g = math .gcd (da , db )
472
+ if g == 1 :
473
+ return Fraction (na * db - da * nb , da * db , _normalize = False )
474
+ s = da // g
475
+ t = na * (db // g ) - nb * s
476
+ g2 = math .gcd (t , g )
477
+ if g2 == 1 :
478
+ return Fraction (t , s * db , _normalize = False )
479
+ return Fraction (t // g2 , s * (db // g2 ), _normalize = False )
396
480
397
481
__sub__ , __rsub__ = _operator_fallbacks (_sub , operator .sub )
398
482
399
483
def _mul (a , b ):
400
484
"""a * b"""
401
- return Fraction (a .numerator * b .numerator , a .denominator * b .denominator )
485
+ na , da = a .numerator , a .denominator
486
+ nb , db = b .numerator , b .denominator
487
+ g1 = math .gcd (na , db )
488
+ if g1 > 1 :
489
+ na //= g1
490
+ db //= g1
491
+ g2 = math .gcd (nb , da )
492
+ if g2 > 1 :
493
+ nb //= g2
494
+ da //= g2
495
+ return Fraction (na * nb , db * da , _normalize = False )
402
496
403
497
__mul__ , __rmul__ = _operator_fallbacks (_mul , operator .mul )
404
498
405
499
def _div (a , b ):
406
500
"""a / b"""
407
- return Fraction (a .numerator * b .denominator ,
408
- a .denominator * b .numerator )
501
+ # Same as _mul(), with inversed b.
502
+ na , da = a .numerator , a .denominator
503
+ nb , db = b .numerator , b .denominator
504
+ g1 = math .gcd (na , nb )
505
+ if g1 > 1 :
506
+ na //= g1
507
+ nb //= g1
508
+ g2 = math .gcd (db , da )
509
+ if g2 > 1 :
510
+ da //= g2
511
+ db //= g2
512
+ n , d = na * db , nb * da
513
+ if d < 0 :
514
+ n , d = - n , - d
515
+ return Fraction (n , d , _normalize = False )
409
516
410
517
__truediv__ , __rtruediv__ = _operator_fallbacks (_div , operator .truediv )
411
518
0 commit comments