Increase the performance of script for loops [closed] - python

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I wrote following code as shown below. When I put it into special webpage's compiler it generates some test case scenarios (I am not aware about inputs it does). My script gives correct results, nevertheless for (i assume) performance tests case scenarios it fails giving me message: time for execution has been exceeded (it shows that message if execution exceeds 5 seconds. I consider to increase performance of my code.
I suspect those lines to be time consuming:
if not any(tablica[r] == x for x in unikaty):
and/or this line:
for i in unikaty:
curr_frequency = tablica.count(i)
How can I increase performance of my script regarding those loops?
Full script:
def solve(N, A):
tablica = []
for r in range(0, N):
tablica.append([[A[0][r],A[0][r+1]],[A[1][r],A[1][r+1]]])
unikaty = []
for r in range(0, N):
if not any(tablica[r] == x for x in unikaty):
unikaty.append(tablica[r])
num = unikaty[0]
counter = 0
for i in unikaty:
curr_frequency = tablica.count(i)
if(curr_frequency > counter):
counter = curr_frequency
num = i
return f'{sum(1 for x in unikaty if tablica.count(x) == tablica.count(num))}'
T = int(input())
if not(T >= 1 and T <= 10):
quit()
for _ in range(T):
N = int(input())
if not(N >= 1 and N <= pow(10, 5)):
quit()
A = [list(map(int, input().split())) for i in range(2)]
print(solve(N, A))

