Resolvendo o limite da função Randomize()

Introdução

No post Boas Práticas de Programação – Código Simples, Resultados Rápidos, eu mencionei um limite operacional da função Randomize() do AdvPL. Caso a diferença entre o maior e o menor número a ser sorteado for maior que 32767, a função vai sortear um número maior ou igual ao número inicial informado, e menor que o número inicial mais 32767.

Programa original

A chamada no fonte original era para sortear um número entre 100000 e 999999. Porém, devido a limitação operacional da função Randomize(), o maior resultado seria sempre menor que 132767.

nTmp := Randomize(100000,999999)

Para resolver esta limitação da função, podemos criar uma segunda função de sorteio, onde podemos tratar este limite de uma forma elegante e performática. Vamos criar a USER Function Randomic(). 

USER Function Randomic(nMin,nMax)
Local nDiff := nMax-nMin
Local nDec := 0
If nDiff < 32766
   Return Randomize(nMin,nMax)
Endif
While nDiff > 32765
   nDiff /= 10 
   nDec++
Enddo
nTmp := randomize(0,int(nDiff))
While nDec > 0 
   nTmp *= 10 
   nTmp += randomize(0,10) 
   nDec--
Enddo
Return nMin+nTmp

Como a U_Randomic() funciona

Inicialmente, determinamos usando a variável local nDiff qual é a diferença do maior para o menor número. Caso a diferença seja suportada pela função Randomize(), retornamos direto a chamada para a função Randomize(), passando os parâmetros originais.

Caso a diferença não seja contemplada pela função Randomize(), dividimos a diferença por 10 até que ela entre dentro do intervalo. Cada divisão realizada incrementa uma unidade na variável local nDec, que indica quantas casas da diferença original foram reduzidas. Uma vez que a diferença se enquadre nos valores suportados, chamamos a função Randomize(), para sortear um número entre 0 e o valor inteiro da diferença apurada após as divisões.

Sorteado este número, agora precisamos sortear mais alguns números para completar as casas decimais que foram “cortadas” da diferença original. A cada iteração, para quantas casas decimais foram cortadas — valor guardado na variável nDec — o valor anteriormente sorteado é multiplicado por 10, e um novo valor sorteado entre 0 e 9 é adicionado ao resultado, decrementando uma unidade em nDec. Terminado o processo, o número final sorteado será o número inicial somando com o número  final sorteado, armazenado em nTmp.

Exemplo

Vamos rodar o programa de testes abaixo, chamando uma vez a função U_RANDOMIC() usando o IDE/TDS em modo de depuração.

User Function TstRand()
Local nRand
nRand := U_Randomic(100000,1000000)
conout(nRand)
Return

Ao entrarmos na função U_Randomic(), a diferença entre o numero final e o inicial será de 900000 (novecentos mil). Ao passar pelo primeiro loop, a primeira divisão por 10 faz o número baixar para 90000 (noventa mil), e nDec é incrementado para uma unidade. Como o número ainda é maior que 32765, este loop é executado novamente, onde nDiff baixa agora para 9000 (nove mil) e nDec é incrementado para 2. Como agora nDiff é menor que 32765, o programa continua.

O sorteio do novo número, a ser armazenado na variável nTmp, será feito usando a função Randomize(), informando o valor mínimo 0 e o máximo 9000. Durante a depuração, o número sorteado por exemplo foi 1271.

Agora, como houve a redução de duas casas decimais, o próximo loop será executado duas vezes. Na primeira execução, o número nTmp é multiplicado por 10 — resultando em 12710 — e um novo dígito entre 0 e 9 será sorteado e acrescentado em nTmp. foi sorteado o número 7, e acrescentado em nTmp, fazendo seu valor atual ser 12717. Na segunda execução, este valor foi multiplicado novamente por 10 — resultando em 127170 — e um novo número foi sorteado e acrescentado — foi sorteado o número 4, e nTmp passou a conter o valor 127174.  Nas duas execuções, nDec foi decrementado duas vezes, voltando a ser 0 (zero). Pronto, na ultima linha, acrescentamos o número inicial (100000) ao número sorteado (127174), resultando no número 227174.

Da forma que a função foi escrita, ela torna muito rápido e seguro o processo de sorteio, não havendo chance do valor sorteado ser maior que o especificado como parâmetro para a função.

Operadores especiais utilizados

Quando queremos realizar uma operação aritmética com uma variável, e queremos atualizar esta mesma variável com o valor resultante da operação, normalmente utilizamos uma sintaxe como:

variavel := variavel <operador> <operando>

Por exemplo:

nVar := nVar * 10 
nVar := nVar + 10 
nVar := nVar + 1

Em AdvPL, existem operadores compostos  especiais, que ao mesmo tempo fazem a atribuição e a operação aritmética. Por exemplo, as operações acima podem ser escritas da seguinte forma:

nVar *= 10
nVar += 10 
nVar++

Os operadores +=-=*=  e  /= são binários — exigem dois argumentos, a variável do lado esquerdo que será usada como base para a operação e receberá o resultado, e a expressão do lado direito (variável ou constante), que será utilizada para realizar a operação. Respectivamente são os operadores de soma (+), subtração (-), multiplicação (*) e divisão (/). Já os operadores ++ e — são unários — têm apenas um argumento, que é a variável em questão, e respectivamente somam ou subtraem o valor 1 da variável informada.

Conclusão

Mesmo que alguma função básica da linguagem AdvPL possua alguma restrição operacional, o conhecimento das demais funcionalidades e capacidades da linguagem podem tornar fácil uma implementação de um novo recurso que atenda a sua necessidade.

Agradeço novamente a audiência, e desejo a todos TERABYTES DE SUCESSO !!! 😀

Referências

 

 

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s