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
Sign up to request clarification or add additional context in comments.

3 Comments

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)
@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.
@inspectorG4dget, yes this answer isn't generalizable. If that's what you wanted you shouldn't have used a binary number as an example.
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 Comments

You could use random.getrandbits(50) instead of your call to randint.
(+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?
A shorter way to pad is: bit_str.rjust(50, "0").
@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.
@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
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) )

Comments

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.