3
\$\begingroup\$

I made a sequence and series solver just for helping me solve homework, and I'm in dire need of ways to make it further compact and efficient, since I used brute force.

If you want to see what this is supposed to do, please see my github.

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.

# just to make the input_checker function smaller and to eliminate repeated code, responsible for iterating through a list of inputs
def three_variables_looper_arithmetic():
    count_list = [input("enter a1: "), input("enter n: "), input("enter d: ")]
    count_list = [float(eval(count)) for count in count_list if isinstance(count, str)]
    return count_list

# just to make the input_checker function smaller and to eliminate repeated code, responsible for iterating through a list of inputs
def three_variables_looper_geometric():
    count_list = [input("enter a1: "), input("enter r: "), input("enter n: ")]
    count_list = [float(eval(count)) for count in count_list if isinstance(count, str)]
    return count_list


# loops through all the inputs of a given situation based on whether its arithmetic
# or not, and checks whether the input is string like "6/2" so it could evaluate it, allows input of fractions
def input_checker(choice_main, choice_sub, L):
    if choice_main == 'arithmetic':
        if choice_sub == 'a_nth':
            return three_variables_looper_arithmetic()

        elif choice_sub == 'sum_to_nth_without_L':
            return three_variables_looper_arithmetic()

        elif choice_sub == 'sum_to_nth_with_L':
            count_list = [input("enter a1: "), input("enter n: "), L]
            count_list = [float(eval(count)) for count in count_list if isinstance(count, str)]
            return count_list

        elif choice_sub == "a_nth_exceed":
            count_list = [input("enter a1: "), input("enter r/d: ")]
            count_list = [float(eval(count)) for count in count_list if isinstance(count, str)]
            return count_list

        elif choice_sub == "sum_to_nth_without_L_exceed":
            count_list = [input("enter a1: "), input("enter r/d: ")]
            count_list = [float(eval(count)) for count in count_list if isinstance(count, str)]
            return count_list

        elif choice_sub == "sum_to_nth_with_L_exceed":
            count_list = [input("enter a1: "), L]
            count_list = [float(eval(count)) for count in count_list if isinstance(count, str)]
            return count_list


    elif choice_main == 'geometric':
        if choice_sub == 'a_nth':
            return three_variables_looper_geometric()

        elif choice_sub == 'sum_to_nth':
            return three_variables_looper_geometric()

        elif choice_sub == 'sum_to_infinity':
            count_list = [input("enter a1: "), input("enter r: ")]
            count_list = [float(eval(count)) for count in count_list if isinstance(count, str)]
            return count_list

        elif choice_sub == "a_nth_exceed":
            count_list = [input("enter a1: "), input("enter r/d: ")]
            count_list = [float(eval(count)) for count in count_list if isinstance(count, str)]
            return count_list

        elif choice_sub == "sum_to_nth_without_L_exceed":
            count_list = [input("enter a1: "), input("enter r/d: ")]
            count_list = [float(eval(count)) for count in count_list if isinstance(count, str)]
            return count_list

# checks if L is an x or not, also based on whether its on the exceed or normal path, and
# an x means L is not present, while a value of L represents it is present and used in calculation
def L_evaluator(L, option, choice_n, value):
    if option == "normal":
        if L == "x":
                    a1, n, d = input_checker(choice_main, choice_n, L)
                    result = (n/2)*(2*a1+(n-1)*d)
                    return result
        else:
                    choice_n = choice_map_sub['x']
                    a1, n, L = input_checker(choice_main, choice_n, L)
                    result = (n/2)*(a1+L)
                    return result

    if option == "exceed":
        if L == "x":
                    a1, d = input_checker(choice_main, choice_n, 0)
                    a1, d = float(a1), float(d)
                    n = 1
                    while True:
                        result = (n/2)*(2*a1+(n-1)*d)
                        if (result >= float(value)):
                            break
                        n += 1
                    return n
        else:
                    choice_n = choice_map_exceed['c']
                    a1, L = input_checker(choice_main, choice_n, L)
                    n = 1
                    while True:
                        result = (n/2)*(a1+L)
                        if (result >= float(value)):
                            break
                        n += 1
                    return n


