I'm trying to solve this problem from Timus Online Judge. To solve this problem you need generate a sequence of 1 000 000 lowercase Latin letters and write it to stdin in 1 second.
It is easy to solve this problem with C++ or Java. I have python solution here:
import os
from random import randint
s = ''.join(chr(97 + randint(0, 25)) for i in range(1000000))
os.write(1, bytes(s, 'utf8'))
It takes 1.7s:
$ time python3.3 1219.py > /dev/null
real 0m1.756s
user 0m1.744s
sys 0m0.008s
And I got "Time limit exceeded" in result. So the question is "How to do it faster?"
UPD1:
Using randint(97, 122)
reduces time at 16ms. Now it is 1.740s
UPD2: Solution by @Martijn Pieters takes 0.979s, but it doesn't pass test either.
UPD3 Martijn Pieters suggested a very good solutions, but it's still slow:
from sys import stdin
from random import choice
from string import ascii_lowercase
s = ''.join([choice(ascii_lowercase) for _ in range(1000000)])
stdout.write(s)
Takes 0.924s
from sys import stdout
from random import choice
from string import ascii_lowercase
for _ in range(1000000):
stdout.write(choice(ascii_lowercase))
Takes 1.173s
from sys import stdout
from random import choice
from string import ascii_lowercase
bal = [c.encode('ascii') for c in ascii_lowercase]
out = stdout.buffer
for _ in range(1000000):
out.write(choice(bal))
Takes 1.155s
from sys import stdout
from random import choice
from string import ascii_lowercase
bal = [c.encode('ascii') for c in ascii_lowercase]
stdout.buffer.write(b''.join([choice(bal) for _ in range(1000000)]))
Takes 0.901s
UPD4
Some guy just solved problem on Timus. I hope he will share his solution :)
UPD5 Thanks to Ashwini Chaudhary for sharing his Python 2.x solution with us:
from random import choice
from string import ascii_lowercase
lis=list(ascii_lowercase)
print ''.join(choice(lis) for _ in xrange(1000000))
It takes 0.527s on my computer and it passes tests on Timus. But problem with Python3.x still remains.
UPD6 Thanks to Markku K. this code:
import os
from random import random
from string import ascii_lowercase
bal = [c.encode('ascii') for c in ascii_lowercase]
os.write(1, b''.join([bal[int(random() * 26)] for _ in range(1000000)]))
Takes 0.445s, but still didn't pass the test
Here's Python 3 code that generates 1000000 "random" lowercase letters in
0.28
seconds (see also0.11
-seconds solution at the end; @Ashwini Chaudhary's code from the question takes0.55
seconds on my machine, @Markku K.'s code --0.53
):% len_lc
skews the distribution (see at the end on how to fix it) though It still satisfies the conditions (ascii, lowercase, frequencies of 1, 2, 3 letter sequences):where
check-seq.py
:Note: on acm.timus.ru
generate-random.py
gives "Output limit exceeded".To improve performance, you could use
bytes.translate()
method (0.11
seconds):How to fix
% len_lc
skew256
(number of bytes) is not evenly divisible by26
(number of lower Latin letters) therefore the formulamin_lc + b % len_lc
makes some values appear less often than others e.g.:Here, the input
range(256)
is uniformly distributed (each byte occurs exactly once) but'wxyz'
letters in the output are less often then the rest9
vs.10
occurrences. To fix it, unaligned bytes could be dropped:Here, the input is uniformly distributed bytes in the range
[0, 234)
the output is uniformly distributed ascii lowercase letters.bytes.translate()
accepts the second argument to specify bytes to delete:If the random generator (
os.urandom
here) produces long sequences of the bytes that are outside of the aligned range (>=234
) then thewhile
loop may execute many times.The time performance can be improved by another order of magnitude if
random.getrandbits(8*n).to_bytes(n, 'big')
is used instead ofos.urandom(n)
. The former uses Mersenne Twister as the core generator that may be faster thanos.urandom()
that uses sources provided by the operating system. The latter is more secure if you use the random string for secrets.I get a huge speed improvement by changing from randint(0,25) to int(random()*25) in your original solution. On my machine, the time went from about 2 seconds, to about 0.6 seconds. If you take a look at the random.py code, you will see that randint is full of checks that you don't want or need.
update: Oops, off by one. You need int(random()*26). Thanks Ashwini
Use random.choices?
On Python 3.6:
My solution which just got accepted (python 2.7, Execution time: 0.984):
Accessing elements of a list is faster is than for strings.
And you don't need
stdout
orstdin
here as most online judges us something like this to test your script:So you can use
print
instead ofstdout
andraw_input()
instead ofstdin
, though for huge inputsstdin.readline
is faster thanraw_input()
.Update 1:
Using @Markku's tip execution time was reduced to .64 in py2.7:
Use
string.ascii_lowercase
instead ofchr
to generate lowercase charaters:Also writing to
stdout
directly appears to be faster, encoding yourself in python is not faster than having it all handled in the C code.I also use a list comprehension;
str.join()
needs to scan through the input sequence twice, once to determine the length of the output, once to actually copy the input elements to output string. A list comprehension then beats out the slower generator-to-list code.Just using
choice(ascii_lowercase)
over your method of generating each character from an integer is over twice as fast:You could try and avoid the
''.join()
overhead by writing individual characters directly tostdout
:Next to try is to write raw bytes:
but these are no improvements over
''.join()
in my tests.Next we move to encoding the ASCII characters to bytes once, then using
bytes.join()
:bal
is a list of lowercase ASCII characters encoded to bytes, from which we random pick 1 million items, join them to into a large byte string then write that in one go to the binary stdout buffer.The bytes join is just as 'slow' as the string version:
but we encode 26 characters, not 1 million so the write stage is faster.
Try turning some part of it into C++ or another compiled language. That will almost guaranteed make it faster. Python, unfortunately, isn't too fast, especially when it comes to things like this. Try C++, C, or Pascal.
EDIT: Also see the Python Performance Tips