"sample larger than population" in random.sample python
Question:
creating a simple pass generator for myself, i noticed that if i want my population to be digits only (0-9) which is overall 10 options, if i want my length over 10, it wont use any of the digits more then once and return the “sample larger then population” error.
is it possible to maintain the code, but add/reduce code lines so it works? or do i HAVE To use a use random choice?
import string
import random
z=int(raw_input("for: n numbers only choose 1, n letters only choose 2, n letters and numbers choose 3, n for everything choose 4:"))
if z==1:
x=string.digits
elif z==2:
x=string.letters
elif z==3:
x=string.letters+string.digits
elif z==4:
x=string.letters+string.digits+string.punctuation
else:
print "die in a fire"
y=int(raw_input("How many passwords would you like?:"))
v=int(raw_input("How long would you like the password to be?:"))
for i in range(y):
string=""
for n in random.sample(x,v):
string+=n
print string
ty
Answers:
The purpose of random.sample()
is to pick a subset of the input sequence, randomly, without picking any one element more than once. If your input sequence has no repetitions, neither will your output.
You are not looking for a subset; you want single random choices from the input sequence, repeated a number of times. Elements can be used more than once. Use random.choice()
in a loop for this:
for i in range(y):
string = ''.join([random.choice(x) for _ in range(v)])
print string
This creates a string of length v
, where characters from x
can be used more than once.
Quick demo:
>>> import string
>>> import random
>>> x = string.letters + string.digits + string.punctuation
>>> v = 20
>>> ''.join([random.choice(x) for _ in range(v)])
'Ms>V\0Mf|W@R,#/.P~Rv'
>>> ''.join([random.choice(x) for _ in range(v)])
'TsPnvN&qlm#mBj-!~}3W'
>>> ''.join([random.choice(x) for _ in range(v)])
'{:dfE;VhR:=_~O*,QG<f'
@Martijn Pieters is right.
But since they state at https://docs.python.org/3.4/library/random.html:
Warning: The pseudo-random generators of this module should not be used for security purposes. Use os.urandom() or SystemRandom if you require a cryptographically secure pseudo-random number generator.
and the purpose of this is for generating passwords, I suggest this approach:
import string
import random
set = string.letters + string.digits + string.punctuation
length = 20
password = ''.join( [ random.SystemRandom().choice( set) for _ in range( length) ] )
print( password)
Could anybody please confirm that this is more secure?
Since the python_3.6 you can use random.choices(x, k=v)
for your purpose. It returns a k sized list of elements chosen from the population with replacement. If the population is empty, raises IndexError.
creating a simple pass generator for myself, i noticed that if i want my population to be digits only (0-9) which is overall 10 options, if i want my length over 10, it wont use any of the digits more then once and return the “sample larger then population” error.
is it possible to maintain the code, but add/reduce code lines so it works? or do i HAVE To use a use random choice?
import string
import random
z=int(raw_input("for: n numbers only choose 1, n letters only choose 2, n letters and numbers choose 3, n for everything choose 4:"))
if z==1:
x=string.digits
elif z==2:
x=string.letters
elif z==3:
x=string.letters+string.digits
elif z==4:
x=string.letters+string.digits+string.punctuation
else:
print "die in a fire"
y=int(raw_input("How many passwords would you like?:"))
v=int(raw_input("How long would you like the password to be?:"))
for i in range(y):
string=""
for n in random.sample(x,v):
string+=n
print string
ty
The purpose of random.sample()
is to pick a subset of the input sequence, randomly, without picking any one element more than once. If your input sequence has no repetitions, neither will your output.
You are not looking for a subset; you want single random choices from the input sequence, repeated a number of times. Elements can be used more than once. Use random.choice()
in a loop for this:
for i in range(y):
string = ''.join([random.choice(x) for _ in range(v)])
print string
This creates a string of length v
, where characters from x
can be used more than once.
Quick demo:
>>> import string
>>> import random
>>> x = string.letters + string.digits + string.punctuation
>>> v = 20
>>> ''.join([random.choice(x) for _ in range(v)])
'Ms>V\0Mf|W@R,#/.P~Rv'
>>> ''.join([random.choice(x) for _ in range(v)])
'TsPnvN&qlm#mBj-!~}3W'
>>> ''.join([random.choice(x) for _ in range(v)])
'{:dfE;VhR:=_~O*,QG<f'
@Martijn Pieters is right.
But since they state at https://docs.python.org/3.4/library/random.html:
Warning: The pseudo-random generators of this module should not be used for security purposes. Use os.urandom() or SystemRandom if you require a cryptographically secure pseudo-random number generator.
and the purpose of this is for generating passwords, I suggest this approach:
import string
import random
set = string.letters + string.digits + string.punctuation
length = 20
password = ''.join( [ random.SystemRandom().choice( set) for _ in range( length) ] )
print( password)
Could anybody please confirm that this is more secure?
Since the python_3.6 you can use random.choices(x, k=v)
for your purpose. It returns a k sized list of elements chosen from the population with replacement. If the population is empty, raises IndexError.