Python 生成带有较低拉丁字母的大随机字符串的最快方法

Python 生成带有较低拉丁字母的大随机字符串的最快方法,python,performance,python-3.x,random,stdin,Python,Performance,Python 3.x,Random,Stdin,我想从Timus Online Judge那里解决这个问题。要解决这个问题,您需要生成一个包含1000000个小写拉丁字母的序列,并在1秒内将其写入stdin 用C++或java很容易解决这个问题。我这里有python解决方案: import os from random import randint s = ''.join(chr(97 + randint(0, 25)) for i in range(1000000)) os.write(1, bytes(s, 'utf8')) 需要1.

我想从Timus Online Judge那里解决这个问题。要解决这个问题,您需要生成一个包含1000000个小写拉丁字母的序列,并在1秒内将其写入stdin

用C++或java很容易解决这个问题。我这里有python解决方案:

import os
from random import randint

s = ''.join(chr(97 + randint(0, 25)) for i in range(1000000))
os.write(1, bytes(s, 'utf8'))
需要1.7秒:

$ time python3.3 1219.py > /dev/null

real    0m1.756s
user    0m1.744s
sys     0m0.008s
结果是“超过时限”。所以问题是“如何更快地完成?”

UPD1: 使用
randint(97122)
将时间缩短为16ms。现在是1.740秒

UPD2: @Martijn Pieters的解决方案需要0.979秒,但它也不能通过测试

UPD3 提出了一个非常好的解决方案,但仍然很慢:

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) 
0.924s

from sys import stdout
from random import choice
from string import ascii_lowercase

for _ in range(1000000):
    stdout.write(choice(ascii_lowercase))
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))
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)]))
1.173秒

from sys import stdout
from random import choice
from string import ascii_lowercase

for _ in range(1000000):
    stdout.write(choice(ascii_lowercase))
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))
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)]))
时间1.155s

from sys import stdout
from random import choice
from string import ascii_lowercase

for _ in range(1000000):
    stdout.write(choice(ascii_lowercase))
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))
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)]))
需要0.901s

from sys import stdout
from random import choice
from string import ascii_lowercase

for _ in range(1000000):
    stdout.write(choice(ascii_lowercase))
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))
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)]))
UPD4

蒂默斯身上有个男人的问题。我希望他能分享他的解决方案:)

UPD5 感谢您与我们分享他的Python 2.x解决方案:

from random import choice
from string import ascii_lowercase
lis=list(ascii_lowercase)
print ''.join(choice(lis) for _ in xrange(1000000)) 
在我的电脑上需要0.527s,并且通过了Timus的测试。但Python3.x的问题仍然存在

UPD6 由于此代码:

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)]))

需要0.445s,但仍然没有通过测试

生成并写入大小为2次方的块


使用26个小写字母的字符串或数组,随机选择,而不是生成字符。

尝试将其中的一部分转换成C++或另一种编译语言。这几乎肯定会使它更快。不幸的是,Python并不是很快,尤其是在这样的事情上。尝试C++、C或.</P>


编辑:另请参见使用
字符串.ascii\u小写
而不是
chr
生成小写字符:

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)
另外,直接写入stdout似乎更快,用python编码并不比用C代码处理快

我也使用列表理解
str.join()
需要扫描输入序列两次,一次用于确定输出的长度,一次用于将输入元素实际复制到输出字符串。然后,列表理解将速度较慢的生成器击败为列表代码的生成器

仅使用
choice(ascii\u小写)
从整数生成每个字符的方法,速度就快了一倍以上:

>>> timeit.timeit('f()', 'from __main__ import yours as f', number=3)
11.299837955011753
>>> timeit.timeit('f()', 'from __main__ import mine as f', number=3)
5.330044150992762
您可以尝试通过将单个字符直接写入
标准输出来避免
'.join()
开销:

from sys import stdout
from random import choice
from string import ascii_lowercase

for _ in range(1000000):
    stdout.write(choice(ascii_lowercase))
下一步要尝试的是写入原始字节:

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))
但与我的测试中的
'.join()
相比,这些都没有改进

接下来,我们将ASCII字符编码为字节一次,然后使用
bytes.join()

bal
是编码为字节的小写ASCII字符列表,我们从中随机选取100万个项目,将它们合并成一个大字节字符串,然后一次性将其写入二进制标准输出缓冲区

字节联接与字符串版本一样“慢”:

>>> timeit.timeit('f()', 'from __main__ import bytes as f', number=3)
5.41390264898655

但是我们编码26个字符,而不是100万个字符,因此写入阶段更快。

我刚刚接受的解决方案(python 2.7,执行时间:0.984):

访问列表元素比访问字符串更快

In [13]: from random import choice

In [14]: from string import ascii_lowercase

In [15]: lis = list(ascii_lowercase)

In [16]: %timeit ''.join(choice(lis) for _ in xrange(10**5))
1 loops, best of 3: 128 ms per loop

