Esse desafio é ler linhas aleatórias de um arquivo potencialmente enorme sem ler o arquivo inteiro na memória.
Entrada
Um número inteiro n
e o nome de um arquivo de texto.
Resultado
n
linhas do arquivo de texto escolhidas uniformemente aleatoriamente sem substituição.
Você pode assumir que n
está no intervalo de 1 ao número de linhas no arquivo.
Tenha cuidado ao amostrar n
números aleatoriamente no intervalo em que a resposta que você obtém é uniforme. rand()%n
em C não é uniforme, por exemplo. Todo resultado deve ser igualmente provável.
Regras e restrições
Cada linha do arquivo de texto terá o mesmo número de caracteres e não terá mais que 80.
Seu código não deve ler nenhum conteúdo do arquivo de texto, exceto:
- Aquelas linhas que produz.
- A primeira linha a descobrir quantos caracteres por linha existem no arquivo de texto.
Podemos assumir que cada caractere no arquivo de texto ocupa exatamente um byte.
Supõe-se que os separadores de linha tenham 1 byte de comprimento. As soluções podem usar separadores de linha longa de 2 bytes apenas se especificarem essa necessidade. Você também pode assumir que a última linha é encerrada por um separador de linhas.
Sua resposta deve ser um programa completo, mas você pode especificar a entrada da maneira que for mais conveniente.
Línguas e bibliotecas
Você pode usar qualquer idioma ou biblioteca que desejar.
Notas
Havia uma preocupação em calcular o número de linhas no arquivo. Como nimi aponta nos comentários, você pode inferir isso do tamanho do arquivo e do número de caracteres por linha.
Motivação
No bate-papo, algumas pessoas perguntaram se essa é realmente uma pergunta "Faça X sem Y". Interpreto isso para perguntar se as restrições são extraordinariamente artificiais.
A tarefa de amostragem aleatória de linhas de arquivos enormes não é incomum e é de fato uma que às vezes tenho que fazer. Uma maneira de fazer isso é no bash:
shuf -n <num-lines>
No entanto, isso é muito lento para arquivos grandes, pois ele lê o arquivo inteiro.
fseek
, e impossível em outros. Além disso, e sen
for maior que o número de linhas no arquivo?sum()
. Não ler um arquivo na memória é uma restrição clara e consistente que não é de forma alguma arbitrária. Ele pode ser testado com um arquivo maior que a memória, que não pode ser contornado por diferenças de idioma. Também tem aplicativos do mundo real (embora isso não seja necessário para um golfe ...).Respostas:
Dyalog APL , 63 bytes
Solicita o nome do arquivo e, em seguida, quantas linhas aleatórias são desejadas.
Explicação
⍞
Solicitar entrada de texto (nome do arquivo)⎕NTIE 0
Amarre o arquivo usando o próximo número de empate disponível (-1 em um sistema limpo)t←
Armazene o número de empate escolhido comot
83 80,⍨
Anexar [83,80] produzindo [-1,83,80]⎕NREAD
Leia os primeiros 80 bytes do arquivo -1 como números inteiros de 8 bits (código de conversão 83)10⍳⍨
Encontre o índice do primeiro número 10 (LF)l←
Armazene o comprimento da linha comol
(⎕NSIZE t)÷
Divida o tamanho do arquivo -1 com o comprimento da linha⎕
Solicitar entrada numérica (número de linhas desejado) )?
X seleções aleatórias (sem substituição) dos primeiros números naturais Y¯1+
Adicione -1 para obter números de linha com origem 0 *l×
Multiplique pelo comprimento da linha para obter os bytes iniciaist 82l∘,¨
Prefixo [-1,82, LineLength] a cada byte inicial (cria lista de argumentos para⎕NREAD
)⎕NREAD¨
Leia cada linha como caractere de 8 bits (código de conversão 82)Exemplo prático
O arquivo /tmp/records.txt contém:
Faça com que o programa RandLines contenha o código acima literalmente, inserindo o seguinte na sessão da APL:
Na sessão APL, digite
RandLines
e pressione Enter.O sistema move o cursor para a próxima linha, que é um prompt de comprimento 0 para dados de caracteres; entrar
/tmp/records.txt
.O sistema agora produz
⎕:
e aguarda entrada numérica; entra4
.O sistema gera quatro linhas aleatórias.
Vida real
Na realidade, você pode querer dar o nome do arquivo e contar como argumentos e receber o resultado como uma tabela. Isso pode ser feito digitando:
Agora você cria MyLines que contém três linhas aleatórias com:
Que tal retornar apenas uma única linha aleatória se count não for especificado:
Agora você pode fazer as duas coisas:
e (observe a ausência de argumento à esquerda):
Tornando o código legível
APLs de golfe são uma má idéia. Aqui está como eu escreveria em um sistema de produção:
* Eu poderia salvar um byte executando no modo de origem 0, o que é padrão em alguns sistemas APL: remova
¯1+
e insira1+
antes10
.fonte
Ruby,
104949290 bytesO nome do arquivo e o número de linhas são passados para a linha de comando. Por exemplo, se o programa é
shuffle.rb
e o nome do arquivo éa.txt
, executeruby shuffle.rb a.txt 3
por três linhas aleatórias.-4 bytes da descoberta da
open
sintaxe no Ruby em vez deFile.new
Além disso, aqui está uma solução de função anônima de 85 bytes que usa uma string e um número como argumentos.
fonte
ruby shuffle.rb 3 < a.txt
dá-lhe um stdin procurável. IDK Ruby, no entanto.Haskell,
240224236 bytesLê o nome do arquivo en do stdin.
Como funciona:
Leva muito tempo e memória para executar este programa para arquivos com muitas linhas, devido a uma
shuffle
função ineficiente horrível .Edit: Eu perdi a parte "aleatória sem substituição" (obrigado @feersum por perceber!).
fonte
PowerShell v2 +, 209 bytes
Aceita entrada
$a
como o nome do arquivo e$n
como o número de linhas. Observe que$a
deve ser um nome de arquivo de caminho completo e considerado como codificação ANSI.Em seguida, construímos um novo
FileStream
objeto e pedimos que ele acesse o arquivo$a
comOpen
privilégio.O
for
loop.Read()
percorre a primeira linha até atingirmos um\n
caractere, incrementando nosso contador de comprimento de linha para cada caractere. Em seguida, definimos$t
igual ao tamanho do arquivo (ou seja, quanto tempo o fluxo é) dividido por quantos caracteres por linha (mais um para contar o terminador), menos um (já que somos indexados a zero). Em seguida, construímos nosso buffer$z
para também ter comprimento de linha.A linha final constrói uma matriz dinâmica com o
..
operador range. Um tubo Nós que a matrizGet-Random
com um-C
ount de$n
para seleccionar aleatoriamente$n
números de linha sem repetição. Os números da sorte são canalizados em um loop com|%{...}
. Cada iteração é armazenada em um.Seek
local específico e,.Read
em seguida, no valor de uma linha de caracteres$z
. Nós re-convertemos$z
como um array de caracteres e-join
juntos, deixando a sequência resultante no pipeline e a saída está implícita no final do programa.Tecnicamente , devemos terminar com uma
$f.Close()
chamada para fechar o arquivo, mas isso custa bytes! : pExemplo
1 Tecnicamente, isso significa que podemos suportar apenas um máximo de 50.000 linhas, pois esse é o maior intervalo que pode ser criado dinamicamente dessa maneira. : - / Mas não podemos simplesmente repetir um
Get-Random
comando$n
vezes, descartando duplicatas de cada loop, pois isso não é determinístico ...fonte
Python 3,
146139 bytesEntrada: [nome do arquivo] \ n [linhas] \ n
Essa solução roubou muito do @pppery, mas
é apenas python3.5 eé um programa completo.Edit: Obrigado ao @Mego pelo intervalo inline e compatibilidade com python3.x. Edit2: Esclarecimento onde está a impressão, porque eu tenho dois comentários sobre isso. (Obviamente, o comentário não faz parte do código ou da contagem de bytes.)
fonte
r=range(f.seek(0,2)//l)
funcionará, eliminando 3 bytes e eliminando a necessidade de 3,5. Melhor ainda, reduza mais 3 bytes, inserindo arange
chamada nasample
chamada. Além disso, este não é um programa completo - você precisa realmente imprimir a lista.r=[*range(f.seek(0,2)//l)]
porque eu pensei que não poderiasample
um gerador. Acontece que eu poderia. @Mega: É completo porque ele imprime uma cada linha dentro da compreensão de listaprint(f.read(l))
Lua,
126122Usa 2 bytes para quebras de linha. Mude o 2 para 1 para 1. Eu só o tenho como 2 porque é isso que meu arquivo de teste tinha.
Entrei na entrada do PHP, mas ainda estou em segundo lugar (atualmente). Maldito seja, entrada Ruby!
fonte
Bash (bem, coreutils), 100 bytes
Explicação
Isso evita a leitura de todo o arquivo usando
dd
para extrair as partes do arquivo que precisamos sem a leitura completa do arquivo; infelizmente, ele acaba sendo muito grande com todas as opções que precisamos especificar:if
é o arquivo de entradabs
é o tamanho do bloco (aqui o definimos como$n
o comprimento da primeira linhaskip
é definido como os números inteiros aleatórios extraídos deshuf
e equivale aoibs
valor pulandoskip
*ibs
bytes)count
o número deibs
seções de comprimento a serem retornadasstatus=none
é necessário para remover a informação normalmente emitida pordd
Nós obtemos o comprimento da linha usando
head -1 $2|wc -c
e o tamanho do arquivo comstat -c%s $2
.Uso
Salve acima como
file.sh
e execute usandofile.sh n filename
.Horários
vs.
Tempos acima para um arquivo de 68MiB gerado usando
seq 1000000 9999999 > test.txt
.Obrigado a @PeterCordes por sua dica -1!
fonte
bs=
vez deibs=
, já que a configuraçãoobs
também é boa. Eu acho que você não pode substituirif=$2
por isso<$2
, pois essa aindaxargs
é a linha de comando.\<$2
também não funciona (xargs usa exec diretamente, sem um shell).2>&-
, para que não haja perigo da saída ir a lugar algum (por exemplo, se o stdin for um descritor de arquivo de leitura / gravação). Ele funciona com o GNUdd
: Ele ainda o produzstdout
antes de tentar e deixar de escreverstderr
.Python 3 -
161 160149 bytesEste código define uma função que é chamada como
f(10,'input.txt')
fonte
C # 259 bytes sem duplicatas
Ungolfed
O File.ReadLines está lento. Isso tem o benefício adicional de que todas as linhas podem ter comprimentos diferentes.
Executá-lo seria:
C # 206 bytes com duplicatas
Ungolfed
fonte
Python (141 bytes)
Mantém cada linha com igual probabilidade, use também com tubos. Porém, ele não responde à limitação da pergunta à frente ...
Uso
cat largefile | python randxlines.py 100
oupython randxlines 100 < largefile
(como @petercordes apontou)fonte
python ./randxlines.py 100 < largefile
seria bom para uma resposta adequada, no entanto: nesse casostdin
, será procurável.