Variable not properly updating when searching for consecutive primes - python

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.

Related

How do I write a function that finds the sum of factorial of even numbers?

I've got a question- how do I write a function sum_even_factorials that finds the sum of the factorials of the even numbers that are less than or equal to n.
Eg:
sum_even_factorials(1)=
1
sum_even_factorials (3)=
3
sum_even_factorials (6)=
747
This is my current code:
Is there a logical error in the current code?
Is there a logical error in the current code?
To begin with, function sum_even_factorial doesn't return a value in every execution flow:
if cond1:
return val1
elif cond2:
return val2
else: # this part is missing in your code
return val3
In addition, note that when you call this function, you are not doing anything with the value that it returns:
sum_even_factorial(6)
Finally, although parts of your code are not visible in your question, I tend to guess that you cannot recursively compute the factorials of even numbers the way you did it, because the factorial of an even number n depends on the factorial of the odd number n - 1.
I think the code needs a loop. I'd write it like so:
def factorial(n):
if n == 0 or n == 1:
return 1;
else:
return n * factorial(n-1)
def sum_even_factorial(n):
current_sum = 0
while n >= 0:
if n % 2 == 0:
current_sum += factorial(n)
n -= 1
return current_sum
print(sum_even_factorial(6))
If you return tuples from the function, you can do it with one single function. See the comments in the code on how it works ...
def factorial_with_sum(n):
if n < 2:
return 1, 0 # first item of the tuple is the factorial, second item is the sum
else:
f, s = factorial_with_sum(n - 1) # calc factorial and sum for n - 1
f = f * n # factorial = n * factorial (n - 1)
if n % 2 == 0:
s = s + f # if n is even, add the current factorial to the sum
return f, s
fact, sum = factorial_with_sum(6)
print(fact)
print(sum)
You can also do it iteratively with a simple for loop as follows
def factorial_with_sum_iterative(n):
s = 0 # initialize sum
f = 1 # and factorial
for i in range(2, n + 1): # iterate from 2 to n
f = f * i # calculate factorial for current i
if i % 2 == 0:
s = s + f # if current i is even, add it to sum
return f, s

How to tell if number can be writen as sum of n different squares?

I know how to check if the number can be represented as the sum of two squares with a brute-force approach.
def sumSquare( n) :
i = 1
while i * i <= n :
j = 1
while(j * j <= n) :
if (i * i + j * j == n) :
print(i, "^2 + ", j , "^2" )
return True
j = j + 1
i = i + 1
return False
But how to do it for n distinct positive integers. So the question would be:
Function which checks if the number can be written as sum of 'n' different squares
I have some examples.
For e.g.
is_sum_of_squares(18, 2) would be false because 18 can be written as the sum of two squares (3^2 + 3^2) but they are not distinct.
(38,3) would be true because 5^2+3^2+2^2 = 38 and 5!=3!=2.
I can't extend the if condition for more values. I think it could be done with recursion, but I have problems with it.
I found this function very useful since it finds the number of squares the number can be split into.
def findMinSquares(n):
T = [0] * (n + 1)
for i in range(n + 1):
T[i] = i
j = 1
while j * j <= i:
T[i] = min(T[i], 1 + T[i - j * j])
j += 1
return T[n]
But again I can't do it with recursion. Sadly I can't wrap my head around it. We started learning it a few weeks ago (I am in high school) and it is so different from the iterative approach.
Recursive approach:
def is_sum_of_squares(x, n, used=None):
x_sqrt = int(x**0.5)
if n == 1:
if x_sqrt**2 == x:
return used.union([x_sqrt])
return None
used = used or set()
for i in set(range(max(used, default=0)+1, int((x/n)**0.5))):
squares = is_sum_of_squares(x-i**2, n-1, used.union([i]))
if squares:
return squares
return None
Quite a compelling exercise. I have attempted solving it using recursion in a form of backtracking. Start with an empty list, run a for loop to add numbers to it from 1 to max feasible (square root of target number) and for each added number continue with recursion. Once the list reaches the required size n, validate the result. If the result is incorrect, backtrack by removing the last number.
Not sure if it is 100% correct though. In terms of speed, I tried it on the (1000,13) input and the process finished reasonably fast (3-4s).
def is_sum_of_squares(num, count):
max_num = int(num ** 0.5)
return backtrack([], num, max_num, count)
def backtrack(candidates, target, max_num, count):
"""
candidates = list of ints of max length <count>
target = sum of squares of <count> nonidentical numbers
max_num = square root of target, rounded
count = desired size of candidates list
"""
result_num = sum([x * x for x in candidates]) # calculate sum of squares
if result_num > target: # if sum exceeded target number stop recursion
return False
if len(candidates) == count: # if candidates reach desired length, check if result is valid and return result
result = result_num == target
if result: # print for result sense check, can be removed
print("Found: ", candidates)
return result
for i in range(1, max_num + 1): # cycle from 1 to max feasible number
if candidates and i <= candidates[-1]:
# for non empty list, skip numbers smaller than the last number.
# allow only ascending order to eliminate duplicates
continue
candidates.append(i) # add number to list
if backtrack(candidates, target, max_num, count): # next recursion
return True
candidates.pop() # if combination was not valid then backtrack and remove the last number
return False
assert(is_sum_of_squares(38, 3))
assert(is_sum_of_squares(30, 3))
assert(is_sum_of_squares(30, 4))
assert(is_sum_of_squares(36, 1))
assert not(is_sum_of_squares(35, 1))
assert not(is_sum_of_squares(18, 2))
assert not(is_sum_of_squares(1000, 13))

