Este desafío se trata de leer líneas aleatorias de un archivo potencialmente enorme sin leer todo el archivo en la memoria.
Entrada
Un entero ny el nombre de un archivo de texto.
Salida
n líneas del archivo de texto elegidas uniformemente al azar sin reemplazo.
Puede suponer que nestá en el rango 1 al número de líneas en el archivo.
Tenga cuidado al muestrear nnúmeros al azar del rango de que la respuesta que obtiene es uniforme. rand()%nen C no es uniforme, por ejemplo. Cada resultado debe ser igualmente probable.
Reglas y restricciones
Cada línea del archivo de texto tendrá el mismo número de caracteres y no tendrá más de 80.
Su código no debe leer ninguno de los contenidos del archivo de texto, excepto:
- Esas líneas que emite.
- La primera línea para calcular cuántos caracteres por línea hay en el archivo de texto.
Podemos suponer que cada carácter en el archivo de texto toma exactamente un byte.
Se supone que los separadores de línea tienen 1 byte de longitud. Las soluciones pueden usar separadores de línea larga de 2 bytes solo si especifican esta necesidad. También puede suponer que la última línea termina con un separador de línea.
Su respuesta debe ser un programa completo, pero puede especificar la entrada de cualquier manera que sea conveniente.
Idiomas y bibliotecas
Puede usar cualquier idioma o biblioteca que desee.
Notas
Había una preocupación sobre el cálculo del número de líneas en el archivo. Como nimi señala en los comentarios, puede inferir esto a partir del tamaño del archivo y el número de caracteres por línea.
Motivación
En el chat, algunas personas preguntaron si esta es realmente una pregunta de "Hacer X sin Y". Interpreto esto para preguntar si las restricciones son inusualmente artificiales.
La tarea de muestrear aleatoriamente líneas de grandes archivos no es infrecuente y, de hecho, es algo que a veces tengo que hacer. Una forma de hacerlo es en bash:
shuf -n <num-lines>
Sin embargo, esto es muy lento para archivos grandes, ya que se lee en todo el archivo.

