How can i make this factorial function recursive? - python

A function that calculates (n-1)! , but with steps.
def function1(n, step):
result = 1
for i in range(1, n, step):
result *= i
return result
I'm not allowed to add any more parameters and I need to make it recursive.
I've tried this:
def function2(n, step):
if n < 0:
return 1
return n * function2(n-step, step)
But for let's say:
function2(6,3)
it wouldn't work, the first function will give me 1 * 4
and the second one would give me 6 * 3 * 1
I don't know how to make it work with the step argument.
Edit:
Some more samples:
First function
function1(13, 3) == 280
function1(11, 3) == 280
function1(6, 3) == 4
function1(11, 2) == 945
function1(8, 2) == 105
function1(4, 2) == 3
More sample:
function1(12, 3) == 280
function1(5, 2) == 3
function1(5, 3) == 4
Second function (same values):
function2(13, 3) == 3640
function2(11, 3) == 880
function2(6, 3) == 0
function2(11, 2) == 10395
function2(8, 2) == 0
function2(4, 2) == 0
Edit2: Some more clarifications: The function computes (n-1)!, but with steps. The step argument would just "step over" or "skip" some numbers (e.g.: function1(12, 3) should compute 1*4*7*10, like with the step argument from range(), cause it's used in the first function)
Thank you!

The obvious difference is that you are building range starting at 1 and counting up to n by step, and in the recursive example you are starting at n and counting down by step. This will result in different numbers being multiplied.
Because you are required not to use any additional function parameters, I would suggest an inner helper function, loop -
def fact (n, step):
def loop (m):
if m >= n:
return 1
else:
return m * loop(m + step)
return loop(1)
If you don't want to use a helper function like loop above, you are constrained to complex modulus arithmetic -
def fact (n, step):
if n % step != 1:
return fact(n + 1, step)
elif n < step:
return 1
else:
return (n - step) * fact(n - step, step)
No matter which way you shake it, the modulus operation for this problem is messy -
def fact (n, step):
q = (n - 1) % step
if q:
return fact(n + step - q, step)
elif n < step:
return 1
else:
return (n - step) * fact(n - step, step)
Once academic constraints like "do not use additional parameters" go away, you can multiply the ascending series in a more familiar way -
def fact (n, step, m = 1):
if m >= n:
return 1
else:
return m * fact(n, step, m + step)
All variations of fact above produce identical output -
print(fact(13, 3) == 280) # True
print(fact(11, 3) == 280) # True
print(fact(6, 3) == 4) # True
print(fact(11, 2) == 945) # True
print(fact(8, 2) == 105) # True
print(fact(4, 2) == 3) # True
print(fact(5, 2) == 3) # True
print(fact(5, 3) == 4) # True

Because the steps is calculated beginnig with 1 You have to normalize n to be a multiple of step plus 1 before you begin
And you can cheat the number of arguments by setting the steps negative on recursive calls.
def function2(n, step):
if n <= 1:
return 1
if step > 0:
n = n - 2
n = n - n % step + 1
step = -step
return n * function2(n + step, step)

Related

Why classical division ("/") for large integers is much slower than integer division ("//")?

I ran into a problem: The code was very slow for 512 bit odd integers if you use classical division for (p-1)/2. But with floor division it works instantly. Is it caused by float conversion?
def solovayStrassen(p, iterations):
for i in range(iterations):
a = random.randint(2, p - 1)
if gcd(a, p) > 1:
return False
first = pow(a, int((p - 1) / 2), p)
j = (Jacobian(a, p) + p) % p
if first != j:
return False
return True
The full code
import random
from math import gcd
#Jacobian symbol
def Jacobian(a, n):
if (a == 0):
return 0
ans = 1
if (a < 0):
a = -a
if (n % 4 == 3):
ans = -ans
if (a == 1):
return ans
while (a):
if (a < 0):
a = -a
if (n % 4 == 3):
ans = -ans
while (a % 2 == 0):
a = a // 2
if (n % 8 == 3 or n % 8 == 5):
ans = -ans
a, n = n, a
if (a % 4 == 3 and n % 4 == 3):
ans = -ans
a = a % n
if (a > n // 2):
a = a - n
if (n == 1):
return ans
return 0
def solovayStrassen(p, iterations):
for i in range(iterations):
a = random.randint(2, p - 1)
if gcd(a, p) > 1:
return False
first = pow(a, int((p - 1) / 2), p)
j = (Jacobian(a, p) + p) % p
if first != j:
return False
return True
def findFirstPrime(n, k):
while True:
if solovayStrassen(n,k):
return n
n+=2
a = random.getrandbits(512)
if a%2==0:
a+=1
print(findFirstPrime(a,100))
As noted in comments, int((p - 1) / 2) can produce garbage if p is an integer with more than 53 bits. Only the first 53 bits of p-1 are retained when converting to float for the division.
>>> p = 123456789123456789123456789
>>> (p-1) // 2
61728394561728394561728394
>>> hex(_)
'0x330f7ef971d8cfbe022f8a'
>>> int((p-1) / 2)
61728394561728395668881408
>>> hex(_) # lots of trailing zeroes
'0x330f7ef971d8d000000000'
Of course the theory underlying the primality test relies on using exactly the infinitely precise value of (p-1)/2, not some approximation more-or-less good to only the first 53 most-significant bits.
As also noted in a comment, using garbage is likely to make this part return earlier, not later:
if first != j:
return False
So why is it much slower over all? Because findFirstPrime() has to call solovayStrassen() many more times to find garbage that passes by sheer blind luck.
To see this, change the code to show how often the loop is trying:
def findFirstPrime(n, k):
count = 0
while True:
count += 1
if count % 1000 == 0:
print(f"at count {count:,}")
if solovayStrassen(n,k):
return n, count
n+=2
Then add, e.g.,
random.seed(12)
at the start of the main program so you can get reproducible results.
Using floor (//) division, it runs fairly quickly, displaying
(6170518232878265099306454685234429219657996228748920426206889067017854517343512513954857500421232718472897893847571955479036221948870073830638539006377457, 906)
So it found a probable prime on the 906th try.
But with float (/) division, I never saw it succeed by blind luck:
at count 1,000
at count 2,000
at count 3,000
...
at count 1,000,000
Gave up then - "garbage in, garbage out".
One other thing to note, in passing: the + p in:
j = (Jacobian(a, p) + p) % p
has no effect on the value of j. Right? p % p is 0.

Variable not properly updating when searching for consecutive primes

Consider the following script:
Python
def f(a, b, n):
return (n ** 2) + (a * n) + b
def prime_check(num):
for i in range(2, (num // 2) + 1):
if num % i == 0:
return False
return True
num_primes = []
coefficients = []
for a in range(-999, 1000, 1):
for b in range(-1000, 1001, 1):
n = 0
coefficients.append((a, b))
while True:
result = prime_check(f(a, b, n))
if result:
n += 1
continue
else:
num_primes.append(n - 1)
break
print(f"num_primes: {num_primes[-1]} coefficients: {coefficients[-1]}")
The algorithm above is meant to search values of |a| < 1000 , |b| <= 1000, for function f(a, b, n), where n = 0 to start, and increments if f(a, b, n) returns a prime number. It keeps incrementing n and checking for primes until f returns a non-prime.
At this point, n - 1 is appended to num_primes to reflect the number of primes this set of coefficients (a, b) produced for consecutive values of n.
When I run this code, the print statement at the end of the inner for loop shows num_primes values are stuck alternating between whatever value b is for the current iteration and 0, rather than the proper number of primes for the coefficients.
I'm not sure where I went wrong here.
As noted by #JohanC, when prime_check(num) was given a negative number, it would return True no matter what the number was.
The fix for this was to make a simple change to prime_check(num) as shown below.
Python
def prime_check(num):
for i in range(2, (abs(num) // 2) + 1):
if num % i == 0:
return False
return True
By calculating abs(num) before division, we eliminate the bad behavior when num < 0.

writing recursive function to compute no. of graphs

I am trying to write a code in python, to calculate the maximum graphs sets by using this formula for n vertices:
2**(n(n-1)/2) (i hope i wrote it correctly)
i am trying to do it in the lowest complexity/running time buy using % of 1000000007- is recursive the right way? or iterative?
I've read the Wikipedia article regarding exponential squaring- but couldn't make the leap from there to my problem :(
after some pen and pen and paper work I've discovered that in this case- n(n-1)/2 is always even - so i am removing the block that deals with odd n values.
This is the code a wrote so far for the recursion-
x for the base (2) and n for the number of vertices:
def graphs_num(x, n):
n = (n*n-n)/2
if n == 0:
return 0
elif n == 1:
return 1
else:
y = graphs_num(x, n/2)
return y*y
no results so far -can you assist please?
2nd edit: (i forgot the x in the last line)
here's #alec's code:
def count(n, total=1):
n = (n*n-1)/2
if n < 2:
return total
total *= 2 ** (n - 1) % 1000000007
return count(x, n-1, total)
count(4)
now I am looking to have x already defined as 2- so the only input need by the function will be n
I'm not completely familiar with this problem but based on the formula you gave this will return the same result for n >= 0 (I assume you wouldn't have a negative number of vertices).
def count(n, total=1):
if n < 2:
return total
total *= 2 ** (n - 1)
return count(n-1, total)
>>> count(5)
1024
Edit (explanation):
Basically you will notice that the values increase by a factor of 2**n.
>>> [2 ** (n * (n-1) // 2) for n in range(1, 6)]
[1, 2, 8, 64, 1024]
This can be illustrated by the following:
f(1) = 1 = 2**0
f(2) = 2 = 2**1 * 2**0
f(3) = 8 = 2**2 * 2**1 * 2**0
...
And simplified to show how it might be used recursively:
f(1) = 1 = 2**0
f(2) = 2 = 2**1 * f(1)
f(3) = 8 = 2**2 * f(2)
...
By this point, it is easy to notice the relationship is f(n) = 2**(n-1) * f(n-1). In the recursive function, the lines underneath the base case do exactly this:
total *= 2 ** (n - 1)
return count(n-1, total)
I multiple the total by 2**(n-1), and recurse to the next value for n, remembering to decrement by 1. The base case ensures we bottom out below 2, and returns the total.
Now if you want to change the base of the exponent and use a mod, it will behave accordingly.
def count(x, n, total=1):
if n < 2:
return total
total *= x ** (n - 1) % 1000000007
return count(x, n-1, total)
>>> count(2, 3)
8
>>> count(3, 3)
27

How to write 2**n - 1 as a recursive function?

I need a function that takes n and returns 2n - 1 . It sounds simple enough, but the function has to be recursive. So far I have just 2n:
def required_steps(n):
if n == 0:
return 1
return 2 * req_steps(n-1)
The exercise states: "You can assume that the parameter n is always a positive integer and greater than 0"
2**n -1 is also 1+2+4+...+2n-1 which can made into a single recursive function (without the second one to subtract 1 from the power of 2).
Hint: 1+2*(1+2*(...))
Solution below, don't look if you want to try the hint first.
This works if n is guaranteed to be greater than zero (as was actually promised in the problem statement):
def required_steps(n):
if n == 1: # changed because we need one less going down
return 1
return 1 + 2 * required_steps(n-1)
A more robust version would handle zero and negative values too:
def required_steps(n):
if n < 0:
raise ValueError("n must be non-negative")
if n == 0:
return 0
return 1 + 2 * required_steps(n-1)
(Adding a check for non-integers is left as an exercise.)
To solve a problem with a recursive approach you would have to find out how you can define the function with a given input in terms of the same function with a different input. In this case, since f(n) = 2 * f(n - 1) + 1, you can do:
def required_steps(n):
return n and 2 * required_steps(n - 1) + 1
so that:
for i in range(5):
print(required_steps(i))
outputs:
0
1
3
7
15
You can extract the really recursive part to another function
def f(n):
return required_steps(n) - 1
Or you can set a flag and define just when to subtract
def required_steps(n, sub=True):
if n == 0: return 1
return 2 * required_steps(n-1, False) - sub
>>> print(required_steps(10))
1023
Using an additional parameter for the result, r -
def required_steps (n = 0, r = 1):
if n == 0:
return r - 1
else:
return required_steps(n - 1, r * 2)
for x in range(6):
print(f"f({x}) = {required_steps(x)}")
# f(0) = 0
# f(1) = 1
# f(2) = 3
# f(3) = 7
# f(4) = 15
# f(5) = 31
You can also write it using bitwise left shift, << -
def required_steps (n = 0, r = 1):
if n == 0:
return r - 1
else:
return required_steps(n - 1, r << 1)
The output is the same
Have a placeholder to remember original value of n and then for the very first step i.e. n == N, return 2^n-1
n = 10
# constant to hold initial value of n
N = n
def required_steps(n, N):
if n == 0:
return 1
elif n == N:
return 2 * required_steps(n-1, N) - 1
return 2 * required_steps(n-1, N)
required_steps(n, N)
One way to get the offset of "-1" is to apply it in the return from the first function call using an argument with a default value, then explicitly set the offset argument to zero during the recursive calls.
def required_steps(n, offset = -1):
if n == 0:
return 1
return offset + 2 * required_steps(n-1,0)
On top of all the awesome answers given earlier, below will show its implementation with inner functions.
def outer(n):
k=n
def p(n):
if n==1:
return 2
if n==k:
return 2*p(n-1)-1
return 2*p(n-1)
return p(n)
n=5
print(outer(n))
Basically, it is assigning a global value of n to k and recursing through it with appropriate comparisons.

Alternating counters in Python (Fibonacci Plot)

I've been assigned a project in my computing class to do a report on some area of mathematics in LaTeX, using Python 2.7 code - I chose the Fibonacci sequence.
As part of my project I wanted to include a plot of the Fibonacci 'spiral' which is actually comprised of a series of quarter-circles of increasing radii. As such, I've tried to define a function to give a loop that returns the centres of these quarter-circles so I can create a plot. Using pen and paper I have found the centres of each quarter-circle and noticed that with each new quarter-circle there's an exchange of coordinates - ie. if n is even, the x-coordinate of the previous centre remains the x-coordinate for the nth centre; similarly, when n is odd, the y-coordinate remains the same.
My problem arises with the other coordinate. They work on an alternating pattern of + or - the (n-2)th Fibonacci number to the y-coordinate (for even n) or x-coordinate (for odd) of the previous centre.
I've created the following loop in SageMathCloud, but I think I've deduced that my counters aren't incrementing when I wanted them to:
def centrecoords(n):
k = 0
l = 1
if fib(n) == 1:
return tuple((0,-1))
elif n % 2 == 0 and k % 2 == 0:
return tuple((centrecoords(n-1)[0], centrecoords(n-1)[1] + ((-1) ** k) * fib(n - 2)))
k += 1
elif n % 2 == 0:
return tuple((centrecoords(n-1)[0], centrecoords(n-1)[1] + ((-1) ** k) * fib(n - 2)))
elif n % 2 != 0 and l % 2 == 0:
return tuple((centrecoords(n-1)[0] + ((-1) ** l) * fib(n - 2), centrecoords(n-1)[1]))
l += 1
else:
return tuple((centrecoords(n-1)[0] + ((-1) ** l) * fib(n - 2), centrecoords(n-1)[1]))
cen_coords = []
for i in range(0, 21):
cen_coords.append(centrecoords(i))
cen_coords
Any help in making the k counter increment with its if statement only, and the same with the l counter would be greatly appreciated.
Your problem is that k and l are local variables. As such they are lost every time the function exits, and re-start at zero and one respectively when is called again (yes, even when it's called from itself).
Nick's code aims to store a single instance each of k and l in the top-level function, sharing them with the recursive calls.
Another reasonable approach might be to rewrite your recursion as a loop, and yield the sequence. This makes it trivial to keep the state of k and l, as your locals are preserved.
Or, you could re-write your function as a class method, and make k and l instance variables. This behaves similarly, with the instance storing your intermediate state between calls to centrecoords.
Apart from all of these, your code looks like it requires each call to centrecoords to receive the next value of n. So, even if you fix the state problem, this is a poor design.
I'd suggest going the generator route, and taking a single argument, the maximum value of n. Then you can iterate over range(n), yielding each result in turn. Note also that your only recursive call is for n-1, which is just your previous iteration, so you can simply remember it.
Quick demo: I haven't tested this, or checked the corner cases ...
def fib(n):
if n < 2:
return 1
return fib(n-1) + fib(n-2)
def centrecoords(max_n):
# initial values
k = 0
l = 1
result=(0,-1)
# note fib(0) == fib(1) == 1
for n in range(2,max_n):
if n % 2 == 0:
result = (result[0], result[1] + ((-1) ** k) * fib(n - 2))
yield result
if k % 2 == 0:
k += 1
else:
result = (result[0] + ((-1) ** l) * fib(n - 2), result[1])
yield result
if l % 2 == 0:
l += 1
cen_coords = list(centrecoords(21))
Expanding on my comment. Your code could look something like the one below. But please not that you might need to adjust starting values of k and l to -1 and 0 correspondingly, because k and l are incremented before recursion calls (opposite to your code which implied that first a recursion is called and only then k and l are increased).
I also deleted tuple, it is unnecessary in python and hard to read, to create a tuple use comma syntax, e.g.: 1, 2.
Also n == 0 (fib(n) == 0) should be considered as special case, or you program will enter infinite recursion and crash when centrecoords called with n=0.
I have no account on SageMathCloud to test it, but it at least should fix counters increment.
def centrecoords(n, k=0, l=1):
if n == 0:
return 0, 0 # this is pure guess and most likely incorrect, but n == 0 (or fib(n) == 0 should be handled separatly)
if fib(n) == 1:
return 0, -1
elif n % 2 == 0 and k % 2 == 0:
k += 1
return centrecoords(n-1, k, l)[0], centrecoords(n-1, k, l)[1] + ((-1) ** k) * fib(n - 2)
elif n % 2 == 0:
return centrecoords(n-1, k, l)[0], centrecoords(n-1, k, l)[1] + ((-1) ** k) * fib(n - 2)
elif n % 2 != 0 and l % 2 == 0:
l += 1
return centrecoords(n-1, k, l)[0] + ((-1) ** l) * fib(n - 2), centrecoords(n-1, k, l)[1]
else:
return centrecoords(n-1, k, l)[0] + ((-1) ** l) * fib(n - 2), centrecoords(n-1, k, l)[1]
cen_coords = []
for i in range(0, 21):
cen_coords.append(centrecoords(i))
cen_coords

Categories

Resources