0
\$\begingroup\$

So I made a Python program to solve some of my A-level binomial questions or just to let me check my answer overall. I need advice on how to make it more compact and simplify it.

This Python code is meant for the fx-cg50 calculator's micropython, where there are a lot of functions that don't work including fractions, some mathematical functions such as math.gcd and math.isclose. So I really require some advice or coding tricks to simplify my program.

Disclaimer: I'm only an A-Level student of 16 years; consider me a beginner. I know eval is insecure, but I'm not planning on uploading this online; it's only for my personal use.

print("Factorial Calculator: ")
term = []

# loops through the terms to print them
def term_list_looper(num):
    for i in range(num):
        print(term[i])

# finds the factorial of a certain number (!)
def factorial(num):
    result = 1
    if type(num) == str:
        num = int(eval(num))
    else:
        pass

    for i in range(1, num+1):
        result *= i
    return result

# find the combination of a nth and r  (nCr)
def combination(nth, rth):
    coefficient = (factorial(nth) / (factorial(rth) * factorial(nth - rth)))
    return coefficient

# finds a certain entry of the pascal's triangle using rows and columns 
def pascal_triangle_c_entry(nth, rth):
    coefficient = (factorial(nth-1)/(factorial(rth-1)*factorial((nth-1)-(rth-1))))
    return int(coefficient)

# finds the working and and coefficent of a term in (a+bx)^n
def binomial_term_coefficient_finder(nth, rth, a, b_coefficient, output):
    diff_nth_rth = nth - rth
    comb = combination(nth, rth)
    a_result = a**diff_nth_rth
    b_result = b_coefficient**rth
    resultant_coefficient = comb * a_result * b_result
    group = str("("+str(nth)+"C"+str(rth)+") ("+str(a)+")^"+str(diff_nth_rth)+" ("+str(b_coefficient)+"x)^"+str(rth))
    if output == 0:
        return group
    elif output == 1:
        print("work: ("+str(nth)+"C"+str(rth)+") ("+str(a)+")^"+str(diff_nth_rth)+" ("+str(b_coefficient)+"x)^"+str(rth))
        print("Coefficient of x^"+str(rth)+"\n:", resultant_coefficient)
    elif output == 2:
        if rth == 0:
            return resultant_coefficient
        else:
            return ("("+str(resultant_coefficient)+")"+'x^'+str(rth))



def first_count_terms(nth, count, a, b_coefficient):
    for r in range(count):
        term.insert(r, binomial_term_coefficient_finder(nth, r, a, b_coefficient, 0))
    print(str(count)+" Terms are:")
    term_list_looper(count)

def first_terms_with_coefficients(nth, count, a, b_coefficient):
    terms = []
    terms = [binomial_term_coefficient_finder(nth, rth, a, b_coefficient, 2) for rth in range(count)]
    print(terms)


def stopper():
    stop_flag = False
    stop_or_continue = ""
    while stop_or_continue != "a" or "b":
        stop_or_continue = input("Stop?: ")
        if stop_or_continue == "a":
            stop_flag = True
            break
        if stop_or_continue == "b":
            stop_flag = False
            break
    if stop_flag:
        raise SystemExit

