How to find the number of times function f is executed? - python

The function f is defined as such:
def f(a, b):
if a <= 0 or b <= 0:
return a + b
else:
s = 0
if b * b % (a + b) != 0:
s += f(a, b - 3)
if a * a % (a + b) != 0:
s += f(a - 2, b)
if a == b - 1:
s += f(a - 3, b - 2)
return s
The question is: "How many times will a function "f" be executed, given f(4, 9)?" For example, for f(0, 0) function "f" will be executed once, since the first time is taken into account as well. Can someone explain to me, how I can find the number of executions? (Examples will be ideal.) Than you in advance!

You can attach an attribute to the function:
def f(a, b):
f.num += 1
if a <= 0 or b <= 0:
...
Result:
>>> f.num = 0
>>> f(0, 0)
0
>>> print(f.num)
1
>>> f.num = 0
>>> f(3, 5)
4
>>> print(f.num)
13

You can increment a global counter, an int wrapped in a list, incrementing it in the function and printing the result after you call it:
i = [0]
def f(a, b):
i[0] += 1
if a <= 0 or b <= 0:
return a + b
else:
s = 0
if b * b % (a + b) != 0:
s += f(a, b - 3)
if a * a % (a + b) != 0:
s += f(a - 2, b)
if a == b - 1:
s += f(a - 3, b - 2)
return s
f(2, 34)
print(i[0])

I don't trust Global variable because of the highly available scope i.e. global variables are accessible to everyone. Instead, you can keep a recursion variable which counts the number of times the function has been called.
def f(a, b, count=0):
count+=1
if a <= 0 or b <= 0:
return [a + b, count]
else:
s = 0
if b * b % (a + b) != 0:
l = f(a, b - 3,count)
s += l[0]
count+=l[1]
if a * a % (a + b) != 0:
l = f(a - 2, b, count)
s += l[0]
count+=l[1]
if a == b - 1:
l = f(a - 3, b - 2, count)
s += l[0]
count+=l[1]
return [s,count]

Related

Improving the runinng time of a numerical algorithm by parallel computation, using gmpy2 module of Python

