Bei dieser Herausforderung geht es darum, zufällige Zeilen aus einer potenziell großen Datei zu lesen, ohne die gesamte Datei in den Speicher einzulesen.
Eingang
Eine Ganzzahl n
und der Name einer Textdatei.
Ausgabe
n
Zeilen der Textdatei werden gleichmäßig und ersatzlos zufällig ausgewählt.
Sie können davon ausgehen, dass n
der Wert zwischen 1 und der Anzahl der Zeilen in der Datei liegt.
Seien Sie vorsichtig, wenn Sie n
zufällig Zahlen aus dem Bereich entnehmen, in dem die Antwort einheitlich ist. rand()%n
in C ist zum Beispiel nicht einheitlich. Jedes Ergebnis muss gleich wahrscheinlich sein.
Regeln und Einschränkungen
Jede Zeile der Textdatei hat die gleiche Anzahl von Zeichen und das sind nicht mehr als 80.
Ihr Code darf keinen Inhalt der Textdatei lesen, außer:
- Diese Zeilen gibt es aus.
- Die erste Zeile, in der ermittelt wird, wie viele Zeichen pro Zeile in der Textdatei enthalten sind.
Wir können davon ausgehen, dass jedes Zeichen in der Textdatei genau ein Byte benötigt.
Zeilentrennzeichen werden mit einer Länge von 1 Byte angenommen. Lösungen dürfen nur dann 2 Byte lange Zeilentrennzeichen verwenden, wenn sie dies erfordern. Sie können auch davon ausgehen, dass die letzte Zeile durch einen Zeilentrenner abgeschlossen wird.
Ihre Antwort sollte ein vollständiges Programm sein, aber Sie können die Eingabe auf jede bequeme Weise festlegen.
Sprachen und Bibliotheken
Sie können eine beliebige Sprache oder Bibliothek verwenden.
Anmerkungen
Es gab Bedenken hinsichtlich der Berechnung der Anzahl der Zeilen in der Datei. Wie nimi in den Kommentaren betont, können Sie dies aus der Dateigröße und der Anzahl der Zeichen pro Zeile ableiten.
Motivation
Im Chat wurden einige Leute gefragt, ob dies wirklich eine "Do X without Y" -Frage ist. Ich interpretiere dies, um zu fragen, ob die Beschränkungen ungewöhnlich künstlich sind.
Das zufällige Abtasten von Zeilen aus riesigen Dateien ist keine Seltenheit und wird von mir manchmal ausgeführt. Eine Möglichkeit, dies zu tun, ist in Bash:
shuf -n <num-lines>
Dies ist jedoch bei großen Dateien sehr langsam, da die gesamte Datei eingelesen wird.
fseek
und in anderen unmöglich. Wasn
ist , wenn die Anzahl der Zeilen in der Datei überschritten wird?sum()
. Das Nichteinlesen einer Datei in den Speicher ist eine klare und konsistente Einschränkung, die in keiner Weise willkürlich ist. Es kann mit einer Datei getestet werden, die größer als der Arbeitsspeicher ist und die nicht durch Sprachunterschiede umgangen werden kann. Es kommt auch vor, dass es reale Anwendungen gibt (obwohl das für ein Golfspiel nicht notwendig ist ...).Antworten:
Dyalog APL , 63 Bytes
Fordert zur Eingabe des Dateinamens auf und gibt an, wie viele zufällige Zeilen gewünscht werden.
Erläuterung
⍞
Eingabeaufforderung (Dateiname)⎕NTIE 0
Verknüpfen Sie die Datei mit der nächsten verfügbaren Verknüpfungsnummer (-1 auf einem sauberen System).t←
Speichern Sie die gewähltet
83 80,⍨
Verknüpfungsnummer als Anhang [83,80], was [-1,83,80] ergibt. Lesen⎕NREAD
Sie die ersten 80 Bytes der Datei -1 als 8-Bit-Ganzzahl (Konvertierungscode 83)10⍳⍨
Finden Sie den Index der ersten Zahl 10 (LF)l←
Speichern Sie die Zeilenlänge alsl
(⎕NSIZE t)÷
Teilen Sie die Größe der Datei -1 mit der Zeilenlänge⎕
Eingabeaufforderung für numerische Eingabe (gewünschte Anzahl von Zeilen )?
X zufällige Auswahlen (ohne Ersetzung) aus den ersten Y natürlichen Zahlen¯1+
Addieren Sie -1, um Zeilennummern mit 0 Ursprung zu erhalten. *l×
Multiplizieren Sie mit der Zeilenlänge, um diet 82l∘,¨
Startbytes zu erhalten Liste der Argumente für⎕NREAD
)⎕NREAD¨
Lesen Sie jede Zeile als 8-Bit-Zeichen (Konvertierungscode 82)Praktisches Beispiel
Die Datei /tmp/records.txt enthält:
Stellen Sie sicher, dass das Programm RandLines den obigen Code wörtlich enthält, indem Sie Folgendes in die APL-Sitzung eingeben:
RandLines
Geben Sie in der APL-Sitzung ein und drücken Sie die Eingabetaste.Das System bewegt den Cursor in die nächste Zeile, bei der es sich um eine Eingabeaufforderung mit der Länge 0 für Zeichendaten handelt. eintreten
/tmp/records.txt
.Das System gibt jetzt
⎕:
Zahlen aus und wartet auf deren Eingabe. eintreten4
.Das System gibt vier zufällige Zeilen aus.
Wahres Leben
In Wirklichkeit möchten Sie möglicherweise Dateinamen und Anzahl als Argumente angeben und das Ergebnis als Tabelle erhalten. Dies kann durch Eingabe von:
Jetzt machst du MyLines mit drei zufälligen Zeilen:
Wie wäre es, nur eine einzelne zufällige Zeile zurückzugeben, wenn count nicht angegeben ist:
Jetzt können Sie beides tun:
und (Fehlen des linken Arguments beachten):
Code lesbar machen
Golf APL Einzeiler sind eine schlechte Idee. So würde ich in einem Produktionssystem schreiben:
* Ich könnte ein Byte speichern, indem ich im 0-Origin-Modus arbeite, der bei manchen APL-Systemen Standard ist: vorher entfernen
¯1+
und einfügen .1+
10
quelle
Ruby,
104949290 BytesDateiname und Zeilenzahl werden in die Kommandozeile übernommen. Zum Beispiel, wenn das Programm ist
shuffle.rb
und der Dateiname ista.txt
, führtruby shuffle.rb a.txt 3
drei zufällige Linien.-4 Bytes durch Erkennen der
open
Syntax in Ruby anstelle vonFile.new
Hier ist auch eine anonyme 85-Byte-Funktionslösung, deren Argumente eine Zeichenfolge und eine Zahl sind.
quelle
ruby shuffle.rb 3 < a.txt
gibt dir einen suchbaren stdin. IDK Ruby allerdings.Haskell,
240224236 BytesLiest Dateinamen und n von stdin.
Wie es funktioniert:
Es braucht viel Zeit und Speicher, um dieses Programm für Dateien mit vielen Zeilen auszuführen, da die
shuffle
Funktion fürchterlich ineffizient ist.Edit: Ich habe den "random without replacement" Teil verpasst (danke @feersum fürs mitbekommen!).
quelle
PowerShell v2 +, 209 Byte
Übernimmt die Eingabe
$a
als Dateiname und$n
als Anzahl der Zeilen. Beachten Sie, dass$a
es sich um einen Dateinamen mit vollständigem Pfad handeln muss, der als ANSI-Codierung angenommen wird.Anschließend erstellen wir ein neues
FileStream
Objekt und weisen es an,$a
mitOpen
Berechtigungen auf die Datei zuzugreifen .Die
for
Schleife.Read()
durchläuft die erste Zeile, bis wir ein\n
Zeichen treffen. Dabei wird der Zeilenzähler für jedes Zeichen erhöht. Wir setzen dann$t
gleich der Größe der Datei (dh, wie lang der Stream ist) geteilt durch wie viele Zeichen pro Zeile (plus eins, damit der Terminator zählt), minus eins (da wir null-indiziert sind). Dann konstruieren wir unseren Puffer so$z
, dass er auch die Zeilenlänge hat.Die letzte Zeile erstellt ein dynamisches Array mit dem
..
Bereichsoperator. 1 Wir leiten dieses ArrayGet-Random
mit einer-C
Anzahl von$n
zufällig ausgewählten$n
Zeilennummern ohne Wiederholung weiter. Die Glückszahlen werden mit in eine Schleife geleitet|%{...}
. Bei jeder Iteration werden wir.Seek
an die jeweilige Stelle und dann.Read
in einer Zeile Zeichen im Wert von gespeichert$z
. Wir wandeln es$z
in ein char-Array um und setzen-join
es zusammen, wobei die resultierende Zeichenfolge in der Pipeline verbleibt und die Ausgabe am Ende des Programms implizit erfolgt.Technisch sollten wir mit einem
$f.Close()
Aufruf zum Schließen der Datei enden , aber das kostet Bytes! : pBeispiel
1 Technisch bedeutet dies, dass wir nur maximal 50.000 Zeilen unterstützen können, da dies der größte Bereich ist, der auf diese Weise dynamisch erstellt werden kann. : - / Aber wir können nicht einfach eine
Get-Random
Befehlsschleife machen$n
und doppelte Schleifen verwerfen, da das nicht deterministisch ist ...quelle
Python 3,
146139 BytesEingabe: [Dateiname] \ n [Zeilen] \ n
Diese Lösung hat viel von @pppery gestohlen,
ist aber nur Python3.5 undein vollständiges Programm.Edit: Danke an @Mego für den Inline-Bereich und die Python3.x-Kompatibilität. Edit2: Klarstellung, wo der Druck ist, weil ich zwei Kommentare dazu habe. (Kommentar ist offensichtlich nicht Teil des Codes oder der Byteanzahl.)
quelle
r=range(f.seek(0,2)//l)
funktioniert, was 3 Bytes spart und die Notwendigkeit für 3.5 beseitigt. Noch besser ist es, 3 weitere Bytes zu sparen, indem Sie denrange
Anruf in densample
Anruf einfügen . Darüber hinaus ist dies kein vollständiges Programm - Sie müssen die Liste tatsächlich drucken.r=[*range(f.seek(0,2)//l)]
weil ich dachte, ich könnte keinensample
Generator. Es stellte sich heraus, dass ich konnte. @ Mega: Es ist vollständig, weil es jede Zeile innerhalb des Listenverständnisses drucktprint(f.read(l))
Lua
126,122Verwendet 2 Byte für Zeilenumbrüche. Ändern Sie die 2 in eine 1 für 1. Ich habe es nur als 2, weil das, was meine Testdatei hatte.
Habe mich unter dem PHP-Eintrag aber noch auf Platz 2 (derzeit). Verfluche dich, Ruby Eintrag!
quelle
Bash (gut, Coreutils), 100 Bytes
Erläuterung
Dadurch wird vermieden, dass die gesamte Datei gelesen wird
dd
, um die Teile der Datei zu extrahieren, die wir benötigen, ohne die Datei vollständig zu lesen.if
Ist die Eingabedateibs
die Blockgröße (hier setzen wir sie auf$n
die Länge der ersten Zeile,skip
die auf die zufälligen Ganzzahlen festgelegt ist, die ausshuf
demibs
Wert skippingskip
*ibs
bytes extrahiert wurden.count
Die Anzahl der zurückzugebendenibs
Längenabschnitte wird zum Entfernenstatus=none
benötigt Die Informationen, die normalerweise von ausgegeben werdendd
Wir bekommen die Zeilenlänge mit
head -1 $2|wc -c
und die Dateigröße mitstat -c%s $2
.Verwendung
Speichern unter
file.sh
und starten mitfile.sh n filename
.Timings
gegen
Mal oben für eine 68MiB-Datei, die mit erzeugt wurde
seq 1000000 9999999 > test.txt
.Danke an @PeterCordes für seinen -1 Tipp!
quelle
bs=
stattdessen auchibs=
einstellen,obs
da dies in Ordnung ist. Ich denke , man nicht ersetzen kannif=$2
mit<$2
obwohl, da diese immer nochxargs
s‘Kommandozeile.\<$2
funktioniert auch nicht (xargs verwendet exec direkt ohne shell).2>&-
, so dass keine Gefahr besteht, dass die Ausgabe irgendwohin geht (z. B. wenn stdin zufällig ein Lese- / Schreib-Dateideskriptor ist). Es funktioniert mit GNUdd
: Es produziert immer noch seine,stdout
bevor es versucht und nicht zu schreibenstderr
.Python 3 -
161 160149 BytesDieser Code definiert eine Funktion, die wie folgt aufgerufen wird
f(10,'input.txt')
quelle
C # 259 Bytes ohne Duplikate
Ungolfed
File.ReadLines ist faul. Dies hat den zusätzlichen Vorteil, dass alle Leitungen unterschiedlich lang sein können.
Laufen würde es sein:
C # 206 Bytes mit Duplikaten
Ungolfed
quelle
Python (141 Bytes)
Hält jede Linie mit gleicher Wahrscheinlichkeit, auch bei Rohren. Es ist jedoch keine Antwort auf die Frage, die Sie überspringen müssen ...
Verwendung
cat largefile | python randxlines.py 100
oderpython randxlines 100 < largefile
(wie bei @petercordes angegeben)quelle
python ./randxlines.py 100 < largefile
wäre in Ordnung für eine richtige Antwort, aber in diesem Fallstdin
wird suchbar sein.