Как сгенерировать 1 миллион случайных чисел и записать их в файл? - PullRequest
4 голосов
/ 06 октября 2011

Я пытался провести несколько тестов на моих внешних алгоритмах сортировки, и я подумал, что мне нужно сгенерировать огромное количество случайных чисел и поместить их в файл.

Вот как я это делаю:

import tempfile, random

nf = tempfile.NamedTemporaryFile(delete=False)
i = 0
while i < 1000:
    j = 0
    buf = ''
    while j < 1000:
        buf += str(random.randint(0, 1000))
        j += 1
    nf.write(buf)
    i += 1

Я подумал, что я должен ускорить процесс генерации на , уменьшив операции ввода-вывода файла , поэтому я использую buf, чтобы сохранить как можно больше чисел, а затем записать в файл buf.

Вопрос:

Я все еще чувствовал, что процесс генерации и записи был медленным.

Я что-то не так делаю?

РЕДАКТИРОВАНИЕ:

В C ++ мы можем просто записать int или float в файл с помощью << без converting them в строку.

Так можем ли мы сделать то же самое в Python?Я имею в виду записать целое число в файл без преобразования его в стр.

Ответы [ 6 ]

7 голосов
/ 06 октября 2011

Операционные системы уже оптимизированы для таких операций ввода-вывода. Таким образом, вы можете напрямую записать числа в файл и получить очень хорошую скорость:

import tempfile, random

with tempfile.NamedTemporaryFile(delete=False) as nf:
    for _ in xrange(1000000):  # xrange() is more efficient than range(), in Python 2
        nf.write(str(random.randint(0, 1000)))

На практике числа будут записываться на диск только тогда, когда оптимизированный по размеру файловый буфер заполнен. Код в вопросе и код выше занимают одно и то же время на моей машине. Поэтому я бы посоветовал использовать мой более простой код и полагаться на встроенные оптимизации операционной системы.

Если результат помещается в памяти (как в случае с 1 миллионом чисел), тогда вы действительно можете сохранить некоторые операции ввода-вывода, создав последнюю строку и записав ее за один раз:

with tempfile.NamedTemporaryFile(delete=False) as nf:
    nf.write(''.join(str(random.randint(0, 1000)) for _ in xrange(1000000)))

Этот второй подход на моем компьютере работает на 30% быстрее (2,6 с вместо 3,8 с), вероятно, благодаря единственному вызову записи (вместо миллиона write() вызовов - и, возможно, гораздо меньшему количеству фактических операций записи на диск).

Подход "много больших записей" в вашем вопросе находится посередине (3,1 с). Это может быть улучшено, хотя: это более ясно и более Pythonic написать это так:

import tempfile, random

with tempfile.NamedTemporaryFile(delete=False) as nf:
    for _ in xrange(1000):
        nf.write(''.join(str(random.randint(0, 1000)) for _ in xrange(1000)))

Это решение эквивалентно, но быстрее, чем код в исходном вопросе (2,6 с на моем компьютере вместо 3,8 с).

Таким образом, первый простой подход, описанный выше, может быть достаточно быстрым для вас. Если это не так, и если весь файл может поместиться в памяти, второй подход является очень быстрым и простым. В противном случае, ваша первоначальная идея (меньше записей, большие блоки) хороша, так как она примерно такая же быстрая, как и подход с «одной записью», и все же довольно проста, когда написана, как указано выше.

3 голосов
/ 06 октября 2011

Не используйте конкатенацию строк в цикле. Вместо этого используйте str.join.

Детали реализации CPython: если s и t являются обеими строками, некоторые реализации Python, такие как CPython, обычно могут выполнять оптимизацию на месте для назначений вида s = s + t или s + = t. Когда это применимо, эта оптимизация делает квадратичное время выполнения гораздо менее вероятным. Эта оптимизация зависит как от версии, так и от реализации. Для чувствительного к производительности кода предпочтительно использовать метод str.join (), который обеспечивает согласованную производительность линейной конкатенации в разных версиях и реализациях.

Ваш код будет выглядеть так:

buf = ''.join(str(random.randint(0, 1000)) for j in range(1000))

И обратите внимание, что поскольку вы не указали разделитель, он будет выглядеть следующим образом:

3847018274193258124003837134....

Измените '' на ',', если хотите, чтобы числа были (например) разделены запятой.

Я также не думаю, что вам нужно буферизовать себя, поскольку запись в файл уже должна быть буферизована.

2 голосов
/ 06 октября 2011

Если вам нужно только сгенерировать несколько случайных чисел, и вы находитесь под Linux, попробуйте команду оболочки

for i in {1..1000000}; do echo $[($RANDOM % 1000)]; done > test.in

хорошо, я тестирую этот код ниже, для завершения

* 1005 требуется около 5 секунд*
1 голос
/ 06 октября 2011

Как это

import random
import struct

with open('binary.dat','wb') as output:
     for i in xrange(1000000):
         u = random.randint(0,999999) # number
         b = struct.pack('i', u) # bytes
         output.write(b)

Это создаст 4 миллиона байтов данных.1 миллион 4-байтовых значений.

Вы можете прочитать о struct и различных вариантах упаковки здесь: http://docs.python.org/library/struct.html.

1 голос
/ 06 октября 2011

Я не уверен в Python, но + = обычно дорогая операция, поскольку копирует строку в новую память.

Использование какого-либо строителя строк или массива, к которому вы присоединяетесь, возможно, намного быстрее.

0 голосов
/ 06 октября 2011

Выполнение миллиона всего будет относительно медленным. Кроме того, в зависимости от того, насколько случайным вы хотите числа, вы можете инвестировать в более надежный генератор случайных целых чисел. Это личный фаворит: http://en.wikipedia.org/wiki/Mersenne_twister

...