Python number in range

I can't seem to understand why the function fix_teen returns 13 and not 0.
As we have r in the range of 13-19, we check if n in r, it even returns "True" when outside the if/else function.
I also tried it this way if 13 <= n <= 19: and the issue still persists.
def fix_teen(n):
r = range(13,19)
if n in r :
return n == 0
elif n == 15 or n ==16:
return n
else :
return n
def no_teen_sum(a, b, c):
print(fix_teen(a))
print(fix_teen(b))
print(fix_teen(c))
print(a+b+c)
no_teen_sum(2, 13, 1)
The range() function returns a sequence of numbers, starting from 0 by default, and increments by 1 (by default), and stops before the specified number.
range(start, stop)
for example to create a sequence of numbers from 0 to 5, and print each item in the sequence:
x = range(6)
for n in x:
print(n)
so r = range(13,19) has to be replaced with r = range(13,20)
The bit of code below, returns the result of an equality operator (n == 0).
if n in r :
return n == 0
If you want to return 0 if the n is in the range, simply return 0:
if n in r:
return 0

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.

Recursive function that returns the remainder

I am instructed to define a recursive function in Python that finds the remainder of n divided by b with the condition to not use the "/" ,"%" or "//" operator. I have defined the following function, which works fine for positive numbers. Is there a better way to do this using recursion and simple conditions.
def division(n, b, q = 1):
"""
parameters : a et b (integers)
returns: the remainder of a and b
pre-requisites : q = 1
"""
if n <= 0 or n < b:
if n == 0:
print("Your division has no remainder.")
elif n in range(0,5):
print("Your remainder is", n)
return 0
else:
return division(n - b, b, q) + q
print(division(274,5))
I believe your teacher was probably only trying to go for remainders without quotients.
def division(n, b):
if n < b:
return n
return division(n - b, b)
print(division(274, 5))
However, since you brought it up, you can do it with the quotient, without having to start with a 1 for the default.
def division(n, b, q = 0):
if n < b:
return n, q
return division(n - b, b, q + 1)
print(division(274, 5))
Main takeaways, you do not need to check n for range (0,5).
What about
def remainder(n, q):
if(n < q):
return n
return remainder(n - q, q)
print(remainder(274, 5)) # will return: 4
print(remainder(275, 5)) # will return: 0
print(remainder(123, 3)) # will return: 0
much shorter ...

Categories

Resources