How to simplify a fraction efficiently?

Question:

I’m writing my own floating point number class, which saves numbers as a fraction of two numbers.
I’ve made 3 different functions to simplify the fraction, but they take all too much time.
The class has two attributes, up and down, to represent the upper and lower number of the fraction.

preems = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229]
class fractionnum:
    def __init__(self,up,down):
        self.up=up
        self.down=down

    def simplify11(self):
        thmax = max(self.up, self.down) // 2
        for x in preems:
            if x>thmax or 1 in (self.up,self.down):
                break
            while self.up%x==self.down%x==0:
                self.up//=x
                self.down//=x


    def simplify2(self):
        def isdivbyup(preem):
            return not self.up%preem
        def isdivbydown(preem):
            return not self.down%preem
        def divtime(preem):
            #down=self.down
            while  not self.up % preem and not self.down %preem:
                self.up //= preem
                self.down//=preem

        downfacts=list(filter(isdivbydown,preems))
        upfacts = list(filter(isdivbyup, preems))
        genfacts=[_ for _ in downfacts if _ in upfacts]
        [divtime(x) for x in genfacts]
    def simplify3(self):
        up=self.up
        down=self.down
        while down!=0: #euclidian algorithm
            prevup=up
            up=down
            down=prevup%down
        self.up//=up
        self.down//=up

Currently, these three functions take almost the same average time to execute: 0.034130303 seconds.
The function is called after every calculation made. A sine function with 20 iterations takes 1974 execution times.

How can I make the function run more efficiently?

Asked By: The_spider

||

Answers:

As it is mentioned in the comments, rational numbers are already implemented by various Python libraries (fractions, sympy etc.). I assume though that you want to do it on your own as an exercise. In such case, your methods simplify11() and simplify2() are not good, since they rely on a precomputed list of prime numbers and will give wrong results for fractions where denominator and numerator have a prime factor which is not among these primes. On top of it, simplify2() uses a list comprehension [divtime(x) for x in genfacts] solely for its side effects, which is both inefficient and a very bad practice.

simplify3() is better, but it does not implement the Euclidean algorithm correctly. It should be something like this:

def simplify3(self):
    if self.up == 0:
        self.down = 1
    else:
        u, d = abs(self.up), abs(self.down)
        if u < d:
            d, u = u, d
        while d != 0:
            u, d = d, u % d
        self.up //= u
        self.down //= u

This assumes that your class makes sure that self.down is not equal to 0.

It is of course simpler to use the gcd() function from the math module:

from math import gcd

def simplify3(up, down):
    common = gcd(self.up, self.down)
    self.up //= common
    self.down //= common
Answered By: bb1
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.