while True:
    print("Choose a for Pas_Tri entry(C)\nChoose b for term coefficient finder\nChoose c for first nth terms\nChoose d for c but with coeff")
    choice = input(">> ")
    while choice != "a" or choice != "b" or choice != "c" or choice != "d":
        if choice == "a":
            nth = int(input("Enter nth: "))
            rth = int(input("Enter rth: "))
            print(pascal_triangle_c_entry(nth, rth))

        elif choice == "b":
            nth = int(input("Enter nth: "))
            rth = int(input("Enter rth: "))
            a = int(input("Enter a: "))
            b_coefficient = input("Enter b's coeff: ")
            if type(b_coefficient) == str:
                b_coefficient = eval(b_coefficient)
            binomial_term_coefficient_finder(nth, rth, a, b_coefficient, 1)

        elif choice == "c":
            nth = int(input("Enter nth: "))
            count = int(input("Enter first nth term num: "))
            a = int(input("Enter a: "))
            b_coefficient = input("Enter b's coeff: ")
            if type(b_coefficient) == str:
                b_coefficient = eval(b_coefficient)
            first_count_terms(nth, count, a, b_coefficient)

        elif choice == "d":
            nth = int(input("Enter nth: "))
            count = int(input("Enter first nth term num: "))
            a = int(input("Enter a: "))
            b_coefficient = input("Enter b's coeff: ")
            if type(b_coefficient) == str:
                b_coefficient = eval(b_coefficient)
            first_terms_with_coefficients(nth, count, a, b_coefficient)

        stopper()
        print("Choose a for Pas_Tri entry(C)\nChoose b for term coefficient finder\nChoose c for first nth terms\nChoose d for c but with coeff")
        choice = input(">> ")

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

there.

This is my first answer, so If I screw up or some of my suggestion are 'too strong', let me know. I'll try to correct it.

I'm just renaming 'nth' and 'rth' to 'n' and 'r' at some places in my code, please keep that in mind.

One more thing, I can't add a comment yet (not enough rep), so could you tell me what purpose 'eval' servers in your code, I couldn't figure it out.

    if type(num) == str:
    num = int(eval(num))

    if type(b_coefficient) == str:
    b_coefficient = eval(b_coefficient)

Are you consider to take expressions as input? If you are even then you can move 'eval' out of functions (like you did in the second example) to the input, (as soon as you input), so functions and the rest of code is streamlined.

Also, you don't need to check if type(num) == str . In python3, the input is 'str' by default. So, if there's an expression you can evaluate it directly.

Now, coming to the code, I'll try to simplify things I'm able to.


Simple Optimization

Solving 'Combinations' as a factorial needs unnecessary computations when they could be avoided. You are calculating three factorials, then the result. But, it could be simplified. (Considering r <= (n-r) and the fact that nCr = nC(n-r))

enter image description here

So, if you modify you factorial and combination functions, you'll save some computation.

def factorial(n, end_point=1) :
    result = 1
    for i in range(end_point, n+1):
        result *= i
    return result

def combination(n, r) :
    r = min(r, n - r)
    c = factorial(n, n-r+1) // factorial(r)
    return c

And if you didn't know, '//' is integer division in python 3. So, if you do this then you won't need to convert output to and integer.

Don't repeat yourself

You can simplify pascal's triangle by doing this

def pascal_triangle_c_entry(n, r):
    return combination(n-1, r-1)

Streamline Stuff

You can break your 'binomial_term_coeff_finder' function in smaller functions. A start would be to make a new 'group' output formatter funtion. It would be called whenever you called 'binomial_term_coeff_finder' with 'output = 0'.

def output_formatter(nth, rth, a, b_coeff) :
    d = n - r
    return "("+str(nth)+"C"+str(rth)+") ("+str(a)+")^"+str(d)+" ("+str(b_coeff)+"x)^"+str(rth)   
    # You don't need the outermost str

Now you don't need the choice (output) 0 and you can delete that. For choice 1 and 2, you can merge them so the function will only return 'resultant_coefficient' and format the output as desired, because the choice 1 and 2 are doing the same thing. The only time you use choice 1 is in choice 'b' of your main function i.e. 'term coefficient finder'.

b_coefficient = input("Enter b's coeff: ")
if type(b_coefficient) == str:
    b_coefficient = eval(b_coefficient)
binomial_term_coefficient_finder(nth, rth, a, b_coefficient, 1)

So, replace this by -

b_coefficient = input("Enter b's coeff: ")
b_coefficient = eval(b_coefficient)  # Again idk why you'd need that
resultant_coeff = binomial_term_coefficient_finder(nth, rth, a, b_coefficient)
print(output_formatter(nth, rth, a, b_coefficient))
print("Coefficient of x^"+str(rth)+"\n:", resultant_coeff)

