Using recursion on sequence of mathematical numbers? - python

I want to use recursion on continuous fractions, which will go like this.
The user will input a value of n, and if n is 3, then it will go like this:
2 + 1/ (1 + 1 / (2 + 2/ (3 + 3 / 4)))
where the first 2 + 1/ are fixed in the equation, now we have to calculate the numbers behind it. That is the part where i have been scratching my head for days. Someone taught me that it is possible to use recursion to calculate that part, but I have been trying to learn how to use it but mine just loops forever or has a recursionError
n = int(input("Enter n value: "))
result = 0
fract = 0
def frac(j):
global fract
for i in range(j):
fract = (j + j)/ frac(j)
return fract
def calc_e():
e = 2 + 1/ frac(n) / n + 1
return e
print(calc_e())
#formula = 2 + 1 / (1 + 1 / (2 + 2 / (3 + 3 / (4 + 4 / (...)))))
TLDR: I dont know how to calculate the part after 2 + 1/

There is no need to use a global variable; I don't see where the need for a for-loop arises either. And as previous answers have pointed out, you have a few algebra mistakes.
You should add a stopping condition argument to frac to stop it from infinitely recursing. Return the value j if this is hit:
# max number of terms n
def frac(j, n):
if j >= n: return n
return j + j / frac(j+1, n)
# remember to pass 1.0 instead of 1, or cast to float in frac
# to avoid integer division which will truncate all fractions
def calc_e(n=100):
return 2 + 1 / frac(1.0, n)
This returns:
n calc_e(n)
----------------------
2 2.666666666666667
4 2.716981132075472
8 2.718281657666404
16 2.718281828459045
32 2.718281828459046
64 2.718281828459046
128 2.718281828459046
256 2.718281828459046
512 2.718281828459046
A higher number of recursions was not possible due to stack overflow.

You can use simple recursion:
#2 + 1/ (1 + 1 / (2 + 2/ (3 + 3 / 4)))
def get_equation(_to_val, string_rep = False):
if string_rep:
def calculate(start, end):
return '{}+{}/({})'.format(start, start, calculate(start+1, end) if start < end else end+1)
return '2+1/({})'.format(calculate(1, _to_val))
def calculate(start, end):
return start+start/(calculate(start+1, end) if start < end else end+1)
return calculate(1, _to_val)
print(get_equation(3))
print(get_equation(3, True))
Output:
1.3947368421052633
2+1/(1+1/(2+2/(3+3/(4))))

You have some basic problems in your frac routine
def frac(j):
global fract
for i in range(j):
fract = (j + j)/ frac(j)
return fract
You haven't settled on how to communicate the result: you keep updating the global fract, but returning its value afterward. Only one of these should be necessary.
Similarly, you are stepping through the continued fraction with both a for loop and recursion. You should use only one of these.
You have no base case on your recursion: there's nothing to tell the algorithm that it has "hit bottom". Every time you hit the computation step, you recur on frac; there's no logic path that does not recur. This is the main reason you get infinite recursion. If these are new concepts for you, please work through a tutorial on recursion; that will explain it at least as well as we can here.
Your algebra is wrong: (j + j)/ frac(j) is the wrong order of operations, and gives you merely 2*j / frac(j). You should have something like j + j/frac(j+1).
Is that enough hints to get you to a more useful coding practice? You may also need help debugging; a few well-chosen print statements will usually show you what's happening. See this lovely debug blog for help.

Related

