4

This is what my problem is: I need to make a random string 50 characters long, made up of 1s and 0s.

I know how to solve this problem, and even have a one-liner for it. I have also looked for various solutions to this problem on SO, only to get back what I already know(1, 2, etc). But what I really want is the most Pythonic way of doing this.

Currently, I'm leaning towards ''.join(( random.choice([0,1]) for i in xrange(50) ))

Is there a more pythonic way of doing this? Is there a built-in that does something like this, perhaps in itertools?

0

4 Answers 4

7

For Python2.7 or better:

In [83]: import random

In [84]: '{:050b}'.format(random.randrange(1<<50))
Out[84]: '10011110110110000011111000011100101111101001001011'

(In Python2.6, use '{0:050b}' instead of '{:050b}'.)


Explanation:

The string.format method can convert integers into their binary string representations. The basic format code to do this is '{:b}':

In [91]: '{:b}'.format(10)
Out[91]: '1010'

To make a string of width 50, use the format code '{:50b}':

In [92]: '{:50b}'.format(10)
Out[92]: '                                              1010'

and to fill in the whitespace with zeros, use {:050b}:

In [93]: '{:050b}'.format(10)
Out[93]: '00000000000000000000000000000000000000000000001010'

The syntax for str.format is a bit daunting at first. Here is my cheat sheet:

http://docs.python.org/library/string.html#format-string-syntax
replacement_field ::= "{" field_name ["!" conversion] [":" format_spec] "}"
field_name        ::= (identifier|integer)("."attribute_name|"["element_index"]")* 
attribute_name    ::= identifier
element_index     ::= integer
conversion        ::= "r" | "s"
format_spec       ::= [[fill]align][sign][#][0][width][,][.precision][type]
fill              ::= <a character other than '}'>
align             ::= "<" | ">" | "=" | "^"
                      "=" forces the padding to be placed after the sign (if any)
                          but before the digits. (for numeric types)
                      "<" left justification
                      ">" right justification 
                      "^" center justification
sign              ::= "+" | "-" | " "
                      "+" places a plus/minus sign for all numbers    
                      "-" places a sign only for negative numbers
                      " " places a leading space for positive numbers
#                     for integers with type b,o,x, tells format to prefix
                      output with 0b, 0o, or 0x.
0                     enables zero-padding. equivalent to 0= fill align.
width             ::= integer
,                     tells format to use a comma for a thousands separator
precision         ::= integer
type              ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" |
                      "o" | "x" | "X" | "%"
    c convert integer to corresponding unicode character
    n uses a locale-aware separator
    % multiplies number by 100, display in 'f' format, with percent sign
3
  • Could you please add an explanation? For example, what does '{:050b}' do? Also, this has the same issues with generalization as does @MikeSamuel's answer. But this seems more pythonic to me (don't know why) Commented Jan 18, 2012 at 23:41
  • 1
    @inspectorG4dget, the {} mean substitute an argument to format for the sequence; : means take the default (next) argument and apply the format that follows; 050 means create a 50 digit result with leading zeros; b means encode it in binary. Commented Jan 18, 2012 at 23:45
  • @inspectorG4dget, yes this answer isn't generalizable. If that's what you wanted you shouldn't have used a binary number as an example. Commented Jan 18, 2012 at 23:48
3
# Choose a number in [0, 1L << 50), and format it as binary.
# The [2:] lops off the prefix "0b"
bit_str = bin(random.randint(0, (1L << 50) - 1))[2:]
# We then need to pad to 50 bits.
fifty_random_bits = '%s%s' % ('0' * (50 - len(bit_str)), bit_str)
5
  • You could use random.getrandbits(50) instead of your call to randint. Commented Jan 18, 2012 at 23:37
  • (+1) Sure, this works for a string made of 0s and 1s, but it doesn't generalize into an arbitrary character set. Is there a way to generalize this? Commented Jan 18, 2012 at 23:37
  • 1
    A shorter way to pad is: bit_str.rjust(50, "0").
    – MRAB
    Commented Jan 18, 2012 at 23:40
  • @inspectorG4dget, is your arbitrary character set of cardinality 2, of cardinality power of 2, or of any finite cardinality? I do have to say that this sounds like a very different question from the one you asked. Commented Jan 18, 2012 at 23:41
  • @MikeSamuel: Fair enough. It seems that with the given level of specificity, this question has been answered. I will post another question to ask about the generalizability Commented Jan 18, 2012 at 23:50
3

That looks pretty pythonic to me. You can lose the brackets if you wish to save on characters:

''.join( random.choice(['0','1']) for i in xrange(50) )
0
3
from random import choice
from itertools import repeat
# map or generator expression, take your pick
"".join( map( choice, repeat( "01", 50)))
"".join( choice(x) for x in repeat("01", 50))

Change the inputs to repeat to generalize.

Not the answer you're looking for? Browse other questions tagged or ask your own question.