for r in range(0, N):
if not any(tablica[r] == x for x in unikaty):
unikaty.append(tablica[r])
You can modifiing to (it's work if tablica consist tuples)
unikaty = list(set(tablica))
But I think that better solution - rewrite following lines to work with set structure unikaty = set(tablica).
May be condition can change to:
if not any(tablica[r] == x for x in unikaty):
to
if tablica[r] not in unikaty:
And
num = unikaty[0]
counter = 0
for i in unikaty:
curr_frequency = tablica.count(i)
if(curr_frequency > counter):
counter = curr_frequency
num = i
to
num = max((tablica.count(i), i) for i in unikaty)[1]
And
return f'{sum(1 for x in unikaty if tablica.count(x) == tablica.count(num))}'
to (calculate invariant before cycle)
mx = tablica.count(num)
return f'{sum(tablica.count(x) == mx for x in unikaty)}'
And I think that f-string is excess and you can return int for sum.
One more way change type of tablica to tuple of tuples (if A[i][j] is hashable values) and use collections.Counter
from collections import Counter
def solve(N, A):
tablica = (((A[0][r],A[0][r+1]),(A[1][r],A[1][r+1])) for r in range(0, N)),
counters = Counter(tablica)
mx = counters.most_common(1)[0][1]
return list(c.values()).count(mx)
And check range like python style
for _ in range(T):
N = int(input())
if not(1 <= N <= 100000):

You can start by using List Comprehension:
Here's an exemple:
Your code :
for r in range(0, N):
if not any(tablica[r] == x for x in unikaty):
unikaty.append(tablica[r])
With List Comprehension:
unikaty = [tablica[r] for r in range(0, N) if not any(tablica[r] == x for x in unikaty)]
This should speed up you program execution.
Also try to avoid as much as possible using for loop, those are very resources consuming. Instead use while loop (it is recommended to use while i=1 for infinity loop).

Related

How many numbers have n as their smallest prime factor within 10^6?

for _ in range(int(input())):
n=int(input())
least_prime = [0] * (1000001)
count=0
for i in range(2, int(1000001**0.5 + 1)):
if least_prime[i] == 0:
least_prime[i] = i
for j in range(i * i, 1000000, 2*i) :
if (least_prime[j] == 0) :
least_prime[j] = i
for i in range(2, 1000001) :
if least_prime[i] == n:
count+=1
print(count)
Can anyone reduce the time complexity of the problem? tried for an hour maybe but can't simplify more than this.
First for loop is for the number of test cases. And the question is regarding how many numbers have n as their smallest prime factor within 10^6?
there are 3 problems with your code:
you repeat the same task however many test case you have, which is a waste of time and resources
you leave unchanged/ignore the numbers/primes above int(1000001**0.5 + 1)
and by doing a step of 2*i in your j range you mislabel a bunch of number, like for example 6, which should have 2 marked as its least prime but it get marked as itself because it get skipped, 6=4+2 but you never get there with your j range because you make a step of 4 so you go 4,8,12,... instead of 4,6,8,...
How to fix it? simple, make the sieve first and only once, no need to repeat the exact same thing 10**6 times or however many test case you have, two or more is two too many times ( if n is always prime that is 78498 which is the number of primes less than 10**6), and the other 2 points are simple fix
I would put it inside its own function, which make it more reusable and easy to play with, and for the counting part, there is no problem with how you do it, but is more convenient with a Counter which make all the counting at once
from collections import Counter
def make_sieve(MAX):
MAX += 1
least_prime = [0] * MAX
for i in range(2, MAX):
if least_prime[i] == 0:
least_prime[i] = i
for j in range(i * i, MAX, i) :
if (least_prime[j] == 0) :
least_prime[j] = i
return least_prime
result = Counter(make_sieve(10**6))
print("n=2->",result[2])# n=2-> 500000
print("n=3->",result[3])# n=3-> 166667
so now your test can be as simple as
for _ in range(int(input())):
n = int(input())
print(result[n])
And just for completeness, here is how you can do it without the Counter
least_prime = make_sieve(10**6)
for _ in range(int(input())):
n = int(input())
count = 0
for p in least_prime:
if p==n:
count+=1
print(count)
but that is also too long, a list already do that for us with .count
least_prime = make_sieve(10**6)
for _ in range(int(input())):
n = int(input())
count = least_prime.count(n)
print(count)
The counter is still better, because in just one go you get all the answers, and if needed you can make your own with a regular dictionary, but I leave that as exercise to the reader.

Memory Limit Exceed to find N-th prime

I was solving a problem (on a website) to print N-th prime number where N is an user input. The problem fixed those limits 1s, 512 MB. I wrote that code bellow.
n = int(input())
r = (n+1)**2
nth = n - 1
d = [x for x in range(2, r)]
non_prime = []
for i in range(2, r):
for x in d:
if i % x == 0 and x != 1 and x != i:
non_prime.append(i)
non_prime = list(set(non_prime))
prime_numbers = [x for x in d if x not in non_prime]
print(prime_numbers[nth])
Now the code works perfectly but after submitting it says Memory Limit Exceeded. How to solve that without changing my code excessively??
(I know there is much easier way to solve that problem. But I solve it on my own.)
The method you are using checks every number up to the square of a given number. I find it much easier to add prime numbers to a list and check its length. Using this method, it checks few numbers and can run fast.
def getPrime(num):
arr = []
x = 2
while len(arr) < num:
prime = True
for divisor in range(2, x):
if x % divisor == 0 and prime == True:
prime = False
if prime == True:
arr.append(x)
x += 1
return arr[num - 1]
I encourage you improve this code, as it is rudimentary.
Edit: I corrected a variable name.

Modifying a generator using send

I want to modify a generator by only changing the manipulate_generator function below. This is a HR challenge that has had me scratching my head throughout the day. I got the idea of using send from a previous SO question on even numbers. I cannot get to work using a primality checker. The algorithm should print the first 10 non-primes. It's currently printing 3.
from math import sqrt
from itertools import count, islice
def is_prime(n):
if n < 2:
return False
for number in islice(count(2), int(sqrt(n) - 1)):
if n % number == 0:
return False
return True
# this is the only portion of the code that can be changed
def manipulate_generator(generator, n):
if not is_prime(n):
generator.send(n+1)
#all the code below cannot be changed
def positive_integers_generator():
n = 1
while True:
x = yield n
if x is not None:
n = x
else:
n += 1
k = int(input())
g = positive_integers_generator()
for _ in range(k):
n = next(g)
print(n)
manipulate_generator(g, n)

Erathostenes sieve optimization [duplicate]

This question already has answers here:
A Fast Prime Number Sieve in Python
(2 answers)
Closed 9 years ago.
I've written the erathostenes algorithm in python a few weeks ago, and it looked like the following:
def erathostenes(n):
A = range(2,n+1)
B = []
i = 0
while A[i] < math.sqrt(n):
B.append(A[i])
j = i
aux = A[i]
while j < len(A):
if A[j]%aux == 0:
A[j] = 0
j += aux
i += 1
while A[i] == 0:
i += 1
for i in range(len(A)):
if A[i] != 0:
B.append(A[i])
i += 1
return B
After thinking a little (I'm noob in programming) i just did some modifications in my algorithm an right now looks like:
def erathostenes(n):
A = range(2,n + 1)
B = []
i = 0
raiz = math.sqrt(n)
lenA = len(A)
rangeLenA = range(lenA)
while A[i] < raiz:
B.append(A[i])
j = i
aux = A[i]
while j < lenA:
A[j] = 0
j += aux
i += 1
while A[i] == 0:
i += 1
for i in rangeLenA:
if A[i] != 0:
B.append(A[i])
i += 1
return B
If I execute the algorithm with n=10.000.000 the execution time in the first code is approximately 7 sec and with the second code it's about 4 seconds.
Any ideas about some more optimizations in my algorithm? thanks!
i += 1
in the last loop is funny.
Consider replacing
for i in rangeLenA:
with
for i in xrange(LenA)
you avoid generating a huge list you don't need.
EDIT:
Also consider this:
for j in xrange(i,lenA,aux):
instead of:
while j < lenA:
And fix the bug
while A[i] <= raiz:
as suggested by fryday.
There is a error in your code. Change
while A[i] < raiz:
on
while A[i] <= raiz:
You can found error when N is square.
For opimization use xrange for rangeLenA instead of range.
Tried to make a non-loop version just for fun. It came out like this:
def erathostenes(n):
def helper_function(num_lst, acc):
if not num_lst:
return acc
if len(num_lst) == 1:
acc.append(num_lst[0])
return acc
num = num_lst.pop(0)
multiples = ([x for x in range(num + 1, num_lst[-1] + 1)
if x % num == 0])
remains = ([x for x in num_lst if x not in multiples])
acc.append(num)
return helper_function(remains, acc )
return helper_function(range(2, n + 1), [])
When i ran the timing, got 826 us for the post erathostenes(1000), and 26ms for my version (!!). Surprised me it was so slow.
Functional programming it's more fun, but looks isn't the right fit for this problem, in Python (my guess is that it would be faster in a more functional language).
So i tried a imperative version. It looks like this:
def erathostenes_imperative(n):
limit = int(math.sqrt(n))
def helper_function(flags, size):
for i in range(2,limit):
if flags[i] == True:
j = 2*i
while j < size:
if j % i == 0:
flags[j] = False
j = j + i
return [x for x in range(2, n + 1) if flags[x]]
return helper_function([True]*(n + 1), n)
What i did was changing the list of integers into a list of True/False flags. Intuitively, looks like it's faster to iterate, right?
My results where 831ms for erathostenes_imperative(100000), vs. 1.45 in your version.
It's a shame that imperative writting it's faster. The code look so messy with all the fors, whiles, i's and j's
Try the Sieve of Atkin. It's similar, but it a modification of the Sieve of Eratosthenes, and it filters out all multiples of 2, 3, 5 right off, as well as a few other optimizations. You might also want to try to find a tool that tells you the run time of each operation and modify the operations with a larger run time.
However, since you're new to programming, you might best be served either implementing other algorithms, or doing other programming exercises to improve.

How to print factors of a number? [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 9 years ago.
Improve this question
I have a question about multiples in python. Does anyone know how I can get the program to print multiples of a certain number? Like if I had them in put "10", it should print "1,10,2,5" or something along those lines
Thanks
Very naively you can test every number up to n(10)
n = 10
results = []
for i in range(1,n+1):
if n % i == 0:
results.append(i)
print(results)
Or as a list comprehension:
n = 10
print([x for x in range(1,n+1) if n % x == 0])
But in reality you only need to test up to the sqrt of n. Using a simple generator:
def divisor(n):
a = 1
l = n ** 0.5
while a <= l:
if n % a == 0:
if a == n//a:
yield a,
else:
yield a, n//a
a += 1
print([x for a in divisor(10) for x in a])
print(sorted(x for a in divisor(10) for x in a)) # Sorted
Here you go:
from collections import defaultdict
from math import sqrt
def factor(n):
i = 2
limit = sqrt(n)
while i <= limit:
if n % i == 0:
yield i
n = n / i
limit = sqrt(n)
else:
i += 1
if n > 1:
yield n
def factorGenerator(n):
d=defaultdict(int)
for f in factor(n):
d[f]+=1
return [(e,d[e]) for e in sorted(d.keys())]
def divisorGen(n):
factors = factorGenerator(n)
nfactors = len(factors)
f = [0] * nfactors
while True:
yield reduce(lambda x, y: x*y, [factors[x][0]**f[x] for x in range(nfactors)], 1)
i = 0
while True:
f[i] += 1
if f[i] <= factors[i][1]:
break
f[i] = 0
i += 1
if i >= nfactors:
return
print list(divisorGen(10))
Prints:
[1, 2, 5, 10]

Categories

Resources