In [17]: %timeit ''.join(choice(ascii_lowercase) for _ in xrange(10**5))
1 loops, best of 3: 134 ms per loop
您不需要在这里使用
stdout
stdin
,因为大多数在线用户都会这样判断我们来测试您的脚本:

$python script.py <in.txt >out.txt

通过在原始解决方案中从randint(0,25)更改为int(random()*25),我获得了巨大的速度提升。在我的机器上,时间从2秒到0.6秒。如果您查看一下random.py代码,就会发现randint中充满了您不想要或不需要的检查


更新:Oops,按一关闭。您需要int(random()*26)。感谢

这是Python 3代码,它在
0.28
秒内生成1000000个“随机”小写字母(另请参见
0.11
-结尾处的秒解;@Ashwini Chaudhary的问题代码在我的机器上花费
0.55
秒,@Markku K.的代码--
0.53
):

%len_lc
扭曲了分布(请参见末尾关于如何修复它的内容),尽管它仍然满足以下条件(ascii、小写、1、2、3个字母序列的频率):

其中
勾选seq.py

#!/usr/bin/env python3
import sys
from collections import Counter
from string import ascii_lowercase

def main():
    limits = [40000, 2000, 100]

    s = sys.stdin.buffer.readline() # a single line
    assert 1000000 <= len(s) <= 1000002 # check length +/- newline
    s.decode('ascii','strict') # check ascii
    assert set(s) == set(ascii_lowercase.encode('ascii')) # check lowercase

    for n, lim in enumerate(limits, start=1):
        freq = Counter(tuple(s[i:i+n]) for i in range(len(s)))
        assert max(freq.values()) <= lim, freq

main()
如何修复
%len\u lc
歪斜
256
(字节数)不能被
26
(小写拉丁字母数)平均整除,因此公式
min_lc+b%len_lc
使某些值的出现频率低于其他值,例如:

#!/usr/bin/env python3
"""Find out skew: x = 97 + y % 26 where y is uniform from [0, 256) range."""
from collections import Counter, defaultdict

def find_skew(random_bytes):
    char2freq = Counter(chr(ord(b'a') + b % 26) for b in random_bytes)
    freq2char = defaultdict(set)
    for char, freq in char2freq.items():
        freq2char[freq].add(char)
    return {f: ''.join(sorted(c)) for f, c in freq2char.items()}

print(find_skew(range(256)))
# -> {9: 'wxyz', 10: 'abcdefghijklmnopqrstuv'}
在这里,输入
范围(256)
是均匀分布的(每个字节恰好出现一次),但是输出中
'wxyz'
字母的出现频率低于其余
9
10
的出现频率。要修复此问题,可以删除未对齐的字节:

print(find_skew(range(256 - (256 % 26))))
# -> {9: 'abcdefghijklmnopqrstuvwxyz'}
这里,输入是在
[0234)
范围内均匀分布的字节,输出是均匀分布的ascii小写字母

bytes.translate()
接受第二个参数以指定要删除的字节:

#!/usr/bin/env python3
import os
import sys

nbytes = 256
nletters = 26
naligned = nbytes - (nbytes % nletters)
tbl = bytes.maketrans(bytearray(range(naligned)),
                      bytearray([ord(b'a') + b % nletters
                                 for b in range(naligned)]))
bytes2delete = bytearray(range(naligned, nbytes))
R = lambda n: os.urandom(n).translate(tbl, bytes2delete)

def write_random_ascii_lowercase_letters(write, n):
    """*write* *n* random ascii lowercase letters."""    
    while n > 0:
        # R(n) expected to drop `(nbytes - nletters) / nbytes` bytes
        # to compensate, increase the initial size        
        n -= write(memoryview(R(n * nbytes // naligned + 1))[:n])

write = sys.stdout.buffer.write
write_random_ascii_lowercase_letters(write, 1000000)
如果随机生成器(
os.uradom
此处)生成超出对齐范围(
=234
)的长字节序列,则
while
循环可能会执行多次

如果使用而不是使用,时间性能可以提高另一个数量级。前者使用Mersenne Twister作为核心生成器,可能比使用操作系统提供的源的
os.uradom()
更快。如果使用随机字符串作为机密,后者更安全。

使用

在Python 3.6上:

import random import string %timeit ''.join(random.choices(string.ascii_lowercase, k=10**6)) 1 loop, best of 3: 235 ms per loop 随机输入 导入字符串 %timeit“”。联接(随机的.choices(string.ascii_小写,k=10**6)) 1个回路,最好为3:235毫秒/回路 执行时间0.51s

from sys import stdout
from string import ascii_lowercase
l = 1000000
q = ['a']*l
lc = list(ascii_lowercase)
c = 0
for i in range(0,l-2,3):
    j = i // 3
    j_26 = j // 26
    q[i]= lc[j_26 // 26 % 26]
    q[i+1] = lc[j_26 % 26]
    q[i+2] = lc[j % 26]

stdout.write(''.join(q))
也许:


我可以用C++来做。我想知道:有没有办法做Python?@ ILaleX:看。