Criar a zzaleatorio, para substituir o $RANDOM #75

Closed
aureliojargas opened this Issue Mar 12, 2013 · 18 comments

Projects

None yet

3 participants

@aureliojargas
Member

A $RANDOM não é 100% portável. É exclusivo do Bash, não funciona em outros shells. Por enquanto nossa meta é ser Bash-only, então não há problema.

A lista de funções que a utilizam hoje.

$ grep -l RANDOM *
zzcnpj.sh
zzcpf.sh
zzlinha.sh
zzmat.sh
zzpalpite.sh
zzsenha.sh
zzshuffle.sh
zzss.sh
zzsubway.sh

Não há pressa, mas no futuro podemos seria bom ter uma solução portável pra gerar números aleatórios.

Eu imagino o uso assim: zzaleatorio [número-inicial] número-final.

Exemplos:

$ zzaleatorio 10           # me dê um núm entre 1 e 10
7
$ zzaleatorio 5 15         # me dê um núm entre 5 e 15
11

Bem simples, sem firulas, sem opções, somente para números inteiros.

O mais trabalhoso no momento é encontrar uma solução 100% portável para gerar um número aleatório. Talvez com expr, talvez awk, não sei, ainda não pesquisei.

@itamarnet
Contributor

Que tal essa sugestão?

#71 (comment)

@itamarnet
Contributor

Usei o awk, como havia citado no comentário anterior. Acho que ficou aceitável, mas gostaria que avaliassem como foi feita a solução, e se realmente está funcionando em outros shell. Comigo, em todos funcionaram bem.

@itamarnet itamarnet closed this Mar 15, 2013
@gmgall
Contributor
gmgall commented Mar 15, 2013

Fiz um teste rápido aqui e parece tudo certo no bash, dash e pdksh.

@itamarnet
Contributor

Obrigado pelo feedeback! De toda maneira qq sugestão ainda é muito bem vinda.

@aureliojargas
Member

Aqui no BSD o comando date não reconhece esse %N:

$ date +%N
N
$
@itamarnet
Contributor

date +%s funciona retornando os segundos desde 01/01/1970 no BSD?? Se sim vou usar esse comando especificamente para substituir quando date +%N não funcionar.
É uma pena pois o %N retorna nanosegundos, na qual eu usava para ter uma "semente" mais aleatória possível. Por padrão no awk, o srand() usa a data e o tempo do dia como "semente", para o uso do rand(), mas usando num laço como while ou for, os resultado ficam repetitivos. Para evitar esse tipo de coisa, no zzmat aleatorio uso uma pausa (sleep 1), para evitar esse problema, talvez tenha que usar esse recurso para suprir essa limitação.
Alguma sugestão para contornar isso?

@aureliojargas
Member

Estou longe do computador agora, depois vou testar, mas tenho quase certeza que o %s funciona. Eu já tinha pensado nele mas desisti por causa desse problema de acessos repetidos dentro do mesmo segundo.

Se for dentro de um loop, talvez a semente poderia ser o %s somando de um contador incremental. Mas isso não resolve o problema de acessos simultâneos por processos diferentes, tipo dois terminais rodando a zzaleatorio ao mesmo tempo.

Tem também o /dev/random, se não me falha a memória. Mas também tem que ver se ele está presente em sistemas mais antigos.

Enfim, longe do computador não consigo testar nem confirmar nada. Mas quis jogar umas idéias aqui por enquanto. Ah, e vou reabri este issue, pois a função precisa ser portável, ok?

@aureliojargas aureliojargas reopened this Mar 17, 2013
@itamarnet
Contributor

Bem lembrado, isso até me deu uma idéia:

$ od -An -N2 -i /dev/random

Mas também não sei se é portável, mas tenho quase certeza que o od existe em todos os sabores de unixes.
Eu acho que o /dev/random também está presente em vários unixes, com algumas diferenças apenas na maneira como é implementado, mas acredito que sempre esteja presente
Se for isso mesmo acho que podemos usa-lo diretamente ou como a "semente".

Fico no aguardo.

@aureliojargas
Member

O od está presente desde sempre, porém suas opções e formato da saída variam :/

