Python memory error in CPU and wall time - python

I am trying to do a function to calculate the modular exponential of three variables and compare the CPU and wall time when:
e=2^n and e=2^n-1
Here is my code:
from random import choice
import random
def question_3(m,n):
list = []
for i in range(2,2**m):
flag=True
for num in list:
if(i%num==0):
flag=False
if(flag):
list.append(i)
p = choice(list)
a = randint(1,int(p)-1)
e = pow(2,n)
return pow(a,e,p)
time t = question_3(150,100)
But when I enter m and n with huge numbers, it gives me:
range() result has too many items

That's because you're forcing range to generate too much data. For example, range(2, 2*1234567891011) will generate a list with the length of 2*1234567891011-2, isn't it a bit too much?
Try using xrange() instead, it generates the data when needed, instead of making all of the data when you call it.
Change it to this:
for i in xrange(2,2**m):
Looks simple, but it has a huge difference. Hope this helps!

Related

how do i print eachother while sentence?

i coded 6number from1 to 45 without duplicating
from random import *
lotto = []
while True:
m = randrange(1, 46)
if m not in lotto:
lotto.append(m)
if len(lotto) == 6:
break
but I want to make different 6number as wanting
example, if i input 2 {1,21,23,43,35,26} {21,15,6,37,28,7}
but with this code,
for i in range(init(input""))
print(lotto)
same result is printed
i wanna different result
how do i add it?
you can put the lotto genrator in a function :
from random import randrange
def give_lotto():
lotto = []
while True:
m = randrange(1, 46)
if m not in lotto:
lotto.append(m)
if len(lotto) == 6:
break
return(lotto)
then print( result in the loop) in your example it gives you the same result because it doesn't generate it again it just prints the final result twie that's why putting it into a function will give your intended result
for i in range(int(input())):
print(give_lotto())
If I understand correctly you are wanting to produce a list of n lotto combinations
import random
def n_lotto(n):
lotto_draws = []
for i in range(n):
balls = random.sample(range(1, 46), 7)
lotto_draws.append(balls)
return lotto_draws
As a side note as you appear to be new a quick FYI. Python is a slow language butit has a lot of powerful builtin functions most of which are written in C and are therefore blazing fast, they are also extremely well tested meaning that it is almost always a good idea to have a quick google for a builtin if what you are trying to do is relatively simple as:
it'll be easier to read
it'll be faster
you're less likely to have bugs
I'm not completely sure what you're after but as a note using
lotto.append(m.pop())
in place of
if m not in lotto:
lotto.append(m)
Is faster and removes the poped character from m (preventing any logic errors in the future)
edit:
Apologies I haven't used the random module in a while and I thought randrange was generating a list not a number, therefore pop will not work and the original implementation is correct. However, for this purpose random.sample is a far better function to use

Python random number loop with different outcomes each time

I’ve been working on a school project and need to do a loop consisting of a couple random numbers but I need them to output a different number each time. The code I’ve been using for the random numbers is this.
import random
a=random.randint(1,9)
I’m new to coding and just starting getting into python for fun and have looked everywhere for how to complete this loop but I can’t find anything that works.I know this code does not include a loop and the loop I was using before was “while True” and “for i in range” Thanks
You have not created any loop yet. You're generating random integer only once.
In order to generate more of them you have to use something like a for loop.
If you're familiar with the concept of range then this is a simple example of generating x-number of random integers.
import random
x = 10
for i in range(0, x):
a = random.randint(1, 9)
print(a)
I am assuming you are doing something like this. Your loop needs to call the random.randint function on each iteration of the loop.
a = random.randint(1,9)
for i in range(5):
print(a)
What you should be doing is this
for i in range(5):
print(random.randint(1,9))
If you want to have 5 values between 1 - 9 without repetition you can use the sample function of the random module. This will select n values from a list and store them in a new list.
array = random.sample(range(1, 9), 5)
for elem in array:
print(elem)
But if you want to have a new random value between 1 and 9 every iteration and you dont care if a number is repeated I would go with the answers the others already gave you.
Random works with a seed, with the same seed the same output
Easiest way to achieve that you want, you will have a different seed each time you run your program
import random
from datetime import datetime
random.seed(datetime.now())
for _ in range(10):
a=random.randint(1,9)

Can I randomly generate numbers without the same number next to eachother?

My goal is to generate a series of random numbers without allowing the same number repeating next to eachother but allowing the same number twice like 1,2,1,2 but not 1,1,2,2 and I'm just not really sure how to accomplish this.
Something like this?
import random
list = []
list.append(random.randrange(50))
for i in range(50):
x = random.randrange(50)
while x == list[i]:
x = random.randrange(50)
list.append(x)
print(list)
Also you should post your own attempt. It gives everyone a good reference and starting point to help you out in a meaning full and focused way.

Generate the n-th random number with Python

