Já se perguntou em como criar números aleatório? Provavelmente sim e provavelmente também ficou mais confuso do que antes de se perguntar. Isso porque uma coisa aleatória é, teoricamente, imprevisível. Então como é possível existirem funções aleatórias sem que exista uma previsibilidade envolvida? No post de hoje, vamos aprender a criar uma simples função que gera números inteiros aleatórios em um dado intervalo utilizando o Arduino.
Para entender melhor a parte de programação do Arduino, recomendo a leitura do post sobre comunicação serial.
Aleatoriedade
Retomando a pergunta feita anteriormente: Então como é possível existirem funções aleatórias sem que exista uma previsibilidade envolvida? Se sua resposta para ela foi “impossível”, então você está certo. Pelo menos em relação ao método que vou mostrar.
Houve um tempo onde acreditavam que o universo era como um mecanismo de relógio e qualquer acontecimento poderia ser previsto de acordo com a física, e outras ciências, por meio de fórmulas. Esse pensamento ainda não é muito distante, pois o universo de fato obedece a várias leis e tudo parece possível de ser calculado e estimado. Nesse contexto, é impossível termos aleatoriedades, já que tudo pode ser calculado.
Entretanto, com a física quântica, as coisas mudaram um pouco de rumo. Pois vários estudos apontaram que o universo não é tão determinístico assim. Isso se deve ao fato de que o comportamento dos átomos, mais especificamente os elétrons, é aleatório. É impossível prever a posição exata de um elétron, apenas uma região com as probabilidades de sua posição.
Portanto, graças a física quântica, o sonho de gerar números aleatórios volta a vida. Infelizmente, as soluções cabíveis atualmente são pseudoaleatórias.
Pseudoaleatorio
Um método pseudoaleatório, aparentemente, é aleatório, mas no fundo é determinístico. A ideia do método aleatório é utilizar uma lógica que seja o mais imprevisível possível para o ser humano. Uma lógica que talvez uma máquina consiga prever, mas que seja impossível para o ser humano usando apenas seu intelecto.
Por exemplo, quando você lança uma moeda para cima e observa em qual lado ela cai. Imagine ainda que você utiliza um braço robótico em um ambiente controlado para lançar essa moeda. Espera-se que a resposta seja sempre a mesma (uma mesma face da moeda). Mas, se você fizer o mesmo teste manualmente em um ambiente qualquer, é praticamente impossível obter um resultado constante.
Isso, porque existem muitas variáveis envolvidas: a força de lançamento da moeda; a posição de lançamento (x,y,z); a direção e o sentido de lançamento; o atrito da moeda com ar; a velocidade, direção e sentido do vento… Portanto é possível perceber que existem muitas variáveis envolvidas que o ser humano, sem a ajuda de máquinas ou dispositivos, não é capaz de calcular e prever o resultado.
Sendo assim, a função que criaremos neste post se baseia nesta imprevisibilidade.
Lógica da função
Explicação
Para gerar números aleatórios, vamos utilizar o timer do Arduino. Assim como expliquei no post sobre multi-funções, o Arduino inicializa um timer do microcontrolador no momento que ele é ligado. Esse timer conta os microssegundos que se passaram desde de que o Arduino foi ligado. Então, se eu liguei o Arduino há 1 segundo, o timer irá me retornar um valor de 1.000.000 (1 microssegundo = 1 segundo *10^6).
Se você já teve a oportunidade de utilizar um cronometro, você já deve ter reparado que os milissegundos passam muito rápido e mal da para acompanhar. É extremamente difícil parar um cronometro em um segundo exato, pois é difícil acertar os milissegundos. Observe na imagem abaixo a velocidade que eles aumentam.
Sendo assim, imagine se esse cronometro tivesse a escala de microssegundos. Não daria pra ver eles variando, pois o microssegundo é 1/1000 do milissegundo, ou seja, passa muito rápido. Portanto, podemos utilizar a própria escala de microssegundos para gerar nossos números aleatórios.
Funcionamento da função
Vamos supor que você deseja um número aleatório de 1 a 9 a cada vez que apertar um botão. Então, se a função aleatório retornasse o último dígito do microssegundo, o numero iria parecer aleatório para nós. Pois a ação de apertar o botão já apresenta um delay e entre cada apertar do botão, o timer teria um valor totalmente diferente.
Exemplificando… Vamos supor que no momento que cliquei no botão, o timer retorna um valor de 35.000.407 (35 segundos e 407 microssegundos). O valor aleatório de 0 a 9 seria o último digito (7). Se apertarmos o botão novamente 1 milissegundo depois, o timer pode retornar 35.001.205. Não tem como saber qual o valor que o timer terá. Então, aparentemente é um processo aleatório.
Sem falar que o próprio microcontrolador pode apresentar alguns delays internos para executar cada parte do código até retornar o valor. Ou então, algum delay por conta de defeitos. Enfim, o resultado final é um número aleatório. Veja o teste comprovatório abaixo, gerando números entre 0 e 60 (como fazer no tópico seguinte):
É possível dizer que os valores são aleatórios, pois não é possível perceber nenhuma tendência aparente nos valores.
Problemas
A função não é de tudo perfeita, pois se você quiser gerar números muito altos, a função começa a apresentar padrões e tendências. Para explicar o motivo, vamos considerar um caso exagerado: vamos supor que você queira gerar números aleatórios entre 0 e 30.000.000.
Imagine que no instante 1, o timer retorna um valor de 20.123.652. Se executarmos a função no instante seguinte, ela irá retornar um valor parecido com o seguinte: 20.124.135. Repare que, para esse caso, a função irá nós dar um valor de 20.xxx.xxx durante 10 segundos, pois o 20 representa os segundos. Então ela deixa de ser aleatória, pois o desejável era que ela retornasse QUALQUER valor entre 0 e 30.000.000 em qualquer momento.
Infelizmente, para intervalos muito altos, a função começa a apresentar padrões (executando a função repetidamente e rapidamente por meio da programação). Veja a imagem abaixo:
É bem perceptível o padrão de repetição. Portanto, a função nos atende muito bem para gerar intervalos pequenos. Fora disso, ela deixa de ser aleatória.
Como fazer
Criando a função de números aleatório
Depois de explicar toda a teoria, vamos criar a função. A primeira coisa que precisamos fazer é declarar uma variável para armazenar o valor do timer. Como o timer retorna valores muito grandes, precisamos de uma variável unsigned long (guarda valores muito grandes).
unsigned long int tempo = 0; //Variavel para armazenar o valor do timer
Feito isso, vamos declarar nossa função. Ela irá retornar um número inteiro e recebe dois parâmetros, o limite inferior dos números aleatórios e o limite superior. Ou seja, a faixa de números que devemos retornar. Exemplo: aleatorio(10,15) – queremos números aleatórios de 10 a 15.
1 | int aleatorio(int inf,int sup){ // Função para gerar numeros aleatorio |
Agora, precisamos pegar o valor do timer. Para pegar o valor dele em microssegundos, existe a função micros().
tempo = micros();
Para retornar o valor do último digito, isto é, se o usuário quisesse apenas um número aleatório de 0 a 9, bastaria fazer:
tempo=tempo%10; //% pega o resto da divisão
Exemplo: 1.435.152 / 10 -> o resto dessa divisão é o ultimo digito. Qualquer número que colocarmos no lugar do 10 será nosso limite superior, pois o resto da divisão de um número x por um outro número y está entre 0 y. Então tempo%63 retornaria algo entre 0 e 63 (incluindo o 0).
Porém, estamos trabalhando com intervalos, sendo assim, vamos fazer o seguinte: vamos gerar números correspondentes a quantidade de números no intervalo desejado (sup-inf). Sup-inf nos informa quantos números existem no intervalo. Portanto, vamos gerar um número que varia de 0 até (sup-inf). Para isso, devemos fazer tempo%((sup-inf)+1): isso nos retornará números que variam de 0 até sup-inf e o +1 é pra incluir o limite superior.
Depois disso, basta adicionar o limite inferior, para que esse número gerado se encaixe no intervalo.
Função pronta
Vou exemplificar o procedimento anterior para melhor entendimento:
Imagine que queremos números de 20 a 30. Sup=30, inf=20. Se fazemos 30-20, temos 10 como resultado. O resultado indica quantos números existem no intervalo. Ao fazermos tempo%(10+1), obtemos um número de 0 a 10. Se fosse tempo%10, obteríamos um número de 0 a 9. Com esse resultado, adicionamos ao limite inferior e obtemos números que variam de 20 a 30 (20+(0-10)).
A função abaixo tem o objetivo de ser o mais simples possível, então pode conter bugs.
1 2 3 4 5 6 7 | int aleatorio(int inf,int sup){ // Função para gerar numeros aleatorios // inf é o limite inferior // sup é o limite superior tempo=micros(); tempo=tempo%((sup-inf)+1)+inf; return tempo; } |
Testando o código
Para testar o código, vou utilizar a comunicação serial do Arduino. Toda vez que eu enviar a letra ‘n’ no serial, eu imprimo o valor da função aleatório com o intervalo desejado:
1 2 3 4 5 6 7 8 9 | void loop() { if(Serial.available() > 0){ // Se houverem dados para ler informacao = Serial.read(); // Armazena a leitura do serial na variavel informação if(informacao == 'n'){ // Se a variavel informaçao for igual a 'n' Serial.println(aleatorio(20,30)); // Printa o valor da funçao aleatorio de 20-30 } } } |
Código completo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | unsigned long int tempo = 0; // Variavel para armazenar o valor do timer char informacao; // Variavel para leitura serial void setup() { Serial.begin(9600);// Inicializa a comunicação serial } void loop() { if(Serial.available() > 0){ // Se houverem dados para ler informacao = Serial.read(); // Armazena a leitura do serial na variavel informação if(informacao == 'n'){ // Se a variavel informaçao for igual a 'n' Serial.println(aleatorio(20,30)); // Printa o valor da funçao aleatorio de 20-30 } } } // Função para gerar numeros aleatorios int aleatorio(int inf,int sup){ // inf é o limite inferior // sup é o limite superior tempo=micros(); tempo=tempo%((sup-inf)+1)+inf; return tempo; } |
Espero que agora você não esteja mais confuso sobre como é possível existir funções para gerar números aleatórios. Lembrando que existem outras técnicas bem mais complexas do que a que eu utilizei.
Estou usando este método, porém às vezes são gerados números negativos
*Ignore minha resposta anterior caso você tenha lido*
Como você está definindo seu limite superior e inferior? Porque, a única forma que vejo deste problema ocorrer, é o limite superior ser menor do que o inferior.