Não é maravilhosa a vida de de programador? :)

@itamarnet
Contributor

Realmente é um grande problema.
Opções e formatos que variam acabam sendo uma baita dor de cabeça para manter compatibilidade.
Vou fazer um commit, com uma solução que vai testar várias possibilidades.

Não é das mais elegantes, mas acho que pode funcionar.

@itamarnet
Contributor

Confabulando com um colega de serviço, ele me deu algumas idéias baseado no que viu em outras zz's
Acha que essa solução seria plausível e portável?

Inluir a variavel cache:
local cache="$ZZTMP.aleatorio"

Incluir na atual linha 47:
[ -s "$cache" ] && v_temp=$(cat "$cache")

E na atual linha 48, mudar para:
zztool testa_numero $v_temp || v_temp=$(od -An -N2 -i /dev/urandom)

Incluir na atual linha 60:
echo "$v_temp" | zzmd5 | sed 's/[^0-9]+//g;s/./&+/g' | sed 's/^+//;s/+$//' | bc > "$cache"

@itamarnet
Contributor

Esqueça o comentário anterior, a idéia parecia boa, mas ocorre repetição e deixa de ser aleatório numa mesma seção.
De volta ao planejamento :/

@itamarnet
Contributor

Não descobri outra maneira para uma solução melhor.
Usando um arquivo cache gera uma repetição de padrão, e não encontrei algo confiável que pudesse dar uma confiança na função ser mesmo randômica.
O uso de qualquer artífice se mostrou ineficiente e ao mesmo tempo nada ágil. Infelizmente a solução já usado no zzmat aleatório, com uma pausa de 1 segundo, mostrou-se a mais adequada, mas lenta por isso.
Se tiver alguma idéia nova, favor avisar, as minhas esgotaram...

@aureliojargas
Member

Testei aqui e sim, existe o /dev/random. O od aqui funcionou com as opções que você indicou, e retornou duas linhas, a segunda vazia e a primeira cheias de espaços e com um número de 5 dígitos no meio:

prompt$ od -An -N2 -i /dev/random | sed -n l
                    44065                                                $
$
prompt$ od -An -N2 -i /dev/random | sed -n l
                    23733                                                $
$
prompt$ 

Acho que temos um vencedor! Um tr pra limpar a saída e está pronto (ignore o echo no final):

prompt$ od -An -N2 -i /dev/random | tr -d -c '[0-9]' ; echo
18598
prompt$ od -An -N2 -i /dev/random | tr -d -c '[0-9]' ; echo
37609
prompt$ od -An -N2 -i /dev/random | tr -d -c '[0-9]' ; echo
25501
@itamarnet
Contributor

Legal, é bom saber disso. Mas teste também e veja se existe o /dev/urandom.
Na verdade é que o /dev/random, em um loop, as vezes da uma engasgada, retardando consideravelmente o script e com o /dev/urandom isso não acontece. E pelo que vi o /dev/urandom é quase tão antigo quanto o /dev/random.
Se der certo também, é uma saída melhor.

No meu caso o od não gera a segunda linha, mas é bom saber.

@itamarnet
Contributor

Se puder testa também com "-d" ao invés de "-i" no comando od. Aqui a saída se comporta ligeiramente diferente no formato, mas também retorna um número inteiro aleatório.

@aureliojargas
Member

/dev/urandom existe, confirmado.

prompt$ od -An -N2 -i /dev/urandom | tr -d -c '[0-9]' ; echo
44320
prompt$ od -An -N2 -i /dev/urandom | tr -d -c '[0-9]' ; echo
1147

A opção -d do od também funciona, e traz o resultado no mesmo formato do -i:

prompt$ od -An -N2 -d /dev/urandom | sed -n l
            32441                                                        $
$
prompt$ od -An -N2 -d /dev/urandom | sed -n l
            14212                                                        $
$
prompt$ 
@itamarnet
Contributor

Feita a inclusão para prevenir linha e espaços adicionais.
Mantive os vários testes, para ampliar possibilidades, na iminência de algum sistema não ter algum recurso.
Referência e7419d6

@itamarnet itamarnet closed this Mar 24, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment