Velocity Reviews > Random string of digits?

Random string of digits?

Roy Smith
Guest
Posts: n/a

 12-25-2011
I want to create a string of 20 random digits (I'm OK with leading
zeros). The best I came up with is:

''.join(str(random.randint(0, 9)) for i in range(20))

Is there something better?

Steven D'Aprano
Guest
Posts: n/a

 12-25-2011
On Sun, 25 Dec 2011 08:30:46 -0500, Roy Smith wrote:

> I want to create a string of 20 random digits (I'm OK with leading
> zeros). The best I came up with is:
>
> ''.join(str(random.randint(0, 9)) for i in range(20))
>
> Is there something better?

'%20d' % random.randint(0, 10**20-1)

--
Steven

Chris Angelico
Guest
Posts: n/a

 12-25-2011
On Mon, Dec 26, 2011 at 12:30 AM, Roy Smith <(E-Mail Removed)> wrote:
> I want to create a string of 20 random digits (I'm OK with leading
> zeros). *The best I came up with is:
>
> ''.join(str(random.randint(0, 9)) for i in range(20))
>
> Is there something better?

The simple option is:
random.randint(0,99999999999999999999)
or
"%020d"%random.randint(0,99999999999999999999)
(the latter gives you a string, padded with leading zeroes). But I'm
assuming that you discarded that option due to lack of entropy (ie you
can't trust randint() over that huge a range).

The way I'd do it would be in chunks. The simple option is one chunk;
your original technique is twenty. We can go somewhere in between.
First thing to do though: ascertain how far randint() is properly
random. The Python 2 docs [1] say that the underlying random()
function uses 53-bit floats, so you can probably rely on about that
much randomness; for argument's sake, let's say it's safe for up to
10,000 but no further (although 53 bits give you about 15 decimal
digits).

''.join('%04d'%random.randint(0,9999) for i in range(5))

For your actual task, I'd be inclined to take ten digits, twice, and
not bother with join():

'%010d%010d'%(random.randint(0,9999999999),random. randint(0,9999999999))

Looks a little ugly, but it works! And only two random number calls
(which can be expensive).

ChrisA

Chris Angelico
Guest
Posts: n/a

 12-25-2011
On Mon, Dec 26, 2011 at 12:50 AM, Chris Angelico <(E-Mail Removed)> wrote:
> The Python 2 docs [1]

Python 2: http://docs.python.org/library/random.html
And same in Python 3: http://docs.python.org/py3k/library/random.html

ChrisA

Chris Angelico
Guest
Posts: n/a

 12-25-2011
On Mon, Dec 26, 2011 at 12:48 AM, Steven D'Aprano
<(E-Mail Removed)> wrote:
> On Sun, 25 Dec 2011 08:30:46 -0500, Roy Smith wrote:
>
>> I want to create a string of 20 random digits (I'm OK with leading
>> zeros). *The best I came up with is:
>>
>> ''.join(str(random.randint(0, 9)) for i in range(20))
>>
>> Is there something better?

>
> '%20d' % random.randint(0, 10**20-1)

I should mention that this sort of thing is absolutely acceptable in
situations where you don't actually need that level of randomness; if
you want to assign 20-digit numbers to each of ten million objects,
you can happily use something that's only really random to fifteen. I
use this technique with base 36 numbers to generate arbitrary
alphanumeric strings to use as message tags, for instance; fill out a
specified field size, even though not every combination will actually
be used.

ChrisA

Roy Smith
Guest
Posts: n/a

 12-25-2011
In article <(E-Mail Removed)>,
Chris Angelico <(E-Mail Removed)> wrote:

> "%020d"%random.randint(0,99999999999999999999)
> (the latter gives you a string, padded with leading zeroes). But I'm
> assuming that you discarded that option due to lack of entropy (ie you
> can't trust randint() over that huge a range).

Actually, the only entropy involved here is the ever increasing amount
of it between my ears. It never occurred to me to try that

> For your actual task, I'd be inclined to take ten digits, twice, and
> not bother with join():
>
> '%010d%010d'%(random.randint(0,9999999999),random. randint(0,9999999999))
>
> Looks a little ugly, but it works! And only two random number calls
> (which can be expensive).

Hmmm. In my case, I was looking more to optimize clarity of code, not
speed. This is being used during account creation on a web site, so it
doesn't get run very often.

It turns out, I don't really need 20 digits. If I can count on

>>> "%020d" % random.randint(0,999999999999999)

to give me 15-ish digits, that's good enough for my needs and I'll
probably go with that. Thanks.

Chris Angelico
Guest
Posts: n/a

 12-25-2011
On Mon, Dec 26, 2011 at 1:21 AM, Roy Smith <(E-Mail Removed)> wrote:
> It turns out, I don't really need 20 digits. *If I can count on
>
>>>> "%020d" % random.randint(0,999999999999999)

>
> to give me 15-ish digits, that's good enough for my needs and I'll
> probably go with that. *Thanks.

I'd say you can. The information about the Mersenne Twister underlying
the module's functions isn't marked as "CPython Implementation Note",
so I would expect that every Python implementation will provide a
minimum of 53-bit precision. (It does have the feeling of an
implementation detail though; is there a guarantee that two Pythons
will generate the same sequence of numbers from the same seed?)

ChrisA

Peter Otten
Guest
Posts: n/a

 12-25-2011
Chris Angelico wrote:

> On Mon, Dec 26, 2011 at 12:30 AM, Roy Smith <(E-Mail Removed)> wrote:
>> I want to create a string of 20 random digits (I'm OK with leading
>> zeros). The best I came up with is:
>>
>> ''.join(str(random.randint(0, 9)) for i in range(20))
>>
>> Is there something better?

>
> The simple option is:
> random.randint(0,99999999999999999999)
> or
> "%020d"%random.randint(0,99999999999999999999)
> (the latter gives you a string, padded with leading zeroes). But I'm
> assuming that you discarded that option due to lack of entropy (ie you
> can't trust randint() over that huge a range).
>
> The way I'd do it would be in chunks. The simple option is one chunk;
> your original technique is twenty. We can go somewhere in between.
> First thing to do though: ascertain how far randint() is properly
> random. The Python 2 docs [1] say that the underlying random()
> function uses 53-bit floats, so you can probably rely on about that
> much randomness; for argument's sake, let's say it's safe for up to
> 10,000 but no further (although 53 bits give you about 15 decimal
> digits).
>
> ''.join('%04d'%random.randint(0,9999) for i in range(5))
>
> For your actual task, I'd be inclined to take ten digits, twice, and
> not bother with join():
>
> '%010d%010d'%(random.randint(0,9999999999),random. randint(0,9999999999))
>
> Looks a little ugly, but it works! And only two random number calls
> (which can be expensive).

Judging from a quick look into the code (method Random._randbelow()) I'd say
you don't need to do that unless you override Random.random() and not
Random.getrandbits(). Even if you roll your own random() you'll get a
warning when you run into the limit:

>>> import random
>>> random.randrange(10**20)

27709407700486201379L
>>> class R(random.Random):

.... def random(self): return 4 # *
....
>>> R().randrange(10**20)

/usr/lib/python2.6/random.py:253: UserWarning: Underlying random() generator
does not supply
enough bits to choose from a population range this large
_warn("Underlying random() generator does not supply \n"
400000000000000000000L

(*) According to the literature 4 is the best random number, 9 being the
runner-up:

A quick sanity check:

>>> from collections import Counter
>>> import random
>>> Counter(str(random.randrange(10**10000)))

Counter({'9': 1060, '6': 1039, '3': 1036, '8': 1007, '7': 997, '4': 977,
'1': 976, '5': 976, '2': 970, '0': 962})

Steven D'Aprano
Guest
Posts: n/a

 12-25-2011
On Mon, 26 Dec 2011 01:51:30 +1100, Chris Angelico wrote:

> On Mon, Dec 26, 2011 at 1:21 AM, Roy Smith <(E-Mail Removed)> wrote:
>> It turns out, I don't really need 20 digits. Â*If I can count on
>>
>>>>> "%020d" % random.randint(0,999999999999999)

>>
>> to give me 15-ish digits, that's good enough for my needs and I'll
>> probably go with that. Â*Thanks.

>
> I'd say you can. The information about the Mersenne Twister underlying
> the module's functions isn't marked as "CPython Implementation Note", so
> I would expect that every Python implementation will provide a minimum
> of 53-bit precision. (It does have the feeling of an implementation
> detail though; is there a guarantee that two Pythons will generate the
> same sequence of numbers from the same seed?)

Yes.

http://docs.python.org/dev/library/r...eproducibility

I think that's a new guarantee. Previously, the answer was only yes-ish:
in theory, no guarantee was made, but in practice, you could normally
rely on it. For example, when the Mersenne Twister became the default
random number generator, the old generator, Wichman-Hill, was moved into
its own module whrandom (deprecated in 2.4; now gone) for those who
needed backwards compatibility.

--
Steven

Steven D'Aprano
Guest
Posts: n/a

 12-25-2011
On Mon, 26 Dec 2011 00:54:40 +1100, Chris Angelico wrote:

> On Mon, Dec 26, 2011 at 12:48 AM, Steven D'Aprano
> <(E-Mail Removed)> wrote:
>> On Sun, 25 Dec 2011 08:30:46 -0500, Roy Smith wrote:
>>
>>> I want to create a string of 20 random digits (I'm OK with leading
>>> zeros). Â*The best I came up with is:
>>>
>>> ''.join(str(random.randint(0, 9)) for i in range(20))
>>>
>>> Is there something better?

>>
>> '%20d' % random.randint(0, 10**20-1)

>
> I should mention that this sort of thing is absolutely acceptable in
> situations where you don't actually need that level of randomness;

Use the Source, Luke, er, Chris

If I've read the source correctly, randint() will generate sufficient
bits of randomness to ensure that the entire int is random.

http://hg.python.org/cpython/file/default/Lib/random.py

--
Steven