fseeke imposible en otros. Además, ¿quénpasa si es mayor que el número de líneas en el archivo?sum(). No leer un archivo en la memoria es una restricción clara y consistente que de ninguna manera es arbitraria. Se puede probar con un archivo más grande que la memoria, que no puede ser solucionado por las diferencias de idioma. También sucede que tiene aplicaciones en el mundo real (aunque eso no es necesario para un golf ...).Respuestas:
Dyalog APL , 63 bytes
Solicita el nombre del archivo, luego cuántas líneas aleatorias se desean.
Explicación
⍞Solicitar ingreso de texto (nombre de archivo)⎕NTIE 0Empate el archivo usando el siguiente número de enlace disponible (-1 en un sistema limpio)t←Almacene el número de enlace elegido comot83 80,⍨Anexar [83,80] produciendo [-1,83,80]⎕NREADLea los primeros 80 bytes del archivo -1 como enteros de 8 bits (código de conversión 83)10⍳⍨Encuentre el índice del primer número 10 (LF)l←Almacene la longitud de la línea comol(⎕NSIZE t)÷Divida el tamaño del archivo -1 con la longitud de la línea⎕Solicite la entrada numérica (número deseado de líneas )?X selecciones aleatorias (sin reemplazo) con los primeros números naturales Y¯1+Agregue -1 para obtener números de línea de origen 0 *l×Multiplique por la longitud de la línea para obtener los bytes de iniciot 82l∘,¨Prepend [-1,82, LineLength] a cada byte de inicio (crea lista de argumentos para⎕NREAD)⎕NREAD¨Lea cada línea como carácter de 8 bits (código de conversión 82)Ejemplo práctico
El archivo /tmp/records.txt contiene:
Haga que el programa RandLines contenga el código anterior literalmente ingresando lo siguiente en la sesión APL:
En la sesión APL, escriba
RandLinesy presione Entrar.El sistema mueve el cursor a la siguiente línea, que es una solicitud de longitud 0 para datos de caracteres; entrar
/tmp/records.txt.El sistema ahora emite
⎕:y espera entrada numérica; entrar4.El sistema genera cuatro líneas aleatorias.
Vida real
En realidad, es posible que desee dar nombre de archivo y contar como argumentos y recibir el resultado como una tabla. Esto se puede hacer ingresando:
Ahora haces que MyLines contenga tres líneas aleatorias con:
¿Qué tal devolver solo una línea aleatoria si no se especifica el recuento:
Ahora puedes hacer ambas cosas:
y (observe la ausencia de argumento izquierdo):
Hacer el código legible
Las frases de golf APL son una mala idea. Así es como escribiría en un sistema de producción:
* Podría guardar un byte ejecutándolo en modo de origen 0, que es estándar en algunos sistemas APL: eliminar
¯1+e insertar1+antes10.fuente
Ruby,
104949290 bytesEl nombre del archivo y el número de líneas se pasan a la línea de comando. Por ejemplo, si el programa es
shuffle.rby el nombre del archivo esa.txt, ejecuteruby shuffle.rb a.txt 3tres líneas aleatorias.-4 bytes desde el descubrimiento de la
opensintaxis en Ruby en lugar deFile.newAdemás, aquí hay una solución de función anónima de 85 bytes que toma una cadena y un número como argumentos.
fuente
ruby shuffle.rb 3 < a.txtte da un stdin buscable. IDK Ruby, sin embargo.Haskell,
240224236 bytesLee el nombre de archivo yn de stdin.
Cómo funciona:
Se necesita mucho tiempo y memoria para ejecutar este programa para archivos con muchas líneas, debido a una
shufflefunción horrible e ineficiente .Editar: me perdí la parte "aleatorio sin reemplazo" (¡gracias @feersum por notarlo!).
fuente
PowerShell v2 +, 209 bytes
Toma la entrada
$acomo el nombre del archivo y$ncomo el número de líneas. Tenga en cuenta que$adebe ser un nombre de archivo de ruta completa y se supone que es una codificación ANSI.Luego construimos un nuevo
FileStreamobjeto y le decimos que acceda al archivo$aconOpenprivilegios.El
forbucle.Read()atraviesa la primera línea hasta que llegamos a un\npersonaje, incrementando nuestro contador de longitud de línea para cada personaje. Luego establecemos$tigual al tamaño del archivo (es decir, cuánto dura la secuencia) dividido por cuántos caracteres por línea (más uno para que cuente el terminador), menos uno (ya que estamos indexados a cero). Luego construimos nuestro buffer$zpara que también sea la longitud de la línea.La línea final construye una matriz dinámica con el
..operador de rango. 1 Canalizamos esa matrizGet-Randomcon la-Cposibilidad de$nseleccionar aleatoriamente$nnúmeros de línea sin repetición. Los números de la suerte se canalizan en un bucle con|%{...}. Cada iteración nos lleva.Seeka la ubicación particular, y luego.Readen el valor de una línea de caracteres, almacenados en$z. Lo relanzamos$zcomo un conjunto de caracteres y-joinlo juntamos, dejando la cadena resultante en la tubería y la salida está implícita al final del programa.Técnicamente deberíamos terminar con una
$f.Close()llamada para cerrar el archivo, ¡pero eso cuesta bytes! :pagEjemplo
1 Técnicamente, esto significa que solo podemos admitir un máximo de 50,000 líneas, ya que ese es el rango más grande que se puede crear dinámicamente de esta manera. : - / Pero, no podemos simplemente repetir un
Get-Randomcomando$nveces, descartando duplicados en cada ciclo, ya que eso no es determinista ...fuente
Python 3,
146139 bytesEntrada: [nombre de archivo] \ n [líneas] \ n
Esta solución robó mucho de @pppery, pero
solo es python3.5 yes un programa completo.Editar: Gracias a @Mego por el rango en línea y la compatibilidad con python3.x. Edit2: Aclaración de dónde está la impresión porque recibí dos comentarios al respecto. (El comentario obviamente no es parte del código o el recuento de bytes).
fuente
r=range(f.seek(0,2)//l)funcionará, lo que reduce 3 bytes y elimina la necesidad de 3.5. Aún mejor, ahorre 3 bytes más al incluir larangellamada en lasamplellamada. Además, este no es un programa completo: debe imprimir la lista.r=[*range(f.seek(0,2)//l)]porque pensé que no podíasampleun generador. Resulta que pude. @Mega: Está completo porque imprime cada línea dentro de la comprensión de la listaprint(f.read(l))Lua,
126122Utiliza 2 bytes para saltos de línea. Cambie el 2 a 1 por 1. Solo lo tengo como 2 porque eso es lo que tenía mi archivo de prueba.
Me metí debajo de la entrada de PHP, pero aún estoy en segundo lugar (actualmente). ¡Maldito seas, Ruby, entrada!
fuente
Bash (bueno, coreutils), 100 bytes
Explicación
Esto evita leer todo el archivo usando
ddpara extraer las partes del archivo que necesitamos sin leerlo por completo, desafortunadamente termina siendo bastante grande con todas las opciones que tenemos que especificar:ifes el archivo de entradabses el tamaño de bloque (aquí lo establecemos en$ncuál es la longitud de la primera líneaskipse establece en los enteros aleatorios extraídos deshufy equivale alibsvalor omitidoskip*ibsbytes)countel número deibssecciones de longitud a devolverstatus=nonees necesario para eliminar la información que normalmente generaddObtenemos la longitud de la línea usando
head -1 $2|wc -cy el tamaño del archivo constat -c%s $2.Uso
Guardar arriba como
file.shy ejecutar usandofile.sh n filename.Tiempos
vs.
Tiempos anteriores para un archivo de 68MiB generado usando
seq 1000000 9999999 > test.txt.¡Gracias a @PeterCordes por su punta -1!
fuente
bs=hacerlo en lugar de hacerloibs=, ya que la configuraciónobstambién está bien. Supongo que no se puede reemplazarif=$2con el<$2embargo, ya que esto sigue siendoxargs's línea de comandos.\<$2tampoco funciona (xargs usa exec directamente, sin un shell).2>&-, por lo que no hay peligro de que la salida vaya a ninguna parte (por ejemplo, si stdin es un descriptor de archivo de lectura y escritura). Funciona con GNUdd: todavía produce sustdoutantes de intentar y no escribirstderr.Python 3 -
161 160149 bytesEste código define una función que se llama como
f(10,'input.txt')fuente
C # 259 bytes sin duplicados
Sin golf
File.ReadLines es perezoso. Esto tiene el beneficio adicional de que todas las líneas pueden tener una longitud diferente.
Ejecutarlo sería:
C # 206 bytes con duplicados
Sin golf
fuente
Python (141 bytes)
Mantiene cada línea con la misma probabilidad, use también con tuberías. Sin embargo, no responde la limitación de omisión de la pregunta ...
Uso
cat largefile | python randxlines.py 100opython randxlines 100 < largefile(como señaló @petercordes)fuente
python ./randxlines.py 100 < largefileembargo, leer de stdin con estaría bien para una respuesta adecuada: en ese casostdinserá buscable.