What exactly is Stop in this question and how do I get the sum? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 months ago.
Improve this question
Problem Statement
Edit: I have transcribed the image as suggested although I think some terms are better shown in the picture if anything is unclear here;
This function takes in a positive integer n and returns the sum of the following series Sn, as long as the absolute value of each term is larger than stop.
Sn= 1 − 1/2 + 1/3 − 1/4 + ... + (−1)n+1/n + ...
You can assume that stop is a float value and 0 < stop < 1.
You need not round the output.
For example, if stop = 0.249, then Sn is evaluated with only four terms.
Sn = 1 − 1/2 + 1/3 − 1/4
For example, if stop = 0.199, then Sn is evaluated with only five terms.
Sn = 1 − 1/2 + 1/3 − 1/4 + 1/5
The built-in function abs() is useful. You should use a while loop.
Test cases:
print( alternating_while(0.249) )
print( alternating_while(0.199) )
gives:
0.5833333333333333
0.7833333333333332
Now for this question, I want to get the sum of this series based on the conditions stipulated in the question.
My problem is I don't understand how to type the formula given in the question because I'm not familiar with how the while-loop works. Can someone instruct me on how to?
def alternating_while(stop):
total = 0
n = 1
term = 1
while abs(term) > stop:
total= (-1) ** (n + 1) / n + alternating_while(n - 1)
return total
No reason to use recursion as it wasn't mentioned as a requirement. Just check the term in the while loop for the stop condition:
Python 3.8+ (for the := operator):
def alternating_while(stop):
n = 1
total = 0
while abs(term := (-1)**(n+1)/n) > stop:
total += term
n += 1
return total
print(alternating_while(0.249))
print(alternating_while(0.199))
Output:
0.5833333333333333
0.7833333333333332
Pre-Python 3.8 version:
def alternating_while(stop):
n = 1
total = 0
while True:
term = (-1)**(n+1)/n
if abs(term) <= stop:
break
total += term
n += 1
return total
Or:
def alternating_while(stop):
n = 1
total = 0
term = (-1)**(n+1)/n
while abs(term) > stop:
total += term
n += 1
term = (-1)**(n+1)/n # redundant
return total
The key is "alternating". You can just increment the current denominator one at a time. If it is odd, you add. Otherwise, you subtract. abs is not really required; I'm not sure why they would mention it.
def alternating_while(stop):
total = 0
denom = 1
while 1/denom > stop:
if denom & 1:
total += 1/denom
else:
total -= 1/denom
denom += 1
return total
print(alternating_while(0.249))
print(alternating_while(0.199))
Output:
0.5833333333333333
0.7833333333333332
You need to cycle between adding and subtracting. The itertools module has a very helpful cycle class which you could utilise thus:
from itertools import cycle
from operator import add, sub
def get_term(d=2):
while True:
yield 1 / d
d += 1
def calc(stop=0.199):
c = cycle((sub, add))
term = get_term()
Sn = 1
while (t := next(term)) > stop:
Sn = next(c)(Sn, t)
return Sn
print(calc())
Output:
0.6936474305598223
Note:
The reference in the problem statement to absolute values seems to be irrelevant as no terms will ever be negative
I understand you need to use while in this particular problem, and this answer won't immediately help you as it is probably a few steps ahead of the current level of your course. The hope however is that you'll find it intriguing, and will perhaps come back to it in the future when you start being interested in performance and the topics introduced here.
from math import ceil
def f(stop):
n = ceil(1 / stop) - 1
return sum([(2 * (k & 1) - 1) / k for k in range(1, n + 1)])
Explanation
First, we want to establish ahead of time n, so that we avoid a math evaluation at each loop to decide whether to stop or not. Instead, the main loop is now for k in range(1, n + 1) which will go from 1 to n, included.
We use the oddness of k (k & 1) to determine the sign of each term, i.e. +1 for k == 1, -1 for k == 2, etc.
We make the series of terms in a list comprehension (for speed).
(A point often missed by many Pythonistas): building the list using such a comprehension and then summing it is, counter-intuitively, slightly faster than summing directly from a generator. In other words, sum([expr for k in generator]) is faster than sum(expr for k in generator). Note: I haven't tested this with Python 3.11 and that version of Python has many speed improvements.
For fun, you can change slightly the loop above to return the elements of the terms and inspect them:
def g(stop):
n = ceil(1 / stop) - 1
return [(2 * (k & 0x1) - 1, k) for k in range(1, n + 1)]
>>> g(.249)
[(1, 1), (-1, 2), (1, 3), (-1, 4)]

What is the time complexity of this recursion?

