8.2. Geração de valores aleatórios

A maior parte dos programas fazem a mesma coisa sempre que são executados com os mesmos dados, por isso dizemos que são deterministas. Normalmente é bom que um programa seja determinista, já que esperamos que o mesmo cálculo, feito com os mesmos valores, tenha o mesmo resultado. Mas, para algumas aplicações, gostaríamos de ter um programa imprevisível – por exemplo, os jogos.

Tornar um programa verdadeiramente imprevisível não é fácil mas há formas de o fazer parecer imprevisível. Uma delas é gerar valores aleatórios e usá-los para determinar o resultado do nosso programa.

A biblioteca do Java fornece uma classe que gera valores pseudo aleatórios que, apesar de não serem puramente aleatórios, servem o nosso objetivo.

8.2.1. A classe Random

Consulte a documentação da classe Random do pacote java.util em https://docs.oracle.com/javase/9/docs/api/java/util/Random.html

Uma instância desta classe é um objeto que sabe gerar valores aleatórios.

A classe define vários métodos de instância que retornam valores aleatórios de vários tipos. Por exemplo,

8.2.2. Criação de objetos

Antes de podermos usar um objeto do tipo Random para gerarmos valores aleatórios, temos que o criar.

Também invoca o construtor da classe (mais sobre isto adiante neste texto).

O operador new é um operador unário, ou seja, requer um só operando:

O operador new retorna uma referência para o objeto acabado de criar; esta referência pode ser atribuída a uma variável de tipo apropriado, como na instrução acima.

Como exemplo, as instruções seguintes geram dez inteiros aleatórios no intervalo [0,5] e imprimem-nos no ecrã:

Se executar várias vezes o mesmo programa, os valores gerados não serão os mesmos, com toda a probabilidade.

Poderíamos representar o conteúdo da memória durante a execução do código acima, quando o i tem o valor 2, da seguinte forma:

Aqui representamos os objetos no lado direito, numa coluna de título "Objects" e as variáveis numa coluna de título "Frames".

Como a variável gerador é de um tipo não primitivo, é usada uma seta para representar o seu conteúdo, que é uma referência a um objeto.

Esse objeto é representado aqui somente pelo texto "java.util.Random instance". Veremos mais para a frente exemplos mais ricos.

 

Para gerar números aleatórios no intervalo [1,5]:

Para gerar números aleatórios no intervalo [-val,val] para um dado valor val:

 

De forma mais geral, o método seguinte gera um número aleatório num intervalo [min,max]:

A sequência de instruções para gerar números aleatórios no intervalo [1,5] apresentada acima tem o mesmo efeito que:

 

Um objeto do tipo Random tem um estado; sempre que lhe pedimos um novo valor aleatório, ele muda de estado.

A ideia de definir o método aleatorioIntervalo com um parâmetro do tipo Random é boa, pois permite que seja o invocador do método que define qual o objeto Random que vai criar o valor aleatório, em vez de um novo objeto Random ser criado dentro do método, sempre que ele é invocado.

O conteúdo de memória na altura em que o método aleatorioIntervalo está a ser executado pela primeira vez (quando o i tem o valor 1) , pode ser representado assim:

Quando um método é invocado, o valor indicado para o parâmetro, na invocação, é copiado para o parâmetro do método. Neste caso particular, o valor da variável gerador é copiado para o parâmetro g.

Como estas duas variáveis são de um tipo não primitivo, já sabemos que o conteúdo de gerador é uma referência a um objeto. Então, o valor que é copiado para g é essa mesma referência.

Na representação da memória, acima, uma referência é representada por uma seta. Por isso, quando duas variáveis contêm referências iguais, as respetivas setas apontam para o mesmo objeto.

 

Se usarmos dois objetos Random criados da seguinte forma:

para gerar valores aleatórios sucessivos,exatamente da mesma maneira com cada um deles, como em:

não só g1 cria 10 valores pseudo aleatórios e g2 cria 10 valores pseudo aleatórios, como os valores que g1 cria são perfeitamente independentes dos valores criados por g2. Além disso, a cada nova execução deste conjunto de instruções são gerados valores potencialmente diferentes das execuções anteriores.

 

Podemos criar objetos Random usando outro construtor – o construtor Random(int seed) – com o qual podemos definir o valor inicial que serve de base para a criação dos valores aleatórios – a semente. Se criarmos dois objetos Random com a mesma semente e os fizermos gerar dez valores cada, como nas instruções que se seguem:

então g1 e g2 geram 10 valores pseudo aleatórios cada um, mas os valores que g1 gera são iguais aos valores gerados por g2 pois a semente usada na construção dos dois objetos foi a mesma. Repare que a única diferença entre este pedaço de código e o anterior é o construtor usado na criação dos objetos Random.

 

De igual forma, um gerador criado com uma semente gerará sempre a mesma sequência de valores. Por isso, o seguinte programa escreve sempre os mesmos 5 valores no ecrã de cada vez que é executado.

 

 

 


 

Anterior: 8.1. Pacotes

Seguinte: 8.3. Construindo uma String passo a passo