Эта задача заключается в чтении случайных строк из потенциально огромного файла без чтения всего файла в память.
вход
Целое число n
и имя текстового файла.
Выход
n
Строки текстового файла выбираются равномерно, произвольно, без замены.
Вы можете предположить, что n
находится в диапазоне от 1 до количества строк в файле.
Будьте осторожны, когда n
случайная выборка чисел из диапазона, который вы получите, является равномерным. rand()%n
в C не является равномерным, например. Каждый результат должен быть одинаково вероятным.
Правила и ограничения
Каждая строка текстового файла будет иметь одинаковое количество символов, и это будет не более 80.
Ваш код не должен читать содержимое текстового файла, кроме:
- Те строки это выводит.
- Первая строка для определения количества символов в строке в текстовом файле.
Мы можем предположить, что каждый символ в текстовом файле занимает ровно один байт.
Предполагается, что разделители строк имеют длину 1 байт. Решения могут использовать двухбайтовые разделители длинных строк, только если они указывают эту необходимость. Вы также можете предположить, что последняя строка заканчивается разделителем строк.
Ваш ответ должен быть полной программой, но вы можете указать ввод любым удобным для вас способом.
Языки и библиотеки
Вы можете использовать любой язык или библиотеку, которая вам нравится.
Примечания
Была проблема с подсчетом количества строк в файле. Как указывает Ними в комментариях, вы можете определить это по размеру файла и количеству символов в строке.
мотивация
В чате некоторые люди спрашивали, действительно ли это вопрос «Делай Х без Y». Я интерпретирую это, чтобы спросить, являются ли ограничения необычно искусственными.
Задача случайной выборки строк из огромных файлов не является чем-то необычным, и на самом деле мне иногда приходится это делать. Один из способов сделать это в bash:
shuf -n <num-lines>
Это, однако, очень медленно для больших файлов, поскольку он читает весь файл.
fseek
, и невозможно в других. Кроме того, что еслиn
число строк в файле больше?sum()
. Не чтение файла в память является четким и последовательным ограничением, которое ни в коем случае не является произвольным. Его можно протестировать с файлом, превышающим объем памяти, который нельзя обойти из-за языковых различий. Также бывает, что в реальных приложениях (хотя это не обязательно для гольфа ...).Ответы:
Дьялог АПЛ , 63 байта
Запрашивает имя файла, затем сколько произвольных строк желательно.
объяснение
⍞
Запрос на ввод текста (имя файла)⎕NTIE 0
Свяжите файл, используя следующий доступный номер связи (-1 в чистой системе).t←
Сохраните выбранный номер связи какt
83 80,⍨
Добавить [83,80], получив [-1,83,80].⎕NREAD
Считайте первые 80 байтов. файла -1 как 8-битные целые числа (код преобразования 83)10⍳⍨
Найти индекс первого числа 10 (LF)l←
Сохранить длину строки какl
(⎕NSIZE t)÷
Делить размер файла -1 на длину⎕
строки Запрашивать числовой ввод (нужное количество строк) )?
X случайные выборки (без замены) из первых натуральных чисел Y¯1+
Добавьте -1, чтобы получить номера строк 0-начала *l×
Умножьте на длину строки, чтобы получить начальные байты.t 82l∘,¨
Prepend [-1,82, LineLength] к каждому начальному байту (создает список аргументов для⎕NREAD
)⎕NREAD¨
Читайте каждую строку как 8-битный символ (код преобразования 82)Практический пример
Файл /tmp/records.txt содержит:
Сделайте так, чтобы программа RandLines содержала вышеуказанный код дословно, введя в сеанс APL следующее:
В сеансе APL введите
RandLines
и нажмите Enter.Система перемещает курсор на следующую строку, которая является подсказкой длины 0 для символьных данных; войти
/tmp/records.txt
.Теперь система выводит
⎕:
и ожидает числовой ввод; войти4
.Система выводит четыре случайные строки.
Реальная жизнь
В действительности вы можете указать имя файла и считать его аргументами и получить результат в виде таблицы. Это можно сделать, введя:
Теперь вы делаете MyLines содержать три случайные строки с:
Как насчет возврата только одной случайной строки, если count не указан:
Теперь вы можете сделать оба:
и (обратите внимание на отсутствие левого аргумента):
Делаем код читабельным
Гольф-однострочные APL - плохая идея. Вот как я бы написал в производственной системе:
* Я мог бы сохранить байт, запустив его в режиме 0-origin, который является стандартным для некоторых систем APL: удалить
¯1+
и вставить1+
до10
.источник
Рубин,
104949290 байтовИмя файла и количество строк передаются в командную строку. Например, если программа имеет
shuffle.rb
имя файлаa.txt
, запуститеruby shuffle.rb a.txt 3
три случайные строки.-4 байта от обнаружения
open
синтаксиса в Ruby вместоFile.new
Кроме того, вот 85-байтовое решение анонимной функции, которое принимает строку и число в качестве аргументов.
источник
ruby shuffle.rb 3 < a.txt
дает вам поиск стандартного ИДК Руби, правда.Haskell,
240224236 байтЧитает имя файла и n из стандартного ввода.
Как это устроено:
Запуск этой программы для файлов с несколькими строками занимает много времени и памяти из-за ужасной неэффективности
shuffle
функции.Редактировать: я пропустил часть "случайно без замены" (спасибо @feersum за то, что заметил!).
источник
PowerShell v2 +, 209 байт
Принимает ввод
$a
как имя файла и$n
как количество строк. Обратите внимание, что это$a
должно быть полное имя файла и предполагается, что это кодировка ANSI.Затем мы создаем новый
FileStream
объект и сообщаем ему доступ к файлу$a
сOpen
привилегиями.for
цикл.Read()
с через первую линию , пока мы не ударили\n
характер, увеличивающийся наш линейный счетчик длины каждого символ. Затем мы устанавливаем$t
равный размеру файла (т. Е. Длине потока), деленному на количество символов в строке (плюс один, чтобы он считал терминатор), минус один (поскольку мы проиндексированы нулями). Затем мы строим наш буфер так же,$z
чтобы он был длиной строки.Последняя строка создает динамический массив с
..
оператором диапазона. 1 Мы передаем этот массивGet-Random
со-C
множеством,$n
чтобы случайным образом выбрать$n
номера строк без повторения. Счастливые числа заключены в петлю с|%{...}
. Каждую итерацию мы сохраняем в.Seek
определенном месте, а затем.Read
в строке символов$z
. Мы повторно бросили$z
как char-массив и-join
все вместе, оставляя результирующую строку в конвейере и вывод неявно в конце программы.технически мы должны завершить
$f.Close()
вызов, чтобы закрыть файл, но это стоит байтов! :ппример
1 Технически это означает, что мы можем поддерживать не более 50 000 строк, поскольку это самый большой диапазон, который можно динамически создать таким образом. : - / Но мы не можем просто зациклить время
Get-Random
команды$n
, отбрасывая дубликаты каждого цикла, так как это не является детерминированным ...источник
Python 3,
146139 байтВвод: [имя файла] \ n [строки] \ n
Это решение в значительной степени украдено у @pppery, но
является только python3.5 ипредставляет собой законченную программу.Редактировать: Спасибо @Mego за встроенный диапазон и совместимость с python3.x. Edit2: разъяснение, где печать, потому что я получил два комментария об этом. (Комментарий, очевидно, не является частью кода или количества байтов.)
источник
r=range(f.seek(0,2)//l)
будет работать, что сбрасывает 3 байта и устраняет необходимость в 3,5. Еще лучше, сбрейте еще 3 байта, вставивrange
вызов вsample
вызове. Кроме того, это не полная программа - вам нужно распечатать список.r=[*range(f.seek(0,2)//l)]
потому что я думал, что я не могsample
генератор. Оказывается, я мог. @Mega: он завершен, потому что печатает каждую строку внутри спискаprint(f.read(l))
Луа,
126122Использует 2 байта для переносов строк. Измените 2 на 1 для 1. У меня только 2, потому что это то, что было в моем тестовом файле.
Попал под запись PHP, но все же 2-е место (в настоящее время). Проклинаю тебя, рубиновая запись!
источник
Bash (ну, coreutils), 100 байт
объяснение
Это позволяет избежать чтения всего файла, используя
dd
для извлечения частей файла, которые нам нужны, без чтения файла полностью, к сожалению, он оказывается довольно большим со всеми опциями, которые мы должны указать:if
является ли входной файлbs
размером блока (здесь мы устанавливаем его,$n
длина первой строки которогоskip
равна случайным целым числам, извлеченным из негоshuf
и равняетсяibs
пропускаемому значениюskip
*ibs
байтовcount
- количествоibs
отрезков длины, которое требуется вернутьstatus=none
, необходимо вырезать информация обычно выводитсяdd
Мы получаем длину строки с помощью
head -1 $2|wc -c
и размер файла с помощьюstat -c%s $2
.использование
Сохранить выше как
file.sh
и запустить с помощьюfile.sh n filename
.Задержки
против
Время выше для файла 68MiB, созданного с использованием
seq 1000000 9999999 > test.txt
.Спасибо @PeterCordes за его -1 совет!
источник
bs=
вместо этогоibs=
, такobs
как настройка тоже хорошо. Я предполагаю , что вы не можете заменитьif=$2
с<$2
хотя, так как это все ещеxargs
«s командной строки.\<$2
тоже не работает (xargs использует exec напрямую, без оболочки).2>&-
, чтобы не было никакой опасности, что вывод куда-либо будет (например, если stdin оказался дескриптором файла для чтения и записи). Он работает с GNUdd
: он все еще выдает егоstdout
до того, как попытается и не сможет записатьstderr
.Python 3 -
161 160149 байтЭтот код определяет функцию, которая называется как
f(10,'input.txt')
источник
C # 259 байт без дубликатов
Ungolfed
File.ReadLines является Ленивым. Это дает дополнительное преимущество: все строки могут иметь разную длину.
Запуск это будет:
C # 206 байт с дубликатами
Ungolfed
источник
Python (141 байт)
Сохраняет каждую строку с равной вероятностью, используйте также с трубами. Это не отвечает на пропущенное ограничение вопроса, хотя ...
Использование
cat largefile | python randxlines.py 100
илиpython randxlines 100 < largefile
(как указано @petercordes)источник
python ./randxlines.py 100 < largefile
было бы хорошо для правильного ответа, однако: в этом случаеstdin
будет искать.