def s_r(n):
if(n==1):
return 1
temp = 1
for i in range(int(n)):
temp += i
return temp * s_r(n/2) * s_r(n/2) * s_r(n/2) * s_r(n/2)
Using recursion tree what’s the Big Oh
And also how do we write this function into one recursive call.
I could only do the first part which I got O(n^2). This was one my exam questions which I want to know the answer and guidance to. Thank you
First, note that the program is incorrect: n/2 is floating point division in python (since Python3), so the program does not terminate unless n is a power of 2 (or eventually rounds to a power of 2). The corrected version of the program that works for all integer n>=1, is this:
def s_r(n):
if(n==1):
return 1
temp = 1
for i in range(n):
temp += i
return temp * s_r(n//2) * s_r(n//2) * s_r(n//2) * s_r(n//2)
If T(n) is the number of arithmetic operations performed by the function, then we have the recurrence relation:
T(1) = 0
T(n) = n + 4T(n//2)
T(n) is Theta(n^2) -- for n a power of 2 and telescoping we get: n + 4(n/2) + 16(n/4) + ... = 1n + 2n + 4n + 8n + ... + nn = (2n-1)n
We can rewrite the program to use Theta(log n) arithmetic operations. First, the temp variable is 1 + 0+1+...+(n-1) = n(n-1)/2 + 1. Second, we can avoid making the same recursive call 4 times.
def s_r(n):
return (1 + n * (n-1) // 2) * s_r(n//2) ** 4 if n > 1 else 1
Back to complexity, I have been careful to say that the first function does Theta(n^2) arithmetic operations and the second Theta(log n) arithmetic operations, rather than use the expression "time complexity". The result of the function grows FAST, so it's not practical to assume arithmetic operations run in O(1) time. If we print the length of the result and the time taken to compute it (using the second, faster version of the code) for powers of 2 using this...
import math
import timeit
def s_r(n):
return (1 + n * (n-1) // 2) * s_r(n//2) ** 4 if n > 1 else 1
for i in range(16):
n = 2 ** i
start = timeit.default_timer()
r = s_r(n)
end = timeit.default_timer()
print('n=2**%d,' % i, 'digits=%d,' % (int(math.log(r))+1), 'time=%.3gsec' % (end - start))
... we get this table, in which you can see the number of digits in the result grows FAST (s_r(2**14) has 101 million digits for example), and the time measured does not grow with log(n), but when n is doubled the time increases by something like a factor of 10, so it grows something like n^3 or n^4.
n=2**0, digits=1, time=6e-07sec
n=2**1, digits=1, time=1.5e-06sec
n=2**2, digits=5, time=9e-07sec
n=2**3, digits=23, time=1.1e-06sec
n=2**4, digits=94, time=2.1e-06sec
n=2**5, digits=382, time=2.6e-06sec
n=2**6, digits=1533, time=3.8e-06sec
n=2**7, digits=6140, time=3.99e-05sec
n=2**8, digits=24569, time=0.000105sec
n=2**9, digits=98286, time=0.000835sec
n=2**10, digits=393154, time=0.00668sec
n=2**11, digits=1572628, time=0.0592sec
n=2**12, digits=6290527, time=0.516sec
n=2**13, digits=25162123, time=4.69sec
n=2**14, digits=100648510, time=42.1sec
n=2**15, digits=402594059, time=377sec
Note that it's not wrong to say the time complexity of the original function is O(n^2) and the improved version of the code O(log n), it's just that these describe a measure of the program (arithmetic operations) and are not at all useful as an estimate of actual program running time.
As #kcsquared said in comments I also believe this function is O(n²). This code can be refactored into one function call by storing the result of recursion call, or just doing some math application. Also you can simplify the range sum, by using the built-in sum
def s_r(n):
if n == 1:
return 1
return (sum(range(int(n))) + 1) * s_r(n/2) ** 4

Is ther any other way to get sum 1 to 100 with recursion?

I'm studing recursive function and i faced question of
"Print sum of 1 to n with no 'for' or 'while' "
ex ) n = 10
answer =
55
n = 100
answer = 5050
so i coded
import sys
sys.setrecursionlimit(1000000)
sum = 0
def count(n):
global sum
sum += n
if n!=0:
count(n-1)
count(n = int(input()))
print(sum)
I know it's not good way to get right answer, but there was a solution
n=int(input())
def f(x) :
if x==1 :
return 1
else :
return ((x+1)//2)*((x+1)//2)+f(x//2)*2
print(f(n))
and it works super well , but i really don't know how can human think that logic and i have no idea how it works.
Can you guys explain how does it works?
Even if i'm looking that formula but i don't know why he(or she) used like that
And i wonder there is another solution too (I think it's reall important to me)
I'm really noob of python and code so i need you guys help, thank you for watching this
Here is a recursive solution.
def rsum(n):
if n == 1: # BASE CASE
return 1
else: # RECURSIVE CASE
return n + rsum(n-1)
You can also use range and sum to do so.
n = 100
sum_1_to_n = sum(range(n+1))
you can try this:
def f(n):
if n == 1:
return 1
return n + f(n - 1)
print(f(10))
this function basically goes from n to 1 and each time it adds the current n, in the end, it returns the sum of n + n - 1 + ... + 1
In order to get at a recursive solution, you have to (re)define your problems in terms of finding the answer based on the result of a smaller version of the same problem.
In this case you can think of the result sumUpTo(n) as adding n to the result of sumUpTo(n-1). In other words: sumUpTo(n) = n + sumUpTo(n-1).
This only leaves the problem of finding a value of n for which you know the answer without relying on your sumUpTo function. For example sumUpTo(0) = 0. That is called your base condition.
Translating this to Python code, you get:
def sumUpTo(n): return 0 if n==0 else n + sumUpTo(n-1)
Recursive solutions are often very elegant but require a different way of approaching problems. All recursive solutions can be converted to non-recursive (aka iterative) and are generally slower than their iterative counterpart.
The second solution is based on the formula ∑1..n = n*(n+1)/2. To understand this formula, take a number (let's say 7) and pair up the sequence up to that number in increasing order with the same sequence in decreasing order, then add up each pair:
1 2 3 4 5 6 7 = 28
7 6 5 4 3 2 1  = 28
-- -- -- -- -- -- -- --
8 8 8 8 8 8 8 = 56
Every pair will add up to n+1 (8 in this case) and you have n (7) of those pairs. If you add them all up you get n*(n+1) = 56 which correspond to adding the sequence twice. So the sum of the sequence is half of that total n*(n+1)/2 = 28.
The recursion in the second solution reduces the number of iterations but is a bit artificial as it serves only to compensate for the error introduced by propagating the integer division by 2 to each term instead of doing it on the result of n*(n+1). Obviously n//2 * (n+1)//2 isn't the same as n*(n+1)//2 since one of the terms will lose its remainder before the multiplication takes place. But given that the formula to obtain the result mathematically is part of the solution doing more than 1 iteration is pointless.
There are 2 ways to find the answer
1. Recursion
def sum(n):
if n == 1:
return 1
if n <= 0:
return 0
else:
return n + sum(n-1)
print(sum(100))
This is a simple recursion code snippet when you try to apply the recurrent function
F_n = n + F_(n-1) to find the answer
2. Formula
Let S = 1 + 2 + 3 + ... + n
Then let's do something like this
S = 1 + 2 + 3 + ... + n
S = n + (n - 1) + (n - 2) + ... + 1
Let's combine them and we get
2S = (n + 1) + (n + 1) + ... + (n + 1) - n times
From that you get
S = ((n + 1) * n) / 2
So for n = 100, you get
S = 101 * 100 / 2 = 5050
So in python, you will get something like
sum = lambda n: ( (n + 1) * n) / 2
print(sum(100))

How can I find out how much storage it would take to store every number between 1 and 2^100?

I am complete beginner when it comes to working with large numbers in code and I know this is definitely the wrong approach but this is what I started with.
import tqdm
try:
total = 0
for num in tqdm.tqdm(range(2**100), total=2**100):
total += len(bin(num)) - 2
finally:
with open('results.txt', 'w') as file:
file.write(f'{total=}')
The result I got was:
0%| | 87580807/1267650600228229401496703205376 [00:39<159887459362604133471:34:24, 2202331.37it/s]
Obviously this approach is going to take way too long. I know I could try making this multi-core but I don't think this is going to make much of a difference in the speed.
What are my options here?
Will using another language like C significantly increase the speed so that it will take days or hours instead of eons? Is there another approach/algorithm I can use?
Ok I figured it out. I used #jasonharper's approach.
So the code would be following:
total = 0
for power in range(1, 101):
total += ((int('1' * power, base=2) - int('1' + '0' * (power - 1), base=2)) + 1) * power
total was equal to 125497409422594710748173617332225, which represents the number of bytes needed to store every number between 1 and 2^100.
For some context it would take ≈425414947195.2363 times the total storage capacity of the Earth to store all numbers between 1 and 2^100.
Reference: https://www.zdnet.com/article/what-is-the-worlds-data-storage-capacity/
Interesting problem, but not all problems should be solved using brute force, there comes the part of the algorithm. Looking at your problem, it seems you want to count the number os bits required till some n. Now if we look closely,
number of bits total number of numbers we can represent
1 2**1 = 2
2 2**2 = 4
3 2**3 = 8
4 2**4 = 16
5 2**5 = 32
...
So the sum is like
1*2 + 2*2 + 3*2^2 + 4*2^3 + ...
= 1 + 1*2^0 + 2*2^1 + 3*2^2 + 4*2^3 + ...
= 1 + sum(k*2^(k-1)) with n from 1 to number of bits
= 1 + (k*2^k - 2^k +1)
= k*2^k - 2^k + 2
So there is a geometric progression visible. Using the summation methioned above you can determine the formula
import math
def log2(x):
return math.log(x) / math.log(2)
def count_the_total_number_of_bits_till(n):
neares_2_to_the_power = math.floor(log2(n))
actual_number_of_bits_required = math.ceil(log2(n))
sum_1 = ((neares_2_to_the_power * (2**neares_2_to_the_power)) - (2**neares_2_to_the_power) + 2)
extra_sum = ((n - 2**neares_2_to_the_power) * (actual_number_of_bits_required))
return sum_1 + extra_sum
count_the_total_number_of_bits_till(2**10)
what you were doing
sum_ = 0
for i in range(2**10):
#equivalent to
# count_the_total_number_of_bits_till(2**10)
sum_ += len(bin(i)[2:])
print(sum_)

Algorithm to give shortest expression for one number in terms of another number

Heads up: apologies for my poor style, inefficient code, and general stupidity. I'm doing this purely out of interest. I have no delusions that I will become a professional programmer.
How would solve this problem (assuming it can be solved). To be clear you want to take in an int x and an int y and return some expression in terms of y that equals x. For example if I passed in 9 and 2 one of the shortest solutions (I'm pretty sure) would be ((2+2)x2)+(2/2) with 4 total operators. You can assume positive integers because that's what I'm doing for the most part.
I have found a fastish partial solution that returns a solution for whatever numbers, but usually not the smallest one. Here it is coded in python:
def re_express(n, a, expression):
if n==0:
print(expression)
else:
if a**a < n:
n -= a**a
expression += "+" + str(a) + "^" +str(a)
re_express(n, a, expression)
elif a*a < n:
n -= a*a
expression += "+" + str(a) + "*" + str(a)
re_express(n, a, expression)
elif a < n:
n -= a
expression += "+" + str(a)
re_express(n, a, expression)
else:
n -= 1
expression += "+" + str(a) + "/" + str(a)
re_express(n, a, expression)
I have also think I have one that returns a pretty small solution, but it is not guaranteed to be the smallest solution. I hand simulated it and it got the 2 and 9 example correct, whereas my first algorithm produced a 5 operator solution: 2^2+2^2+2/2. But it gets slow quickly for large n's. I wrote it down in pseudocode.
function re_express(n, a, children, root, solutions):
input: n, number to build
a, number to build with
children, an empty array
root, a node
solutions, an empty dictionary
if not root:
root.value = a
if the number of layers in the tree is greater than the size of the shortest solution, return the shortest solution
run through each mathematical operator and create a child node of root as long as the child will have a unique value
each child.value = operator(root, a)
for each child: child.parent = root
for each child: child.operator = operator as a string
loop through all children and check to see what number you need to produce n with each operator, if that number is in the list of children
when you find something like that then you have paths back up the tree, use the following loop for both children (obviously a child with only i
i = 0
while child != root:
expression += “(“ + str(a) + child.operator
child = child.parent
expression += str(a)
for in range(i):
expression += “)”
then you combine the expressions and you have one possible solution to add up to n and you have one possible answer
store the solution in solutions with the key as the number of operators used and the value as the expression in string form
re_express(n, a, children, root, solutions)
As you can tell the big-O for that algorithm is garbage and it doesn't even give a guaranteed solution, so there must be a better way.
We can use a variation of Dijkstra's algorithm to find the result. However, we don't really have a graph. If we consider numbers as nodes of the graph, then edges would start at a pair of nodes and an operation and end at another number. But this is just a detail that does not prevent Dijkstra's idea.
The idea is the following: We start with y and explore more and more numbers that we can express using a set of pre-defined operations. For every number, we store how many operations we need to express it. If we find a number that we have already seen (but the previous expression takes more operations), we update the number's attributes (this is the path length in Dijktra's terms).
Let's do the example x = 9, y = 2.
Initially, we can represent 2 with zero operations. So, we put that in our number list:
2: 0 operations
From this list, we now need to find the number with the least number of operations. This is essential because it guarantees that we never need to visit this number again. So we take 2. We fix it and explore new numbers that we can express using 2 and all other fixed numbers. Right now, we only have 2, so we can express (using + - * /):
2: 0 operations, fixed
0: 1 operation (2 - 2)
1: 1 operation (2 / 2)
4: 1 operation (2 + 2 or 2 * 2)
On we go. Take the next number with the least number of operations (does not matter which one). I'll take 0. We can't really express new numbers with zero, so:
2: 0 operations, fixed
0: 1 operation (2 - 2), fixed
1: 1 operation (2 / 2)
4: 1 operation (2 + 2 or 2 * 2)
Now let's take 1. The number of operations for new numbers will be the sum of the input numbers' number of operations plus 1. So, using 1, we could
2 + 1 = 3 with 0 + 1 + 1 = 2 operations (2 + (2 / 2))
0 + 1 = 1 with 3 operations --> the existing number of operations for `0` is better, so we do not update
1 + 1 = 2 with 3 operations
2 - 1 = 1 with 2 operations
0 - 1 = -1 with 3 operations *new*
1 - 2 = -1 with 2 operations *update*
1 - 0 = 1 with 3 operations
1 - 1 = 0 with 3 operations
2 * 1 = 2 with 2 operations
0 * 1 = 0 with 3 operations
1 * 1 = 1 with 3 operations
2 / 1 = 2 with 2 operations
0 / 1 = 0 with 3 operations
1 / 2 = invalid if you only want to consider integers, use it otherwise
1 / 0 = invalid
1 / 1 = 1 with 3 operations
So, our list is now:
2: 0 operations, fixed
0: 1 operation (2 - 2), fixed
1: 1 operation (2 / 2), fixed
3: 2 operations (2 + 2/2)
4: 1 operation (2 + 2 or 2 * 2)
-1: 2 operations (2/2 - 2)
Go on and take 4 and so on. Once you've reached your target number x, you are done in the smallest possible way.
In pseudo code:
L = create a new list with pairs of numbers and their number of operations
put (y, 0) into the list
while there are unfixed entries in the list:
c = take the unfixed entry with the least number of operations
fix c
if c = x:
finish
for each available operation o:
for each fixed number f in L:
newNumber = o(c, f)
operations = c.numberOfOperations + f.numberOfOperations + 1
if newNumber not in L:
add (newNumber, operations) to L
else if operations < L[newNumber].numberOfOperations:
update L[newNumber].numberOfOperations = operations
repeat for o(f, c)
If you store the operation list with the numbers (or their predecessors), you can reconstruct the expression at the end.
Instead of using a simple list, a priority queue will make the retrieval of unfixed entries with the minimum number of operations fast.
Here is my code for this problem which I don't think is the fastest and the most optimal algorithm, but it should always find the best answer.
First, let's assume we don't have parentheses and solve the subproblem for finding the minimum number of operations to convert a to n with these operations ['+', '*', '/', '-']. For solving this problem we use BFS. First we define an equation class:
class equation:
def __init__(self, constant, level=0, equation_list=None):
self.constant = constant
if equation_list != None:
self.equation_list = equation_list
else:
self.equation_list = [constant]
self.level = level
def possible_results(self):
return recursive_possible_results(self.equation_list)
def get_child(self, operation):
child_equation_list = self.equation_list + [operation]
child_equation_list += [self.constant]
child_level = self.level + 1
return equation(self.constant, child_level, child_equation_list)
The constructor gets a constant which is the constant in our expressions (here is a) and a level which indicates the number of operations used in this equation, and an equation_list which is a list representation of equation expression. For the first node, ( root ) our level is 0, and our equation_list has only the constant.
Now let's calculate all possible ways to parentheses an equation. We use a recursive function which returns a list of all possible results and their expressions:
calculated_expr = {}
def is_operation(symbol):
return (symbol in operations)
def recursive_possible_results(equation_list):
results = []
if len(equation_list) == 1:
return [{'val': equation_list[0], 'expr': str(equation_list[0])}]
key = ''
for i in range(len(equation_list)):
if is_operation(equation_list[i]):
key += equation_list[i]
if key in calculated_expr.keys():
return calculated_expr[key]
for i in range(len(equation_list)):
current_symbol = equation_list[i]
if is_operation(current_symbol):
left_results = recursive_possible_results(equation_list[:i])
right_results = recursive_possible_results(equation_list[i+1:])
for left_res in left_results:
for right_res in right_results:
try:
res_val = eval(str(left_res['val']) + current_symbol + str(right_res['val']))
res_expr = '(' + left_res['expr'] + ')' + current_symbol + '(' + right_res['expr'] + ')'
results.append({'val': res_val, 'expr': res_expr})
except ZeroDivisionError:
pass
calculated_expr[key] = results
return results
(I think this is the part of the code that needs to be more optimized since we are calculating a bunch of equations more than once. Maybe a good dynamic programming algorithm ...)
For Breadth-First-Search we don't need to explore the whole tree from the root. For example for converting 2 to 197, we need at least 7 operations since our biggest number with 6 operations is 128 and is still less than 197. So, we can search the tree from level 7. Here is a code to create our first level's nodes (equations):
import math
import itertools
def create_first_level(n, constant):
level_exprs = []
level_number = int(math.log(n, constant)) - 1
level_operations = list(itertools.product(operations, repeat=level_number))
for opt_list in level_operations:
equation_list = [constant]
for opt in opt_list:
equation_list.append(opt)
equation_list.append(constant)
level_exprs.append(equation(constant, level_number, equation_list))
return level_exprs
In the end, we call our BFS and get the result:
def re_express(n, a):
visit = set()
queue = []
# root = equation(a)
# queue.append(root)
# Skip levels
queue = create_first_level(n, a)
while queue:
curr_node = queue.pop(0)
for operation in operations:
queue.append(curr_node.get_child(operation))
possible_results = curr_node.possible_results()
for pr in possible_results:
if pr['val'] == n:
print(pr['expr'])
print('Number of operations: %d' % curr_node.level)
queue = []
break
re_express(9, 2)
Here is the output:
(((2)+(2))*(2))+((2)/(2))
Number of operations: 4

Categories