# finds the first n to exceed a certain value, by using brute force method
def minimum_n_finder(choice_main, choice_map_exceed):
    choice_n_input = None
    if choice_main == "arithmetic":
        while choice_n_input not in ['a', 'b']:
            choice_n_input = input("Enter a for nth\nEnter b for sum\n>> ")
        choice_n = choice_map_exceed[choice_n_input]
        print("enter x in n")

        if choice_n == "a_nth_exceed":
            print("a1+(n-1)d > Value")
            a1, d = input_checker(choice_main, choice_n, 0)
            n = 1
            value = input("Enter the value to exceed: ")
            while True:
                result = a1+(n-1)*d
                if (result >= float(value)):
                    break
                n += 1
            print("The minimum n to exceed is " + str(n))

        if choice_n == "sum_to_nth_without_L_exceed":
            n = 1
            print("Sn=(n/2)(2a1+(n-1)d)>Value\nSn=(n/2)(a1+L)>Value\nEnter x if L is unknown")
            L = input("Enter L: ")
            value = input("Enter the value to exceed: ")
            result = L_evaluator(L, "exceed", choice_n, value)
            print("The minimum n to exceed is " + str(result))


    elif choice_main == 'geometric':
        while choice_n_input not in ['a', 'b']:
            choice_n_input = input("Enter a for nth\nEnter b for sum_to_nth\n>> ")
        choice_n = choice_map_exceed[choice_n_input]

        if choice_n == "a_nth_exceed":
            print("a1(r)^(n-1)>Value")
            a1, r = input_checker(choice_main, choice_n, 0)
            if a1 == 0:
                print("a cannot be 0")
                raise SystemExit
            n = 1
            value = input("Enter the value to exceed: ")
            while True:
                result = a1*(r)**(n-1)
                if (result >= float(value)):
                    break
                n += 1
            print("The minimum n to exceed is " + str(n))

        elif choice_n == "sum_to_nth_without_L_exceed":
            print("Sn=(a1(1-(r)^n))/(1-r)")
            a1, r = input_checker(choice_main, choice_n, 0)
            if a1 == 0:
                print("a cannot be 0")
                raise SystemExit
            n = 1
            value = input("Enter the value to exceed: ")
            while True:
                result = (a1*(1-(r)**n))/(1-r)
                if (result >= float(value)):
                    break
                n += 1
            print("The minimum n to exceed is " + str(n))


# as this code is for a calculator the x button is very easily accessible to shut the whole program.
def stopper():
    stop_or_continue = input("Stop?: enter x then\n>>>  ")
    if stop_or_continue == "x":
        raise SystemExit  

print("Sequence & Series Solver")

