Похоже, что это лучшее решение для больших входных файлов:
awk -v seed="$RANDOM" -v max="$(wc -l < file)" 'BEGIN{srand(seed); n=int(rand()*max)+1} NR==n{print; exit}' file
, поскольку оно использует стандартные инструменты UNIX, оно не ограничивается файлами длиной 32 769 строк или меньше, не имеет каких-либосмещение к любому концу ввода, он будет производить различный вывод, даже если вызывается дважды за 1 секунду, и он выходит сразу после печати целевой строки, а не продолжается до конца ввода.
Обновление:
С учетом вышесказанного у меня нет объяснения, почему скрипт, который вызывает rand () один раз в строке и читает каждую строку ввода, примерно в два раза быстрее, чем скрипткоторый вызывает rand () один раз и выходит в первой строке соответствия:
$ seq 100000 > file
$ time for i in $(seq 500); do
awk -v seed="$RANDOM" -v max="$(wc -l < file)" 'BEGIN{srand(seed); n=int(rand()*max)+1} NR==n{print; exit}' file;
done > o3
real 1m0.712s
user 0m8.062s
sys 0m9.340s
$ time for i in $(seq 500); do
awk -v seed="$RANDOM" 'BEGIN{srand(seed)} rand() < 1/NR{ out=$0 } END { print out}' file;
done > o4
real 0m29.950s
user 0m9.918s
sys 0m2.501s
Они оба выдали очень похожие типы вывода:
$ awk '{a[$0]++} END { for (i in a) print i, a[i]}' o3 | awk '{sum+=$2; max=(NR>1&&max>$2?max:$2); min=(NR>1&&min<$2?min:$2)} END{print NR, sum, min, max}'
498 500 1 2
$ awk '{a[$0]++} END { for (i in a) print i, a[i]}' o4 | awk '{sum+=$2; max=(NR>1&&max>$2?max:$2); min=(NR>1&&min<$2?min:$2)} END{print NR, sum, min, max}'
490 500 1 3
Окончательное обновление:
Оказывается, он звонил wc
, что (неожиданно для меня по крайней мере!) Занимало большую часть времени.Вот улучшение, когда мы вынимаем его из цикла:
$ time { max=$(wc -l < file); for i in $(seq 500); do awk -v seed="$RANDOM" -v max="$max" 'BEGIN{srand(seed); n=int(rand()*max)+1} NR==n{print; exit}' file; done } > o3
real 0m24.556s
user 0m5.044s
sys 0m1.565s
, поэтому решение, в котором мы вызываем wc
вперед и rand()
один раз, быстрее, чем вызов rand()
для каждой строки, как и ожидалось.