I am trying to generate random numbers that are used to generate a part of a world (I am working on world generation for a game). I could create these with something like [random.randint(0, 100) for n in range(1000)] to generate 1000 random numbers from 0 to 100, but I don't know how many numbers in a list I need. What I want is to be able to say something like random.nth_randint(0, 100, 5) which would generate the 5th random number from 0 to 100. (The same number every time as long as you use the same seed) How would I go about doing this? And if there is no way to do this, how else could I get the same behavior?
Python's random module produces deterministic pseudo random values.
In simpler words, it behaves as if it generated a list of predetermined values when a seed is provided (or when default seed is taken from OS), and those values will always be the same for a given seed.
Which is basically what we want here.
So to get nth random value you need to either remember its state for each generated value (probably just keeping track of the values would be less memory hungry) or you need to reset (reseed) the generator each time and produce N random numbers each time to get yours.
def randgen(a, b, n, seed=4):
# our default seed is random in itself as evidenced by https://xkcd.com/221/
random.seed(seed)
for i in range(n-1):
x = random.random()
return random.randint(a, b)
If I understood well your question you want every time the same n-th number. You may create a class where you keep track of the generated numbers (if you use the same seed).
The main idea is that, when you ask for then nth-number it will generate all the previous in order to be always the same for all the run of the program.
import random
class myRandom():
def __init__(self):
self.generated = []
#your instance of random.Random()
self.rand = random.Random(99)
def generate(self, nth):
if nth < len(self.generated) + 1:
return self.generated[nth - 1]
else:
for _ in range(len(self.generated), nth):
self.generated.append(self.rand.randint(1,100))
return self.generated[nth - 1]
r = myRandom()
print(r.generate(1))
print(r.generate(5))
print(r.generate(10))
Using a defaultdict, you can have a structure that generates a new number on the first access of each key.
from collections import defaultdict
from random import randint
random_numbers = defaultdict(lambda: randint(0, 100))
random_number[5] # 42
random_number[5] # 42
random_number[0] # 63
Numbers are thus lazily generated on access.
Since you are working on a game, it is likely you will then need to preserve random_numbers through interruptions of your program. You can use pickle to save your data.
import pickle
random_numbers[0] # 24
# Save the current state
with open('random', 'wb') as f:
pickle.dump(dict(random_numbers), f)
# Load the last saved state
with open('random', 'rb') as f:
opened_random_numbers = defaultdict(lambda: randint(0, 100), pickle.load(f))
opened_random_numbers[0] # 24
Numpy's new random BitGenerator interface provides a method advance(delta) some of the BitGenerator implementations (including the default BitGenerator used). This function allows you to seed and then advance to get the n-th random number.
From the docs:
Advance the underlying RNG as-if delta draws have occurred.
https://numpy.org/doc/stable/reference/random/bit_generators/generated/numpy.random.PCG64.advance.html#numpy.random.PCG64.advance

Trying to speed up python code by replacing loops with functions

I am trying to come up with a faster way of coding what I want to. Here is the part of my program I am trying to speed up, hopefully using more inbuilt functions:
num = 0
num1 = 0
rand1 = rand_pos[0:10]
time1 = time.clock()
for rand in rand1:
for gal in gal_pos:
num1 = dist(gal, rand)
num = num + num1
time2 = time.clock()
time_elap = time2-time1
print time_elap
Here, rand_pos and gal_pos are lists of length 900 and 1 million respectively.
Here dist is function where I calculate the distance between two points in euclidean space.
I used a snippet of the rand_pos to get a time measurement.
My time measurements are coming to be about 125 seconds. This is way too long!
It means that if I run the code over all the rand_pos, it will take about three hours to do!
Is there a faster way I can do this?
Here is the dist function:
def dist(pos1,pos2):
n = 0
dist_x = pos1[0]-pos2[0]
dist_y = pos1[1]-pos2[1]
dist_z = pos1[2]-pos2[2]
if dist_x<radius and dist_y<radius and dist_z<radius:
positions = [pos1,pos2]
distance = scipy.spatial.distance.pdist(positions, metric = 'euclidean')
if distance<radius:
n = 1
return n
While most of the optimization probably needs to happen within your dist function, there are some tips here to speed things up:
# Don't manually sum
for rand in rand1:
num += sum([dist(gal, rand) for gal in gal_pos])
#If you can vectorize something, then do
import numpy as np
new_dist = np.vectorize(dist)
for rand in rand1:
num += np.sum(new_dist(gal_pos, rand))
# use already-built code whenever possible (as already suggested)
scipy.spatial.distance.cdist(gal, rand1, metric='euclidean')
There is a function in scipy that does exactly what you want to do here:
scipy.spatial.distance.cdist(gal, rand1, metric='euclidean')
It will be faster than anything you write in pure Python probably, since the heavy lifting (looping over the pairwise combinations between arrays) is implemented in C.
Currently your loop is happening in Python, which means there is more overhead per iteration, then you are making many calls to pdist. Even though pdist is very optimized, the overhead of making so many calls to it slows down your code. This type of performance issue was once described to me with a very useful analogy: its like trying to have a conversation with someone over the phone by saying one word per phone call, even though each word is going across the line very fast, your conversation will take a long time because you need to hang up and dial again repeatedly.

Categories

Resources