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 n
y el nombre de un archivo de texto.
Salida
n
líneas del archivo de texto elegidas uniformemente al azar sin reemplazo.
Puede suponer que n
está en el rango 1 al número de líneas en el archivo.
Tenga cuidado al muestrear n
números al azar del rango de que la respuesta que obtiene es uniforme. rand()%n
en 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.
fseek
e imposible en otros. Además, ¿quén
pasa 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 0
Empate el archivo usando el siguiente número de enlace disponible (-1 en un sistema limpio)t←
Almacene el número de enlace elegido comot
83 80,⍨
Anexar [83,80] produciendo [-1,83,80]⎕NREAD
Lea 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
RandLines
y 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.rb
y el nombre del archivo esa.txt
, ejecuteruby shuffle.rb a.txt 3
tres líneas aleatorias.-4 bytes desde el descubrimiento de la
open
sintaxis en Ruby en lugar deFile.new
Ademá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.txt
te 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
shuffle
función horrible e ineficiente .Editar: me perdí la parte "aleatorio sin reemplazo" (¡gracias @feersum por notarlo!).
fuente
PowerShell v2 +, 209 bytes
Toma la entrada
$a
como el nombre del archivo y$n
como el número de líneas. Tenga en cuenta que$a
debe ser un nombre de archivo de ruta completa y se supone que es una codificación ANSI.Luego construimos un nuevo
FileStream
objeto y le decimos que acceda al archivo$a
conOpen
privilegios.El
for
bucle.Read()
atraviesa la primera línea hasta que llegamos a un\n
personaje, incrementando nuestro contador de longitud de línea para cada personaje. Luego establecemos$t
igual 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$z
para 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-Random
con la-C
posibilidad de$n
seleccionar aleatoriamente$n
números de línea sin repetición. Los números de la suerte se canalizan en un bucle con|%{...}
. Cada iteración nos lleva.Seek
a la ubicación particular, y luego.Read
en el valor de una línea de caracteres, almacenados en$z
. Lo relanzamos$z
como un conjunto de caracteres y-join
lo 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-Random
comando$n
veces, 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 larange
llamada en lasample
llamada. Además, este no es un programa completo: debe imprimir la lista.r=[*range(f.seek(0,2)//l)]
porque pensé que no podíasample
un 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
dd
para extraer las partes del archivo que necesitamos sin leerlo por completo, desafortunadamente termina siendo bastante grande con todas las opciones que tenemos que especificar:if
es el archivo de entradabs
es el tamaño de bloque (aquí lo establecemos en$n
cuál es la longitud de la primera líneaskip
se establece en los enteros aleatorios extraídos deshuf
y equivale alibs
valor omitidoskip
*ibs
bytes)count
el número deibs
secciones de longitud a devolverstatus=none
es necesario para eliminar la información que normalmente generadd
Obtenemos la longitud de la línea usando
head -1 $2|wc -c
y el tamaño del archivo constat -c%s $2
.Uso
Guardar arriba como
file.sh
y 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ónobs
también está bien. Supongo que no se puede reemplazarif=$2
con el<$2
embargo, ya que esto sigue siendoxargs
's línea de comandos.\<$2
tampoco 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 sustdout
antes 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 100
opython randxlines 100 < largefile
(como señaló @petercordes)fuente
python ./randxlines.py 100 < largefile
embargo, leer de stdin con estaría bien para una respuesta adecuada: en ese casostdin
será buscable.