Ce défi consiste à lire des lignes aléatoires à partir d'un fichier potentiellement énorme sans lire l'intégralité du fichier en mémoire.
Contribution
Un entier n
et le nom d'un fichier texte.
Production
n
lignes du fichier texte choisies uniformément au hasard sans remplacement.
Vous pouvez supposer qu'il n
est compris entre 1 et le nombre de lignes du fichier.
Soyez prudent lorsque vous échantillonnez des n
nombres au hasard dans la plage où la réponse que vous obtenez est uniforme. rand()%n
en C n'est pas uniforme par exemple. Chaque résultat doit être également probable.
Règles et restrictions
Chaque ligne du fichier texte aura le même nombre de caractères et ce ne sera pas plus de 80.
Votre code ne doit pas lire le contenu du fichier texte, sauf:
- Ces lignes qu'il produit.
- La première ligne pour déterminer le nombre de caractères par ligne dans le fichier texte.
Nous pouvons supposer que chaque caractère du fichier texte prend exactement un octet.
Les séparateurs de ligne sont supposés faire 1 octet de long. Les solutions peuvent utiliser des séparateurs de ligne de 2 octets uniquement si elles spécifient ce besoin. Vous pouvez également supposer que la dernière ligne se termine par un séparateur de ligne.
Votre réponse doit être un programme complet mais vous pouvez spécifier l'entrée de la manière qui vous convient.
Langues et bibliothèques
Vous pouvez utiliser n'importe quelle langue ou bibliothèque que vous aimez.
Remarques
Il y avait une préoccupation au sujet du calcul du nombre de lignes dans le fichier. Comme le souligne nimi dans les commentaires, vous pouvez en déduire la taille du fichier et le nombre de caractères par ligne.
Motivation
Dans le chat, certaines personnes ont demandé s'il s'agissait vraiment d'une question "Do X without Y". J'interprète cela pour demander si les restrictions sont inhabituellement artificielles.
La tâche d'échantillonnage aléatoire de lignes à partir de fichiers volumineux n'est pas rare et en fait, je dois parfois le faire. Une façon de le faire est en bash:
shuf -n <num-lines>
Ceci est cependant très lent pour les fichiers volumineux car il lit dans tout le fichier.
fseek
, et impossible dans d'autres. De plus, que se passe-t-il sin
est supérieur au nombre de lignes dans le fichier?sum()
. Ne pas lire un fichier en mémoire est une restriction claire et cohérente qui n'est en aucune façon arbitraire. Il peut être testé avec un fichier plus grand que la mémoire, qui ne peut pas être contourné par les différences de langue. Il arrive également d'avoir des applications réelles (bien que cela ne soit pas nécessaire pour un golf ...).Réponses:
Dyalog APL , 63 octets
Demande le nom du fichier, puis le nombre de lignes aléatoires souhaitées.
Explication
⍞
Demander la saisie de texte (nom de fichier)⎕NTIE 0
Lier le fichier en utilisant le prochain numéro de lien disponible (-1 sur un système propre)t←
Stocker le numéro de lien choisi sous la formet
83 80,⍨
Ajouter [83,80] donnant [-1,83,80]⎕NREAD
Lire les 80 premiers octets du fichier -1 sous forme d'entiers 8 bits (code de conversion 83)10⍳⍨
Trouver l'index du premier nombre 10 (LF)l←
Stocker la longueur de ligne sousl
(⎕NSIZE t)÷
Diviser la taille du fichier -1 avec la longueur de ligne⎕
Demander une entrée numérique (nombre de lignes souhaité )?
X sélections aléatoires (sans remplacement) sur les premiers Y nombres naturels¯1+
Ajoutez -1 pour obtenir des numéros de ligne d'origine 0 *l×
Multipliez par la longueur de ligne pour obtenir les octets de débutt 82l∘,¨
Ajoutez [-1,82, LineLength] à chaque octet de début (crée liste des arguments pour⎕NREAD
)⎕NREAD¨
Lire chaque ligne sous forme de caractère 8 bits (code de conversion 82)Exemple pratique
Le fichier /tmp/records.txt contient:
Faites en sorte que le programme RandLines contienne le code ci-dessus textuellement en entrant ce qui suit dans la session APL:
Dans le type de session APL
RandLines
et appuyez sur Entrée.Le système déplace le curseur sur la ligne suivante, qui est une invite de longueur 0 pour les données de caractères; entrez
/tmp/records.txt
.Le système sort maintenant
⎕:
et attend une entrée numérique; entrez4
.Le système génère quatre lignes aléatoires.
Vrai vie
En réalité, vous voudrez peut-être donner un nom de fichier et compter comme arguments et recevoir le résultat sous forme de tableau. Cela peut être fait en entrant:
Maintenant, vous faites MyLines contenir trois lignes aléatoires avec:
Que diriez-vous de renvoyer une seule ligne aléatoire si le nombre n'est pas spécifié:
Maintenant, vous pouvez faire les deux:
et (notez l'absence d'argument de gauche):
Rendre le code lisible
Les monoplaces APL au golf sont une mauvaise idée. Voici comment j'écrirais dans un système de production:
* Je pourrais enregistrer un octet en exécutant en mode 0 d'origine, ce qui est standard sur certains systèmes APL: supprimez
¯1+
et insérez1+
avant10
.la source
Rubis,
104949290 octetsLe nom de fichier et le nombre de lignes sont passés dans la ligne de commande. Par exemple, si le programme est
shuffle.rb
et le nom de fichier esta.txt
, exécutezruby shuffle.rb a.txt 3
pour trois lignes aléatoires.-4 octets de découverte du
open
syntaxe dans Ruby au lieu deFile.new
En outre, voici une solution de fonction anonyme de 85 octets qui prend une chaîne et un nombre comme arguments.
la source
ruby shuffle.rb 3 < a.txt
vous donne un stdin recherché. IDK Ruby, cependant.Haskell,
240224236 octetsLit le nom de fichier et n depuis stdin.
Comment ça fonctionne:
Il faut beaucoup de temps et de mémoire pour exécuter ce programme pour les fichiers avec de nombreuses lignes, en raison d'une horrible
shuffle
fonction inefficace .Edit: j'ai raté la partie "aléatoire sans remplacement" (merci @feersum pour l'avoir remarqué!).
la source
PowerShell v2 +, 209 octets
Prend l'entrée
$a
comme nom de fichier et$n
comme nombre de lignes. Notez qu'il$a
doit s'agir d'un nom de fichier à chemin complet et supposé être un codage ANSI.Nous construisons ensuite un nouvel
FileStream
objet et lui demandons d'accéder au fichier$a
avecOpen
privilège.La
for
boucle.Read()
parcourt la première ligne jusqu'à ce que nous touchions un\n
caractère, en incrémentant notre compteur de longueur de ligne à chaque caractère. Nous avons ensuite mis$t
la taille du fichier (c'est-à-dire la durée du flux) divisée par le nombre de caractères par ligne (plus un pour qu'il compte le terminateur), moins un (car nous sommes indexés zéro). Nous construisons ensuite notre tampon$z
pour qu'il soit également de longueur de ligne.La dernière ligne construit un tableau dynamique avec l'
..
opérateur de plage. 1 Nous redirigeons ce tableau versGet-Random
avec un-C
nombre de$n
pour sélectionner aléatoirement des$n
numéros de ligne sans répétition. Les numéros porte-bonheur sont placés dans une boucle avec|%{...}
. Chaque itération nous.Seek
à l'emplacement particulier, puis.Read
dans la valeur d'une ligne de caractères, stockés dans$z
. Nous avons re-casté$z
comme un tableau de caractères et-join
ensemble, laissant la chaîne résultante sur le pipeline et la sortie est implicite à la fin du programme.Techniquement, nous devrions terminer par un
$f.Close()
appel pour fermer le fichier, mais cela coûte des octets! : pExemple
1 Techniquement, cela signifie que nous ne pouvons prendre en charge qu'un maximum de 50 000 lignes, car il s'agit de la plus grande gamme pouvant être créée dynamiquement de cette manière. : - / Mais, nous ne pouvons pas simplement boucler une
Get-Random
commande$n
fois, en supprimant les doublons de chaque boucle, car ce n'est pas déterministe ...la source
Python 3,
146139 octetsEntrée: [nom de fichier] \ n [lignes] \ n
Cette solution a volé lourdement à @pppery mais
est uniquement en python3.5 etest un programme complet.Edit: Merci à @Mego pour la gamme en ligne et la compatibilité python3.x. Edit2: Clarification de l'emplacement de l'impression car j'ai reçu deux commentaires à ce sujet. (Le commentaire ne fait évidemment pas partie du code ni du nombre d'octets.)
la source
r=range(f.seek(0,2)//l)
fonctionnera, ce qui rase 3 octets et supprime le besoin de 3.5. Encore mieux, rasez 3 octets de plus en insérant l'range
appel dans l'sample
appel. De plus, ce n'est pas un programme complet - vous devez réellement imprimer la liste.r=[*range(f.seek(0,2)//l)]
que je pensais que je ne pouvais pas desample
générateur. Il s'avère que je pouvais. @Mega: Il est complet car il imprime une ligne à l'intérieur de la liste de compréhensionprint(f.read(l))
Lua,
126122Utilise 2 octets pour les sauts de ligne. Remplacez le 2 par 1 pour 1. Je ne l'ai que 2 car c'est ce que mon fichier de test avait.
Je me suis placé sous l'entrée PHP, mais toujours la 2e place (actuellement). Je vous maudis, entrée Ruby!
la source
Bash (enfin, coreutils), 100 octets
Explication
Cela évite de lire le fichier entier en utilisant
dd
pour extraire les parties du fichier dont nous avons besoin sans lire le fichier entièrement, malheureusement cela finit assez grand avec toutes les options que nous devons spécifier:if
est le fichier d'entréebs
est la taille du bloc (ici, nous le définissons à$n
quelle est la longueur de la première ligneskip
est définie sur les entiers aléatoires extraits deshuf
et équivaut à laibs
valeur sautantskip
*ibs
octetscount
le nombre deibs
sections de longueur à retournerstatus=none
est nécessaire pour supprimer les informations normalement produites pardd
Nous obtenons la longueur de ligne en utilisant
head -1 $2|wc -c
et la taille du fichier avecstat -c%s $2
.Usage
Enregistrer ci-dessus sous
file.sh
et exécuter en utilisantfile.sh n filename
.Timings
contre.
Temps ci-dessus pour un fichier de 68 Mo généré à l'aide de
seq 1000000 9999999 > test.txt
.Merci à @PeterCordes pour son -1 conseil!
la source
bs=
au lieu deibs=
, car le réglage estobs
également très bien. Je suppose que vous ne pouvez pas remplacerif=$2
par<$2
cependant, car c'est toujoursxargs
la ligne de commande.\<$2
ne fonctionne pas non plus (xargs utilise directement exec, sans shell).2>&-
, donc il n'y a aucun danger que la sortie aille n'importe où (par exemple, si stdin se trouve être un descripteur de fichier en lecture-écriture). Il fonctionne avec GNUdd
: il produit toujours sonstdout
avant d'essayer et de ne pas écrirestderr
.Python 3 -
161 160 160149 octetsCe code définit une fonction qui s'appelle comme
f(10,'input.txt')
la source
C # 259 octets sans doublons
Non golfé
File.ReadLines est paresseux. Cela présente l'avantage supplémentaire que toutes les lignes peuvent avoir une longueur différente.
Le faire fonctionner serait:
C # 206 octets avec doublons
Non golfé
la source
Python (141 octets)
Conserve chaque ligne avec une probabilité égale, à utiliser également avec des tuyaux. Cependant, cela ne répond pas à la limitation de saut de la question ...
Utilisation
cat largefile | python randxlines.py 100
oupython randxlines 100 < largefile
(comme l'a souligné @petercordes)la source
python ./randxlines.py 100 < largefile
serait bien pour une bonne réponse, cependant: dans ce cas, cestdin
sera recherché.