Don't complicate

In your 'first_count_terms' function, I don't know why you're using 'insert' method. If I'm missing something, let me know. Also, you don't need to print first 'count' terms, since you're only generating 'count' terms. imo, this would be better -

def first_count_terms(n, count, a, b_coefficient):
    print(count, "Terms are:") # Same as print(str(count)+" Terms are:")
    for r in range(count):
        print(output_formatter(n, r, a, b_coefficient))
# I used output_formatter call here, which is equivalent to binomial_fun call with choice 0

In your following function, ditch the second line. You don't need a predefined list when using list comprehension.

def first_terms_with_coefficients(nth, count, a, b_coefficient):
    terms = []  # This line is unnecessary
    terms = [binomial_term_coefficient_finder(nth, rth, a, b_coefficient, 2) for rth in range(count)]
    # don't forget to remove '2' from the arguments if you consolidated the function earlier.
    print(terms)

Your 'stopper' function is unnecessarily complicated. You can just do,

def stopper():
    choice = input('Stop? (y/n) : ')
    if choice == 'y' :
        raise SystemExit

However, I can't comment on whether you need a stopper function at all, because I don't know how your calculator exits from a code.


The inner while loop in the below code serves no purpose you can just remove it and the last two lines of your code and it'll behave the same.

while True:
    print("Choose a for Pas_Tri entry(C)\nChoose b for term coefficient finder\nChoose c for first nth terms\nChoose d for c but with coeff")
    choice = input(">> ")
    while choice != "a" or choice != "b" or choice != "c" or choice != "d":
        if choice == "a":
            nth = int(input("Enter nth: "))
            rth = int(input("Enter rth: "))
.
.
.
                b_coefficient = eval(b_coefficient)
            first_terms_with_coefficients(nth, count, a, b_coefficient)

        stopper()
        print("Choose a for Pas_Tri entry(C)\nChoose b for term coefficient finder\nChoose c for first nth terms\nChoose d for c but with coeff")
        choice = input(">> ")
\$\endgroup\$
7
  • \$\begingroup\$ idk what micropython is but in desktop python > 3.6 you can use f-strings . These simply formatting your output quite a bit, return "("+str(nth)+"C"+str(rth)+") ("+str(a)+")^"+str(d)+" ("+str(b_coeff)+"x)^"+str(rth) would be return f'({nth}C{rth}) ({a})^{d} ({b_coeff}x)^{rth}' \$\endgroup\$ Commented Feb 28, 2020 at 17:55
  • \$\begingroup\$ im afraid f'strings dont work in micropython, a bummer indeed, and for your question about the eval statements, recall the fact that this program is for a calculator, as a result, the only way to enter fractions is like this "5/7", this will be originally in string, so i have to evaluate it before further processing. \$\endgroup\$
    – Anonymous
    Commented Feb 28, 2020 at 23:40
  • \$\begingroup\$ so i ran into a bug with your program at your factorial and combination function, apparently when i try to calculate the 7th row and 2th entry it gives me a value of 0 \$\endgroup\$
    – Anonymous
    Commented Feb 28, 2020 at 23:51
  • \$\begingroup\$ ahh update: i fixed your bug by removing the max(rth, nth-rth) function and replacing factorial(r) with factorial(rth) as the combination function removes the nth-rth part so only the rth part remains to be divided. \$\endgroup\$
    – Anonymous
    Commented Feb 29, 2020 at 0:07
  • \$\begingroup\$ I couldn't replacate your bug. For me combination (6,1) = 6 and combination (7,2) =21. Idk, how you're getting '0'. There is however a problem with that function, the 'max' function should've been a 'min' funtion so it lessens the calculation. \$\endgroup\$ Commented Feb 29, 2020 at 3:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.