# asks whether you want to solve arithmetically or geometrically, depends on the sequence/series
while True:
    choice_main , choice_input_main = None, None
    choices_main_options = ['a','b']
    choice_map_main ={"a": 'arithmetic', "b": 'geometric'}
    while choice_input_main not in choices_main_options:
        choice_input_main = input("a for arithmetic\nb for geometric\n>> ")
    choice_main = choice_map_main[choice_input_main]

    if choice_main == "arithmetic":
        print("Arithmetic: ")
        choice_sub, choice_input_sub = None, None
        choices_sub_options = ['a', 'b', 'c']
        choice_map_sub = {'a': 'a_nth', 'b': 'sum_to_nth_without_L', 'x': 'sum_to_nth_with_L', 'c':'minimum_number_of_terms_to_exceed'}
        while choice_input_sub not in choices_sub_options:
            choice_input_sub = input("a for a_nth term\nb for sum\nc for min_term_to_exceed\n>> ")
        choice_sub = choice_map_sub[choice_input_sub]

        # the variable choice_main refers to whether the choice is arithmetic or geometric
        # choice_sub refers to the types of formulas you'll use in sequences/series
        if choice_sub == "a_nth":
            print("a_nth=a1+(n-1)d")
            a1, n, d = input_checker(choice_main, choice_sub, 0)
            result = a1+(n-1)*d
            print(round(result,4))

        elif choice_sub == "sum_to_nth_without_L":
            print("Sn=(n/2)(2a1+(n-1)d)\nSn=(n/2)(a1+L)\nEnter x if L is unknown")
            L = input("Enter L: ")
            print(round(L_evaluator(L, "normal", choice_sub, 0), 4))

        elif choice_sub == "minimum_number_of_terms_to_exceed":
            choice_map_exceed = {'a': 'a_nth_exceed', 'b': 'sum_to_nth_without_L_exceed', 'c': 'sum_to_nth_with_L_exceed'}
            minimum_n_finder("arithmetic", choice_map_exceed)


    elif choice_main == "geometric":
        print("Geometric: ")
        choice_sub, choice_input_sub = None, None
        choices_sub_options = ['a', 'b', 'c', 'd']
        choice_map_sub = {'a': 'a_nth', 'b': 'sum_to_nth', 'c': 'sum_to_infinity', 'd': 'minimum_number_of_terms_to_exceed'}
        while choice_input_sub not in choices_sub_options:
            choice_input_sub = input("a for a_nth term\nb for sum\nc for sum to infinity\nd for min_terms_exceed\n>> ")
        choice_sub = choice_map_sub[choice_input_sub]

        if choice_sub == "a_nth":
            print("a_nth=a1(r)^(n-1)")
            a1, r, n = input_checker(choice_main, choice_sub, 0)
            result = a1*(r)**(n-1)
            print(round(result,4))

        elif choice_sub == "sum_to_nth":
            print("Sn=(a1(1-(r)^n))/(1-r)")
            a1, r, n = input_checker(choice_main, choice_sub, 0)
            try:
                result = (a1*(1-(r)**n))/(1-r)
                print(round(result,4))
            except (ZeroDivisionError, NameError):
                print("r cannot be 1!")

        elif choice_sub == "sum_to_infinity":
            print("S_inf=a1/(1-r)")
            a1, r = input_checker(choice_main, choice_sub, 0)
            if (r > 1):
                print("r cannot be greater than 1")
                raise SystemExit
            try:
                result = a1/(1-r)
                print(round(result,4))
            except (ZeroDivisionError, NameError):
                print("r cannot be 1!")

        elif choice_sub == "minimum_number_of_terms_to_exceed":
            choice_map_exceed = {'a': 'a_nth_exceed', 'b': 'sum_to_nth_without_L_exceed', 'c': 'sum_to_nth_with_L_exceed'}
            minimum_n_finder("geometric", choice_map_exceed)

    stopper()
\$\endgroup\$
3
  • \$\begingroup\$ What would the user enter when asked for enter a1:? A mathematical expression? \$\endgroup\$
    – AlexV
    Commented Mar 11, 2020 at 17:13
  • \$\begingroup\$ yeah i set the prints before the input to show the formula so youd know what you are entering to calculate \$\endgroup\$
    – Anonymous
    Commented Mar 11, 2020 at 20:14
  • \$\begingroup\$ and each input like a1, r/d, is one value not an expression, tho the expression could be 1/2 for example or generally something that isnt whole and could be expressed using only fractions \$\endgroup\$
    – Anonymous
    Commented Mar 11, 2020 at 20:14

1 Answer 1

1
\$\begingroup\$

First, you can just return the list comprehension instead of first overwriting count_list. That should save you a few lines without affecting readability.

For all of your if-cases, you could use a dictionary instead. So that if choice_sub in dict.keys(): dict[choice_sub].

Generally, when functions are getting as long as yours are, it's a good idea to start thinking about OOP and classes. And you could separate the "front-end" (all of your prints and inputs) from your "back-end" (the functions/methods performing the calculations). This would also make it easier on yourself if you want to migrate away from a CLI app to something with a graphical interface (which could help make the formatting of the formulas more readable).

\$\endgroup\$

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.