How to optimize and find output for large inputs? - python

For an input number N, I am trying to find the count of numbers of special pairs (x,y) such that the following conditions hold:
x != y
1 <= N <= 10^50
0 <= x <= N
0 <= y <= N
F(x) + F(y) is prime where F is sum of all digits of the number
finally print the output of the count modulo 1000000007
Sample Input: 2
Sample Output: 2
Explanation:
(0,2) Since F(0)+F(2)=2 which is prime
(1,2) Since F(1)+F(2)=3 which is prime
(2,1) is not considered as (1,2) is same as (2,1)
My code is:
def mod(x,y,p):
res=1
x=x%p
while(y>0):
if((y&1)==1):
res=(res*x)%p
y=y>>1
x=(x*x)%p
return res
def sod(x):
a=str(x)
res=0
for i in range(len(a)):
res+=int(a[i])
return res
def prime(x):
p=0
if(x==1 or x==0):
return False
if(x==2):
return True
else:
for i in range(2,(x//2)+1):
if(x%i==0):
p+=1
if(p==0):
return (True)
else:
return(False)
n=int(input())
res=[]
for i in range (n+1):
for j in range(i,n+1):
if(prime(sod(int(i))+sod(int(j)))):
if([i,j]!=[j,i]):
if([j,i] not in res):
res.append([i,j])
count=len(res)
a=mod(count,1,(10**9)+7)
print(res)
print(a)
I expect the output of 9997260736 to be 671653298 but the error is code execution timed out.

Already posted a bit too long comments, so changing it to answer:
When thinking of such problems, don't translate the problem directly to code but see what can you do only once or in different order.
As of now, you're doing N*N passes, each time calculating a sum of digits for x and y (not that bad) AND factoring each sum to check whether it's prime (really bad). That means for sum s you're checking whether it's prime s+1 times! (for 0+s, 1+(s-1), ..., (s-1)+1, s+0).
What you can do differently?
Let's see what we know:
Sum of digits is the same for many numbers.
Sum of sod(x) and sod(y) is the same for many values.
Number is prime during its 1st and nth check (and checking whether it's prime is costly).
So the best would be to calculate prime numbers only once, and each of the sum only once. But how to do that when we have many numbers?
Change the direction of thinking: get the prime number, split it into two numbers (sodx and sody), then generate x and y from those numbers.
Example:
Prime p = 7. That give possible sums as 0+7, 1+6, 2+5, 3+4.
Then for each sum you can generate a number, e.g. for N=101 and sod=1, you have 1, 10, 100, and for sod=2 you have 2, 11, 20, 101. You can possibly store this, but generating this should not be that bad.
Other optimisation:
You have to think how to limit generating prime numbers using your N:
given N with lenN digits (remember, lenN is ~log(N)), the biggest sum of digits possible is 9*lenN (for N consisting of only 9's). That means our sodx and sody are <= 9*lenN, so prime p = sodx + sody <= 18*lenN
Look: that means 18*lenN checking for whether number is prime vs N*N checks your previous algorithm had!

Related

Finding if a number is a perfect square through sum of odd numbers

I'm stuck in this Python exercise:
A number n is a perfect square if, for some natural number k, it can be written as the sum of the first k odd numbers, that is, 1 + 3 +· · ·+k. The first five perfect squares are 1, 4, 9, 16 and 25. Write a function perfect_square(n) whose result is True, if n is a perfect square. Otherwise, the result must be False.
This is the solution I came up with:
def perfect_square(n):
s=0
for i in range(1, n+1,2):
s=s+i
if s==n:
print(True)
else:
print(False)
When I tested the numbers of the question, 1 and 4 were correct, but from 9 the result is "False" when it should be True. Can someone please help me find what is wrong with my code? This is for an introductory class, so this is supposed to have a simple solution.
Thank you in advance!
for some natural number k
You're not given an exact k but we know the upper bound couldn't be bigger than n so just check them all.
def perfect_square(n):
summation = 0
for value in range(1, n + 1, 2):
summation += value
if summation == n:
return True
return False

Shorter way to test if prime number

To test if a number is prime, I do:
def isprime(n):
if n < 2: return False
for i in range(2, n):
if n % i == 0:
return False
else:
return True
I wonder how to make that more efficient/shorter to write.
With generator:
def isprime(n):
return (all(False for i in range(2,n) if n % i == 0) and not n < 2)
print (isprime(0))
print (isprime(1))
print (isprime(2))
print (isprime(3))
print (isprime(9))
print (isprime(10))
print (isprime(13))
Output:
False
False
True
True
False
False
True
Alongside #Théophile's suggestion, I'd add the following recommendations:
Test whether a number is even and greater than 2 before even calling is_prime (eliminating the need for a function call).
Instead of range(2, n), use range(3, n, 2). This will consider only odd numbers; the third parameters of range is the step by which you'll increment.
Instead of looping through all the integers (or all the odd integers) less than the square root of n, create a cache of the prime numbers you've already found and loop through them. One of the fastest and most elegant ways to do this is using functools.lru_cache, but it will suffice simply to write provide a cache yourself.
Here's a quick and dirty way of doing this that is longer but more efficient than your original proposal:
from math import sqrt
# We seed the cache with the first two odd primes because (1) it's low-
# hanging fruit and (2) we avoid the need to add extra lines of code (that
# will increase execution time) for cases in which the square roots of numbers
# are less than any primes in the list
odd_primes = [3, 5]
def is_prime(n):
# If n is not prime, it must have at least one factor less than its square
# root.
max_prime_factor_limit = int(sqrt(n))
# use a generator here to avoid evaluating all the qualifying values in
# odd_primes until you need them. For example, the square root of 27 is
# 5.1962, but, since it's divisible by 3, you'll only test if 27 is prime
# one time. Not a big deal with smaller integers, but the time to compute
# the next prime will increase pretty fast as there are more potential
# factors.
available_primes = (p for p in odd_primes if p <= max_prime_factor_limit)
for prime in available_primes:
if n % prime == 0:
return False
return True
for i in range(7, 99, 2):
# if no prime factors were found, add to cache
if is_prime(i):
odd_primes.append(i)
print(odd_primes)
There are additional things you can do to speed this up. The one that immediately springs to mind is, instead of calculating the square root of each number you're checking, use the squares of the primes to determine the upper limit of the set of primes you'll check. In other words, if you know that the square of 169 is 13, so you know that any number greater than 169 and less than 289 (the square of 17) will have a prime factor <= 13. You can also use that to save time by calculating the list of prime factors and passing the list to the function you're using to determine if an integer is prime. Note, of course, that this will only work if you're actually creating a list of primes.
number = int(input('please enter a number:'))
if number>1:
for numbers in range(2, number):
if (number % numbers) ==0:
print(f"{number} is not a prime number")
break
else:
print(f"{number} is a prime number")
else:
print(f"{number} is not a prime number")

How does this python loop check for primality, if it loops less than the number n?

Hi guys so I was wondering how is this code:
def is_prime(n):
for i in range(2, int(n**.5 + 1)):
if n % i == 0:
return False
return True
able to check for prime when on line 2: for i in range(2, int(n**.5 + 1)): the range is not : range(2, n)? Shouldn't it have to iterate through every number till n but excluding it? This one is not doing that but somehow it works... Could someone explain why it works please.
The loop iterates on all numbers from 2 to the square root on n. For any divisor it could find above that square root (if it continued iterating to n - 1), there would obviously be another divisor below it.
Because the prime factorisation of any number n (by trial division) needs only check the prime numbers up to sqrt(n)
.. Furthermore, the trial factors need go no further than sqrt(n)
because, if n is divisible by some number p, then n = p × q and
if q were smaller than p, n would have been detected earlier as
being divisible by q or by a prime factor of q.
On a sidenote, trial division is slow to check for primality or possible primality. There are faster probabilistic tests like the Miller-Rabin test which can check quickly if a number is composite or probably prime.

Sum of powers for lists of tuples

My assignment is to create a function to sum the powers of tuples.
def sumOfPowers(tups, primes):
x = 0;
for i in range (1, len(primes) + 1):
x += pow(tups, i);
return x;
So far I have this.
tups - list of one or more tuples, primes - list of one or more primes
It doesn't work because the inputs are tuples and not single integers. How could I fix this to make it work for lists?
[/edit]
Sample output:
sumOfPowers([(2,3), (5,6)], [3,5,7,11,13,17,19,23,29]) == 2**3 + 5**6
True
sumOfPowers([(2,10**1000000 + 1), (-2,10**1000000 + 1), (3,3)], primes)
27
Sum of powers of [(2,4),(3,5),(-6,3)] is 2^4 + 3^5 + (−6)^3
**The purpose of the prime is to perform the computation of a^k1 + ... a^kn modulo every prime in the list entered. (aka perform the sum computation specified by each input modulo each of the primes in the second input list, then solve using the chinese remainder theorem )
Primes list used in the example input:
15481619,15481633,15481657,15481663,15481727,15481733,15481769,15481787
,15481793,15481801,15481819,15481859,15481871,15481897,15481901,15481933
,15481981,15481993,15481997,15482011,15482023,15482029,15482119,15482123
,15482149,15482153,15482161,15482167,15482177,15482219,15482231,15482263
,15482309,15482323,15482329,15482333,15482347,15482371,15482377,15482387
,15482419,15482431,15482437,15482447,15482449,15482459,15482477,15482479
,15482531,15482567,15482569,15482573,15482581,15482627,15482633,15482639
,15482669,15482681,15482683,15482711,15482729,15482743,15482771,15482773
,15482783,15482807,15482809,15482827,15482851,15482861,15482893,15482911
,15482917,15482923,15482941,15482947,15482977,15482993,15483023,15483029
,15483067,15483077,15483079,15483089,15483101,15483103,15483121,15483151
,15483161,15483211,15483253,15483317,15483331,15483337,15483343,15483359
,15483383,15483409,15483449,15483491,15483493,15483511,15483521,15483553
,15483557,15483571,15483581,15483619,15483631,15483641,15483653,15483659
,15483683,15483697,15483701,15483703,15483707,15483731,15483737,15483749
,15483799,15483817,15483829,15483833,15483857,15483869,15483907,15483971
,15483977,15483983,15483989,15483997,15484033,15484039,15484061,15484087
,15484099,15484123,15484141,15484153,15484187,15484199,15484201,15484211
,15484219,15484223,15484243,15484247,15484279,15484333,15484363,15484387
,15484393,15484409,15484421,15484453,15484457,15484459,15484471,15484489
,15484517,15484519,15484549,15484559,15484591,15484627,15484631,15484643
,15484661,15484697,15484709,15484723,15484769,15484771,15484783,15484817
,15484823,15484873,15484877,15484879,15484901,15484919,15484939,15484951
,15484961,15484999,15485039,15485053,15485059,15485077,15485083,15485143
,15485161,15485179,15485191,15485221,15485243,15485251,15485257,15485273
,15485287,15485291,15485293,15485299,15485311,15485321,15485339,15485341
,15485357,15485363,15485383,15485389,15485401,15485411,15485429,15485441
,15485447,15485471,15485473,15485497,15485537,15485539,15485543,15485549
,15485557,15485567,15485581,15485609,15485611,15485621,15485651,15485653
,15485669,15485677,15485689,15485711,15485737,15485747,15485761,15485773
,15485783,15485801,15485807,15485837,15485843,15485849,15485857,15485863
I am not quite sure if I understand you correctly, but maybe you are looking for something like this:
from functools import reduce
def sumOfPowersModuloPrimes (tups, primes):
return [reduce(lambda x, y: (x + y) % p, (pow (b, e, p) for b, e in tups), 0) for p in primes]
You shouldn't run into any memory issues as your (intermediate) values never exceed max(primes). If your resulting list is too large, then return a generator and work with it instead of a list.
Ignoring primes, since they don't appear to be used for anything:
def sumOfPowers(tups, primes):
return sum( pow(x,y) for x,y in tups)
Is it possible that you are supposed to compute the sum modulo one or more of the prime numbers? Something like
2**3 + 5**2 mod 3 = 8 + 25 mod 3 = 33 mod 3 = 0
(where a+b mod c means to take the remainder of the sum a+b after dividing by c).
One guess at how multiple primes would be used is to use the product of the primes as the
divisor.
def sumOfPower(tups, primes):
# There are better ways to compute this product. Loop
# is for explanatory purposes only.
c = 1
for p in primes:
p *= c
return sum( pow(x,y,c) for x,y in tups)
(I also seem to remember that a mod pq == (a mod p) mod q if p and q are both primes, but I could be mistaken.)
Another is to return one sum for each prime:
def sumOfPower(tups, primes):
return [ sum( pow(x,y,c) for x,y in tups ) for c in primes ]
def sumOfPowers (powerPairs, unusedPrimesParameter):
sum = 0
for base, exponent in powerPairs:
sum += base ** exponent
return sum
Or short:
def sumOfPowers (powerPairs, unusedPrimesParameter):
return sum(base ** exponent for base, exponent in powerPairs)
perform the sum computation specified by each input modulo each of the primes in the second input list
That’s a completely different thing. However, you still haven’t really explained what your function is supposed to do and how it should work. Given that you mentioned Euler's theorem and the Chinese remainder theorem, I guess there is a lot more to it than you actually made us believe. You probably want to solve the exponentiations by using Euler's theorem to reduce those large powers. I’m not willing to further guess what is going on though; this seems to involve a non-trivial math problem you should solve on the paper first.
def sumOfPowers (powerPairs, primes):
for prime in primes:
sum = 0
for base, exponent in powerPairs:
sum += pow(base, exponent, prime)
# do something with the sum here
# Chinese remainder theorem?
return something

Help adding the Prime Number Hypothesis by C.F. Gauss to my Sieve of Eratosthenes

Hey all!
So I'm almost done with a problem that I started working on for school that deals with the Sieve of Eratosthenes. I managed to get the program to print out all the prime numbers from 2 to the square root of 1000. However, my teacher is asking me to use the Prime Number Hypothesis (?) by C.F. Gauss. This is what he says:
C. F. Gauss hypothesized that (N) the number of primes less than or equal to N is defined
as (N) = N/loge(N) as N approaches infinity. This was called the prime number
hypothesis. In a for loop print the prime numbers, a counter indicating its ordinal
number (1, 2, 3, etc.) and the value of (N).
I tried making another for loop, and printing the prime numbers but it's just not working for me! :( ugh.
Any help would be greatly appreciated! :)
import math
def sieves(N):
x = 1000*[0]
prime = 2
print('2')
i = 3
while (i <= N):
if i not in x:
print(i)
prime += 1
j = i
while (j <= (N / i)):
x.append(i * j)
j += 1
i += 2
print("\n")
def main():
count = 0
for i in range (1000):
count = count + 1
print(sieves(math.sqrt(1000)))
main()
The problem seems to be in the line j=i. You want to start j = 1 so that you catch all multiples of i and add them to your "x" list of non-prime elements. If you start j = i you will miss some. And when you test for "i not in x" it will evaluate to true for some non-primes.
Also instead of x.append(i*j) you probably meant x[i*j] = 1 (i.e. set to 1 means i*j == not prime)
With that modification "i not in x" can be optimized to "x[i] == 0", no reason to search the entire array. This would be a lot more efficient than keeping a sparse array "x" and having to search it's elements every step in the loop.

Categories