Imagina el siguiente caso:
Tomamos una bolsa y pedimos a todos los alumnos de tu clase, que escriban su nombre, lo doblen y lo metan en la bolsa.
ahora pedimos que se saque un nombre y lo anotasmos en la pizarra, volvemos a guardar el papelito en la bolsa, y pedimos otro... y así tantos como quieras.
Entiendes que la posibilidad de que salgan nombres reptidos resulta inevitable?.
Vamos a hacer que sea evitable... todo lo que necesitamos sabes es cuantos números queremos tener (o alumnos de tu clase con nombre distintos), pongamos que aunque en tu clase haya 35 alumnos solo hay 20 nombres distintos, el resto se repiten.
Bien, como solo queremos que salgan una única vez, solo escribimos el nombre de esos 20 únicos en un papelito, l odoblamos y lo metemos en la bolsa... la agitamos (barajamos), y ahora cuando se saque uno de la bolsa, se queda fuera...(lo metemos en otra bolsa, por ejemplo)... el resultado es que saldrán nombres sin repetirse.
Si lo has entendido hasta aquí, ahora un poco de pseudocódigo:
array bytes Lista()
// los metemos en la bolsa (1 vez cada uno distinto)
// permitimos elegir desde 0 a 19, lo mismo que desde 35 a 54
// (para 20 elementos de cantidad, del ejemplo).
Funcion GenerarListaSinRepeticion(array entero Lista(), entero Cantidad, entero Inicial = 0)
entero k
alojar memoria para Lista(0 a Cantidad-1)
bucle para k desde Inicial hasta Cantidad-1+Inicial
lista(k-inicial) = k
siguiente
Fin funcion
// ahora los barajamos: pero en vez de meterlos en otra bolsa (array)
// lo depositamos al final y el que está allí, lo dejamos en el índice obtenido al azar,
// estos es intercambian su posición...
Funcion Barajar(array entero Lista(), entero Cantidad)
entero k, index, tmp
Bucle para k desde Cantidad-1 hasta 1 regresivamente
index = random(entre k y 0)
tmp = lista(index)
lista(index) = lista(k)
lista(k) = tmp
Siguiente
Fin funcion
Ahora los ítems en el array están barajados, de forma aleatoria, aparecen una sola vez, y puedes tomarlos linealmente , cuando los consumas (llegues al último), debes barajarlos nuevamente. Y si necesitas cambiar la cantidad invocas de nuevo la función GenerarListaSinRepeticion.
Fíjate que se puede personalizar todo lo que se quiera... imagina que quiero que salga 5 veces 3 de ellos, 4 veces 10 de ellos, 2 veces 25 de ellos y 1 sola vez 500 elementos...
Lo primero es totalizar cuantos elementos se necesitan:
Cantidad --- Repetidos:
3 x 5 = 15
10 x 3 = 30
25 x 2 = 50
500 x 1 = 500
-------------------
TOTAL = 595
Luego debemos crear un array con 595 elementos, y luego condiconarlos a que se repitan las veces pedidas...
Como interesa que no haya que hacer cada vez una función distinta para cada caso, es mejor parametrizarlo para reutilizarlo una y otra vez con solo 2 funciones:
estructura Articulos
entero Cantidad
entero Repeticiones
fin estructura
Array entero Lista() = funcion GenerarArray(array de articulo Reparto() )
entero Total, k, n
// Calcular cuantos ítems totales hay.
Bucle para k desde 0 hasta reparto.Length-1 // cantidad de ítems en el array.
total += Reparto(K).Cantidad * Reparto(k).Repeticiones
Siguiente
// dimensionar el array en memoria al tamaño total recién calculado
alojar array Lista(0 hasta Total -1)
// Introducir la cantidad de repetidos de cada 'clase'
n = 0
v = 0
Bucle para k desde 0 hasta reparto.Length-1
AddReparto(Lista, n,v, Reparto(k).Cantidad, Reparto(k).Repetidos)
// mejor que sean pasados por referencia y se actualizan allí con cada llamada.
//n += (Reparto(k).Cantidad * Reparto(k).Repetidos)
//v + = Reparto(k).Cantidad
Siguiente
Fin funcion
// Index y Valor son parámetros pasados por referencia
// (para recibir de vuelta valores actualizados para el siguiente ciclo).
Funcion AddReparto(array entero Lista(), entero Index, entero Valor, entero Cantidad, entero Repes)
entero j, k
Bucle para j desde 1 hasta Cantidad
Bucle para k desde 1 hasta Repes
Lista(index) = Valor
Index += 1
Siguiente
Valor += 1
Siguiente
Fin funcion
Para probarlo, metamos en el array Reparto, los valores que dimos antes de ejemplo:
Cantidad --- Repetidos:
3 x 5 = 15
10 x 3 = 30
25 x 2 = 50
500 x 1 = 500
-------------------
TOTAL = 595
Código de ejemplo para llamar a las funciones
Array Articulos Reparto(0 hasta 3)
Reparto(0).Cantidad = 3
Reparto(0).Repetidos = 5
Reparto(0).Cantidad = 10
Reparto(0).Repetidos = 3
Reparto(0).Cantidad = 25
Reparto(0).Repetidos = 2
Reparto(0).Cantidad = 500
Reparto(0).Repetidos = 1
Lista = GenerarArray(Reparto() )
//ImprimirLista(Lista, Lista.Lenght)
//Barajar(Lista, Lista.Lenght)
//ImprimirLista(Lista, Lista.Lenght)
Si imprimes el array antes de barajar... ahí verás:
Indice - repeticiones del valor
(3 valores se repiten 5 veces cada uno)
0 - 0
1 - 0
2 - 0
3 - 0
4 - 0
5 - 1
6 - 1
7 - 1
8 - 1
9 - 1
10 - 2
11 - 2
12 - 2
13 - 2
14 - 2
-----------
(10 valores se repiten 3 veces cada uno)
15 - 3
16 - 3
17 - 3
18 - 4
19 - 4
20 - 4
... (si no me equivoco, esto acaba en)
42 - 12
43 - 12
44 - 12
--------------
(25 valores se repiten 2 veces cada uno)
....
(xx .... .... ... ...)
.....
(500 valores sin repetirse (solo 1 vez aparece)
95 - 38
96 - 39
97 - 40
.....
593 - 536
594 - 537
--- FIN ---
Para terminar, te propongo un sencillo ejercicio: Imagina un mazo de cartas, que son típicamente 4 palos de baraja con valores del 1 al 10-12 (baraja española)
Date cuenta como cambiaría el código de crear la lista, pués ahora cada uno se repite 4 veces... crea dicha función y luego barájala, saca por pantalla todo el array y verifica que en efecto han salido 4 veces cada 'carta'.
Unos años atrás redacté en wikipedia el siguiente artículo bastante exhaustivo al respecto sobre el barajado aleatorio... te podría interesar hecharle un ojo, cuando tengas tiempo.
https://es.wikipedia.org/wiki/Algoritmo_de_Fisher-Yates
p.d. Olvidaba señalarte que Java contiene este barajado, localiza un método llamado 'Shuffle' en la clase random (OJO: La función de barajado, no la que hace los repartos en las cantidades que interese), pero aprenderás más entendiendo el tocho que te he redactado.