I'm working on a numerical algorithm as follows.
import timeit
import gmpy2
import math
A = gmpy2.mpz(1000007777)
# Input a positive integer to factor.
# Put it inside the parentheses as "A = gmpy2.mpz(....)"
def factor(A):
B = gmpy2.mpz(math.sqrt(2 * A + 0.25) - 1)
D = gmpy2.mpz(A - (B * B + B) / 2)
while (D > 0):
B += 1
D = gmpy2.mpz(A - (B * B + B) / 2)
n = gmpy2.mpz(0)
while (D != 0 and B <= A):
if (D > 0):
B += 1
D -= B
else:
n += 1
D += n
if B > A:
return output(0, 0, 0)
else:
print(f"[B={B}, n={n}]")
if (B - n) % 2 == 0:
E = gmpy2.mpz((B - n) / 2)
F = gmpy2.mpz(B + n + 1)
else:
E = gmpy2.mpz(B - n)
F = gmpy2.mpz((B + n + 1) / 2)
return output(A, E, F)
def output(A, E, F):
if A == 0:
print(f"Initial Value Error: Reset the B value")
else:
if A % E != 0 or A % F != 0 or A != E * F:
print(f"[Error Occurred] {A} != {E} * {F}")
else:
print(f"{A} = {E} * {F}")
return 0
if __name__ == '__main__':
if A >= 2:
timer_start = timeit.default_timer()
factor(A)
timer_stop = timeit.default_timer()
running_time = round(timer_stop - timer_start, 6)
print("running time: ", running_time, " seconds")
else:
print("undefined for A<2")
(Note that the output of B and n comes out as [B=21739322, n=21739276] with our argument in the example code)
The task of this code aims to reach 0 by adding and subtracting integers. The D value shall oscillate between 0 until it reaches 0. I wanted to improve the running time of this task by parallel computation, using multiprocessing module of Python.
import timeit
import gmpy2
import math
import multiprocessing
A = gmpy2.mpz(1000007777)
# Input a positive integer to factor.
# Put it inside the parentheses as "A = gmpy2.mpz(....)"
k = 4
# Input the number of cores to be used as "k=..."
# [!!Caveat!!] Don't use too much number of cores which exceeds the
# availability of CPU resources of your personal computer environment.
# It can damage the CPU !!!
def factor(n):
if A < 2:
print("undefined for A<2")
else:
B = gmpy2.mpz(math.sqrt(2 * A + 0.25) - 1)
D = gmpy2.mpz(A - (B * B + B) / 2)
while (D > 0):
B += 1
D = gmpy2.mpz(A - (B * B + B) / 2)
D += gmpy2.mpz(n * (n + 1) / 2)
while (D != 0 and B <= A):
if (D > 0):
B += 1
D -= B
else:
D += k * n + (k * (k + 1)) / 2
n += k
if B > A:
return output(0, 0, 0)
else:
if (B - n) % 2 == 0:
E = gmpy2.mpz((B - n) / 2)
F = gmpy2.mpz(B + n + 1)
else:
E = gmpy2.mpz(B - n)
F = gmpy2.mpz((B + n + 1) / 2)
return output(A, E, F)
def output(A, E, F):
timer_stop = timeit.default_timer()
running_time = round(timer_stop - timer_start, 6)
if A == 0:
print(f"Initial Value Error: Reset the B or k value \n")
else:
if A % E != 0 or A % F != 0 or A != E * F:
print(f"[Error Occurred] {A} != {E} * {F} \n")
else:
print(f"[running time: {running_time} seconds] {A} = {E} * {F} \n")
return 0
if __name__ == '__main__':
n = []
timer_start = timeit.default_timer()
with multiprocessing.Pool(processes=k) as pool:
for x in range(0, k):
y = gmpy2.mpz(x)
n.append(y)
results = pool.map(factor, n)
The idea behind this is that, executing some addition of integers until meeting its target value, which is 0 in our code, shall be done faster if the added size of integers becomes bigger. For example, it is quite obvious that adding integers as 4+4+4... is faster to reach 100 than adding integers as 1+1+1...
However, my numerical experiments have shown that the latter code of multiprocessing module is nowhere faster than the previous code of single-core.
But, noticing that the value n=21739276 of which we mentioned above is divisible by 4, I've tried the modification of single-core version code as follows
import timeit
import gmpy2
import math
A = gmpy2.mpz(1000007777)
# Input a positive integer to factor.
# Put it inside the parentheses as "A = gmpy2.mpz(....)"
def factor(A):
B = gmpy2.mpz(math.sqrt(2 * A + 0.25) - 1)
D = gmpy2.mpz(A - (B * B + B) / 2)
while (D > 0):
B += 1
D = gmpy2.mpz(A - (B * B + B) / 2)
n = gmpy2.mpz(0)
while (D != 0 and B <= A):
if (D > 0):
B += 1
D -= B
else:
D += 4*n+10
n += 4
if B > A:
return output(0, 0, 0)
else:
print(f"[B={B}, n={n}]")
if (B - n) % 2 == 0:
E = gmpy2.mpz((B - n) / 2)
F = gmpy2.mpz(B + n + 1)
else:
E = gmpy2.mpz(B - n)
F = gmpy2.mpz((B + n + 1) / 2)
return output(A, E, F)
def output(A, E, F):
if A == 0:
print(f"Initial Value Error: Reset the B value")
else:
if A % E != 0 or A % F != 0 or A != E * F:
print(f"[Error Occurred] {A} != {E} * {F}")
else:
print(f"{A} = {E} * {F}")
return 0
if __name__ == '__main__':
if A >= 2:
timer_start = timeit.default_timer()
factor(A)
timer_stop = timeit.default_timer()
running_time = round(timer_stop - timer_start, 6)
print("running time: ", running_time, " seconds")
else:
print("undefined for A<2")
In my computer, this made the running time to be decreased as around 3 seconds from about 4 seconds. So I concluded that the reason for the multiprocessing version code being slower is due to this part
else:
D += k * n + (k * (k + 1)) / 2
n += k
where the code is constantly interpreting what the argument is and translating it as an actual integer.
To avoid this, the easiest way to do is making a several code, when doing parallel computation on 4 cores, as follows.
else:
D += n + 1
n += 1
else:
D += 2*n + 3
n += 2
else:
D += 3*n + 6
n += 3
else:
D += 4*n + 10
n += 4
Among them, only the "n += 3" one will not give a desired output since it is not divisible by 4. (Recall that 4 is a divisor of n=21739276 from our original code)
If there are only 4 cases to test, then you can just make 4 copies of similar code by your hand.
However, let's think about this code
import timeit
import gmpy2
import math
A = gmpy2.mpz(1000007777)
# Input a positive integer to factor.
# Put it inside the parentheses as "A = gmpy2.mpz(....)"
def factor(A):
B = gmpy2.mpz(math.sqrt(2 * A + 0.25) - 1)
D = gmpy2.mpz(A - (B * B + B) / 2)
while (D > 0):
B += 1
D = gmpy2.mpz(A - (B * B + B) / 2)
n = gmpy2.mpz(0)
while (D != 0 and B <= A):
if (D > 0):
B += 1
D -= B
else:
D += 11299*n+63839350
n += 11299
if B > A:
return output(0, 0, 0)
else:
print(f"[B={B}, n={n}]")
if (B - n) % 2 == 0:
E = gmpy2.mpz((B - n) / 2)
F = gmpy2.mpz(B + n + 1)
else:
E = gmpy2.mpz(B - n)
F = gmpy2.mpz((B + n + 1) / 2)
return output(A, E, F)
def output(A, E, F):
if A == 0:
print(f"Initial Value Error: Reset the B value")
else:
if A % E != 0 or A % F != 0 or A != E * F:
print(f"[Error Occurred] {A} != {E} * {F}")
else:
print(f"{A} = {E} * {F}")
return 0
if __name__ == '__main__':
if A >= 2:
timer_start = timeit.default_timer()
factor(A)
timer_stop = timeit.default_timer()
running_time = round(timer_stop - timer_start, 6)
print("running time: ", running_time, " seconds")
else:
print("undefined for A<2")
Note that 11299 is a divisor of n=21739276. Indeed, this has decreased the running time of the code from around 2 seconds from 4 seconds.
If we do the same job with subtract part
if (D > 0):
B += 1
D -= B
I'm sure the code shall be faster but I haven't tried this yet.
So, why do we need to do this? That is, why do we need to make multiple version of similar codes to do a task of this algorithm, instead of directly replacing the arguments with the divisor of n?
It is because, we don't know the B and n value before we try it.
(If you find a method to know them don't tell me, just publish it on some decent journal)
Therefore, unless we figure out any pattern of B and n values to be identified, I believe the best method at current stage is to make multiple copies of similar code to be executed.
Here is the organized question,
Is there any way to replace the 'actual lines' inside a code by following the number of cores to be used?
For example, if there is a desired answer for the question, when there is a original code which include some lines of code as follows,
else:
D += n + 1
n += 1
and applying the method of desired answer shall result in copying the following codes
else:
D += 2*n + 3
n += 2
else:
D += 3*n + 6
n += 3
else:
D += 4*n + 10
n += 4
which do the same task, but only those lines of code have been replaced. If the number of code has been increased to 6, then we will have two more codes to be added that will concurrently executed for doing the task.
else:
D += 5*n + 15
n += 5
else:
D += 6*n + 21
n += 6
To repeat, the 'actual lines' of the code shall be replaced with another 'actual lines' since the following code
else:
D += k * n + (k * (k + 1)) / 2
n += k
is not that helpful for doing the exhaustive task, because of its job of interpreting what the actual argument of k is.
I've tried to be as specific as possible to describe my problem. If you need more elaboration please let me have a chance to elaborate the problem via comments and edits. Thank you.
What I've tried
I've tried to do the similar task at Java. Since Java is well known for OOP, and I thought OOP might be a promising way to solve this problem. However, the problem that Java has, it is not designed for arithmetic. I've tried the BigInteger library but it was extremely slow in my numerical test, even more than Python.
The other possible method could be doing some Scanf job by one's hand for the whole number of the sequence of code I'm going to use to. However, even assuming this method works, if the number of cores to be used increase as thousands then the method would not be considered as the best solution to this.
We've shown that
else:
D += 11299*n+63839350
n += 11299
this code works faster than the previous codes. However for a bigger argument of A, we might need more than thousands, maybe millions of multi-cores, to do the task efficiently.
So I have concluded this method is not the best shot that we can try, leaving aside whether it actually works.
Edit
I wanted to engage on discussion as much as I can. However, because of some unfriendly behavior at the comments, I may refrain myself from doing that. (Really appreciate the answer though)
This could be my last edit in this post. I just wanted to point out, the fact that 11299 is a divisor of n=21739276 doesn't mean that we can't try more than that.
Note that n = 21739276 = 19276 (mod 30000). Then, by replacing that part of the code as follows
n = gmpy2.mpz(19276)
D += gmpy2.mpz(n * (n + 1) / 2)
while (D != 0 and B <= A):
if (D > 0):
B += 1
D -= B
else:
D += 30000*n+450015000
n += 30000
I could reduce the running time from around 2.4seconds to 2.1seconds. I expect the running time can be reduced more if I manipulate this subtraction part,
if (D > 0):
B += 1
D -= B
but I haven't tried yet. All of these elements were the motivation behind that I thought I needed to know how to do the effective parallel computation. As I said, I may not engage on further discussion unless definitely required. If you are interested, you may feel free to test and modify this code without noticing me.
a needlessly long question, but this is python ... you can generate code at runtime if you write the function as a string, and replace the parts you need with numbers from arguments, you can exec the code exactly as you want, but this is not pythonic or fastest.
if your "hot loop" is limited by calculating things that can be known beforehand then it's best to just calculate them before the loop.
k_local = k # magic trick, bind the global k to a local variable k_local to make lookup faster
second_term = (k_local * (k_local + 1)) / 2
while (D != 0 and B <= A):
if (D > 0):
B += 1
D -= B
else:
D += k_local * n + second_term
n += k_local
this has the EXACT same performance as
else:
D += 11299*n+63839350
n += 11299
one thing i can recommend, is to make all variables local, (make k an argument), and use cython to compile your functions to C, this way the compiler overhead is partially removed, and if you really want better performance you can use gmpy's cython "bindings" to manipulate it on C level and remove the interpreter overhead entirely (not the ABI overhead though)
if you really want to go one step further, you can rewrite this in C++ and get more performance, as gmpy is originally a C package, and you will be removing the ABI overhead, do this if you really want to squeeze the last drop of performance out of it, as your "hot loop" is probably too hot for python's virtual machine, that's trying to simulate a computer inside a computer (same to be said for java VM), rewriting this in C++ can be as much as 50 times faster than your current implementation, but the gap can be made as small as only 5 times if you use the previous suggestions.

Bisection method for finding different valued roots in Python

I am trying to implement the bisection method for finding different valued roots. Lets say I have y = 2x + 1, what value of x will give me y = 1.
I am not sure how to phrase the if statement in the while loop. This just converges on 'a' as the answer.
def f(x):
return 2*x + 1
def bisection_method(f, a, b, tol):
if f(a) > 1 and f(b) > 1:
print("No root found.")
else:
iter = 0
while (b - a)/2.0 > tol:
print(a, b, (b-a)/2)
midpoint = (a + b)/2.0
if f(a) < 1:
b = midpoint
else:
a = midpoint
iter += 1
return midpoint, iter
x, its = bisection_method(f, -10, 10, 0.000001)
print(x, its)
To fix your code you sahouls change if to
...
if f(midpoint) > 1:
b = midpoint
else:
a = midpoint
...
But this will only work for increasing functions f, ie that cross 1 from below to above. To handle decreasing functions as well you need to modify the code in a couple of places. Yo ubasically want to find a subinterval on which f switches from being above 1 to below 1, or vice versa
def bisection_method(f, a, b, tol):
if (f(a) - 1) *(f(b) - 1) > 0: # change here
print("No root found.")
else:
iter = 0
while (b - a)/2.0 > tol:
# print(a, b, (b-a)/2)
midpoint = (a + b)/2.0
if (f(a) -1)*(f(midpoint)-1) <= 0: # change here
b = midpoint
else:
a = midpoint
iter += 1
return midpoint, iter

Fib2 function without recursion in Python

I need help for defining the fibanocci 2 function. The fibanocci 2 function is decribed as :
fib2(n) = {0 if n <= 0, 1 if n = 1, 2 if n = 2, ( fib2( n - 1) * fib2( n - 2)) - fib2( n - 3) else}
We need to define this function iterative.
I tried my best but i couldn't write a working code.
def fib2(n: int) -> int:
if n <= 0:
return 0
elif n == 1:
return 1
elif n == 2:
return 2
else:
n = ((n - 1) * (n - 2) - (n - 3)
return n
a = fib2(7)
print (a)
assert (fib2(7) == 37)
the output from this fib2 function is 26 but it should be 37.
Thank you in advance
For the iterative version you have to use a for loop.
And just add the 3 previous numbers to get the next one.
Here is a piece of code:
def fib3(n):
a = 0
b = 1
c = 0
for n in range(n):
newc = a+b+c
a = b
b = c
c = newc
return newc
print(fib3(7))
assert (fib3(7) == 37)
You can not change the value of a parameter.
Please try to return directly :
Return ((fb2(n-1)×fb2(n-2))-fb2(n-3))
So it will work as a recursive function.
def fibo(n):
current = 0
previous_1 = 1
previous_2 = 0
for i in range(1,n):
current = previous_1 + previous_2
previous_2 = previous_1
previous_1 = current
return current
To do it iteratively, the best way is to write it on paper to understand how it works.
Mathematically fibo is Fn = Fn-1 + Fn-2 . Therefore, you can create variables called previous_1 and previous_2 which represents the elements of Fn and you simply update them on each run.
Fn is current

python: codingbat no_teen_sum - why my function isn't working as expected?

Below is the code I used for the no_teen_sum and subsequent fixed_teen functions.
The first code is what I submitted - and worked for all test cases:
def no_teen_sum(a, b, c):
# checks if value is a teen then child conditional checks whether
# fix_teen passes the value, otherwise initialize the value as 0
if 13 <= a <= 19:
if fix_teen(a):
a = a
else:
a = 0
if 13 <= b <= 19:
if fix_teen(b):
b = b
else:
b = 0
if 13 <= c <= 19:
if fix_teen(c):
c = c
else:
c = 0
return a + b + c
And the fix_teen function that is called:
def fix_teen(n):
# checks if n is 15 or 16 but checking if it is found in the set
# written this way to be expandable without becoming verbose
if n in {15, 16}:
return True
However, looking at this I saw a lot of repitition and realized maybe I had misread what the question was asking. It was valid in terms of finding a solution but not as clean as it could be. So I tried to work on an improvement.
Improved code:
def no_teen_sum(a, b, c):
fix_teen(a)
fix_teen(b)
fix_teen(c)
return a + b + c
And the modified fix_teen function:
def fix_teen(n):
# checks if n is a teen
if 13 <= n <= 19:
# checks if n is 15 or 16 but checking if it is found in the set
# if True then leave n as it is
if n in {15, 16}:
n = n
return n
# if it fails then n = 0
else:
n = 0
return n
# if n is not in the teens return it as is
return n
The issue I am having for example a test case of (1, 2, 18) is that it returns 21. It should return 3. I tried putting print statements in between each 'fix_teen' call in the main function to see what value it had for a, b, c and it just left them as is (1, 2, 18) rather than (1, 2, 0)
The weird part is if I called fixed_teen(18) independently it returns 0.
Your no_teen_sum(a, b, c) function is returning a + b + c (which is literally what gets passed to the function)! You should make a, b and c equal to the result from the fix_teen function to get the desired result!
def no_teen_sum(a, b, c):
a = fix_teen(a)
b = fix_teen(b)
c = fix_teen(c)
return a + b + c
def no_teen_sum(a, b, c):
return print(fix_teen(a) + fix_teen(b) + fix_teen(c))
def fix_teen(n):
if n in (13, 14, 17, 18, 19):
return 0
return n
no_teen_sum(1, 2, 3)
no_teen_sum(2, 13, 1)
no_teen_sum(2, 1, 14)
def no_teen_sum(a, b, c):
return fix_teen(a) + fix_teen(b) + fix_teen(c)
def fix_teen(n):
teen = [13, 14, 17, 18, 19]
if n in teen :
return 0
else:
return n

Sum of Two Integers without using '+' operator in python 2 or 3

class Solution(object):
def getSum(self, a, b):
if (a == 0):
return b
if (b == 0):
return a;
while(b != 0):
_a = a ^ b
_b = (a & b) << 1
a = _a
b = _b
return a
But when one of a, b < 0 or both, how the script should be like?
+ operator internally makes a call to __add__(). So, you may directly call a.__add__(b) to get sum. Below is the modified code:
>>> class Solution(object):
... def getSum(self, a, b):
... return a.__add__(b)
...
>>> s = Solution()
>>> s.getSum(1, 2)
3
OR, you may use operator.add(a, b) as:
>>> import operator
>>> operator.add(1, 2)
3
I enjoy solving other people's problems.
class Solution(object):
def getSum(self, a, b):
return sum([a, b])
Edit: If you are in for some fantasy. You can do operator overloading.
class CrappyInt(int):
def __init__(self, num):
self.num = num
def __eq__(self, a):
return self.__add__(a)
m = CrappyInt(3)
n = CrappyInt(4)
print m == n
# 7
This maybe a little hard for you now. But it is fun.
You can also use operator package
import operator
class Solution(object):
def getSum(self, a, b):
return operator.add(a, b)
And if you want to hassle with binary bitwise operations. Here is first method:
def complex_add_1(x, y):
while y != 0:
b = x & y
x = x ^ y
y = b << 1
return x
And here is the another one with recursion:
def complex_add_2(x, y):
if y == 0:
return x
else:
return complex_add_2(x ^ y, (x & y) << 1)
Edit: methods with bitwise operations are working only in Python 3
My old highschool program that I found on my usb stick. Yes it's crude but it works.
def s(a,b):
a = bin(a)[2:]
b = bin(b)[2:]
c_in = 0
value = ''
if not len(a) == len(b):
to_fill = abs(len(a) - len(b))
if len(a) > len(b):
b = b.zfill(len(a))
else:
a = a.zfill(len(b))
for i,j in zip(reversed(a),reversed(b)):
i_xor_j = int(i) ^ int(j)
i_and_j = int(i) and int(j)
s = int(c_in) ^ int(i_xor_j)
c_in_and_i_xor_j = int(c_in) and int(i_xor_j)
c_in = int(i_and_j) or int(c_in_and_i_xor_j)
value += str(int(s))
value += str(int(c_in))
return int(value[::-1],2)
print(s(5,9))
#>> 14
Or I would have see if I could cheat with sum.
This was kinda fun :) I'm not sure if this is what you're after, but these all should work so long as you're using integers
def subtract(a, b):
if a < 0:
return negative(add(negative(a), b))
if b < 0:
return add(a, negative(b))
if a < b:
return negative(subtract(b, a))
if b == 0:
return a
return subtract(a^b, (~a & b) << 1)
def negative(a):
if a == 0: return 0
a = ~a
b = 1
while b > 0:
a, b = a^b, (a&b) << 1
return a
def add(a, b):
if a < 0:
return negative(subtract(negative(a), b))
if b < 0:
return subtract(a, negative(b))
if b == 0:
return a
return add(a^b, (a & b) << 1)
def multiply(a, b):
if a == 0 or b == 0:
return 0
if b < 0:
a = negative(a)
b = negative(b)
A = 0
while b > 0:
A = add(A, a)
b = subtract(b, 1)
return A
def div(a, b):
if b == 0:
raise ZeroDivisionError
if a == 0:
return 0
if b < 0:
a = negative(a)
b = negative(b)
A = 0
if a < 0:
while a < 0:
a = add(a, b)
A = subtract(A, 1)
else:
while b < a :
a = subtract(a, b)
A = add(A, 1)
return A
def mod(a, b):
return subtract(a, multiply(div(a, b),b))
negation by two's compliment is used to get a function where a and b are both greater than 0 and the correct operation (addition or subtraction) is selected. further with subtraction, care is taken to not yield a negative result. This is done because the recursive definitions of add and subtract hate crossing 0. Also in this case the recursive add and subtract do the exact same thing as your loop.

Categories