numeros - porcentajes aleatorios de java
math.random netbeans (12)
Aquí hay un código que escribí para un programa que estoy creando. Encontré este hilo al intentar resolver este problema exacto, así que espero que esto ayude a algunos otros. El diseño se basó en la respuesta de @eruonna de lectura anterior.
public static int[] randomNumbers(int numOfNumbers){
int percentN = numOfNumbers;
int[] intArray = new int[101];
//set up the array with values
for(int i = 0; i < intArray.length; i++){
intArray[i] = i;
}
//set up an array to hold the selected values
int[] selectionArray = new int[(percentN - 1)];
//run a for loop to go through and select random numbers from the intArray
for(int n = 0; n < selectionArray.length; n++){
int randomNum = (int)(Math.random() * 100);
selectionArray[n] = intArray[randomNum];
}
//bubble sort the items in the selectionArray
for(int out = (selectionArray.length - 1); out > 1; out--){
for(int in = 0; in < out; in++){
if(selectionArray[in] > selectionArray[in + 1]){
int temp = selectionArray[in];
selectionArray[in] = selectionArray[in + 1];
selectionArray[in + 1] = temp;
}
}
}
//create an array to hold the calculated differences between each of the values to create random numbers
int[] calculationArray = new int[percentN];
//calculate the difference between the first item in the array and 0
calculationArray[0] = (selectionArray[0] - 0);
//calculate the difference between the other items in the array (except for the last value)
for(int z = 1; z < (calculationArray.length - 1); z++){
calculationArray[z] = (selectionArray[z] - selectionArray[z - 1]);
}
//calculate the difference for the last item in the array
calculationArray[(calculationArray.length - 1)] = (100 - selectionArray[(selectionArray.length - 1)]);
return calculationArray;
}
Necesito generar n porcentajes (enteros entre 0 y 100) de manera que la suma de todos los n números sume 100.
Si hago nextInt()
n veces, asegurándome cada vez que el parámetro es 100 menos la suma acumulada previamente, mis porcentajes están sesgados (es decir, el primer número generado generalmente será el más grande, etc.). ¿Cómo hago esto de manera imparcial?
Es posible que necesite definir lo que realmente quiere decir con "sesgado", pero si lo único que le importa es que la distribución de los números es independiente de su posición, simplemente puede crear los números de manera "sesgada" y luego asignar al azar sus números. posiciones
Otro método "imparcial" sería crear n-1 porcentajes aleatorios, ordenarlos (llame a esto x1 x2 x3 ...) y luego definir sus porcentajes finales para que sean:
x1
x2 - x1
x3 - x2
...
100 - x(n-1)
De esa manera obtendrás n números aleatorios que suman 100.
Este problema se conoce como muestreo uniforme de un simplex y Wikipedia proporciona dos algoritmos:
http://en.wikipedia.org/wiki/Simplex#Random_sampling
Vea también estas preguntas relacionadas:
Genere n enteros aleatorios con cualquier rango (llámelos a a[1]
.. a[n]
). Suma tus enteros y llama a eso b
. Sus porcentajes serán [a[1]/b, ..., a[n]/b]
.
Edición: buenos puntos, redondear los resultados para sumar exactamente 100 no es trival. Un enfoque sería tomar el piso de a[x]/b
para x
en 1..n
como sus números enteros, luego distribuir las unidades restantes 100-(sum of integers)
al azar. No estoy seguro de si esto introduciría algún sesgo en el resultado.
Haz una matriz. Caiga aleatoriamente el 100% en cada una de las partes de esa matriz. El ejemplo muestra n = 7.
import java.util.Random;
public class random100 {
public static void main (String [] args) {
Random rnd = new Random();
int percents[] = new int[7];
for (int i = 0; i < 100; i++) {
int bucket = rnd.nextInt(7);
percents[bucket] = percents[bucket] + 1;
}
for (int i = 0; i < 7; i++) {
System.out.println("bucket " + i + ": " + percents[i]);
}
}
}
Imagina que tienes 100 piedras y N cubetas para colocarlas. Puedes tomar las 100 y colocarlas en una cubeta aleatoria. De esta manera, el total será de los 100 con los que comenzó y no habrá sesgos entre ningún grupo.
public static int[] randomBuckets(int total, int n_buckets) {
int[] buckets = new int[n_buckets];
Random rand = new Random();
for(int i=0;i<total;i++)
buckets[rand.nextInt(n_buckets)]++;
return buckets;
}
public static void main(String... args) {
for(int i=2; i<=10;i++)
System.out.println(Arrays.toString(randomBuckets(100, i)));
}
Huellas dactilares
[55, 45]
[38, 34, 28]
[22, 21, 32, 25]
[28, 24, 18, 15, 15]
[17, 14, 13, 21, 18, 17]
[17, 19, 14, 15, 6, 15, 14]
[11, 14, 14, 14, 4, 17, 9, 17]
[13, 12, 15, 12, 8, 10, 9, 11, 10]
[11, 13, 12, 6, 6, 11, 13, 3, 15, 10]
A medida que aumenta el conteo, la distribución se acerca de manera uniforme.
System.out.println(Arrays.toString(randomBuckets(100000000, 100)));
Huellas dactilares
[1000076, 1000612, 999600, 999480, 998226, 998303, 1000528, 1000450, 999529,
998480, 998903, 1002685, 999230, 1000631, 1001171, 997757, 1000349, 1000527,
1002408, 1000852, 1000450, 999318, 999453, 1000099, 1000759, 1000426, 999404,
1000758, 1000939, 999950, 1000493, 1001396, 1001007, 999258, 1001709, 1000593,
1000614, 1000667, 1000168, 999448, 999350, 1000479, 999991, 999778, 1000513,
998812, 1001295, 999314, 1000738, 1000211, 999855, 999349, 999842, 999635,
999301, 1001707, 998224, 1000577, 999405, 998760, 1000036, 1000110, 1002471,
1000234, 1000975, 998688, 999434, 999660, 1001741, 999834, 998855, 1001009,
999523, 1000207, 998885, 999598, 998375, 1000319, 1000660, 1001727, 1000546,
1000438, 999815, 998121, 1001128, 1000191, 998609, 998535, 999617, 1001895,
999230, 998968, 999844, 999392, 999669, 999407, 998380, 1000732, 998778, 1000522]
La clave es generar N números aleatorios entre 0 y 100, pero usarlos como "marcadores" en lugar de la secuencia final de números para generar. Luego, recorre su lista de marcadores en orden ascendente, calculando cada porcentaje para generar como (marcador actual - marcador anterior).
Esto proporcionará una distribución mucho más uniforme que la simple generación y salida de cada número uno a la vez.
Ejemplo
import java.util.Random;
import java.util.TreeSet;
import java.util.SortedSet;
public class Main {
public static void main(String[] args) {
Random rnd = new Random();
SortedSet<Integer> set = new TreeSet<Integer>();
for (int i=0; i<9; ++i) {
set.add(rnd.nextInt(101));
}
if (set.last() < 100) {
set.add(100);
}
int prev = 0;
int total = 0;
int output;
for (int j : set) {
output = j - prev;
total += output;
System.err.println(String.format("Value: %d, Output: %d, Total So Far: %d", j, output, total));
prev = j;
}
}
}
Salida
$ java Main
Value: 0, Output: 0, Total So Far: 0
Value: 2, Output: 2, Total So Far: 2
Value: 55, Output: 53, Total So Far: 55
Value: 56, Output: 1, Total So Far: 56
Value: 57, Output: 1, Total So Far: 57
Value: 69, Output: 12, Total So Far: 69
Value: 71, Output: 2, Total So Far: 71
Value: 80, Output: 9, Total So Far: 80
Value: 92, Output: 12, Total So Far: 92
Value: 100, Output: 8, Total So Far: 100
Para ser precisos, depende exactamente de cómo desea que las muestras sean imparciales. Aquí hay una manera aproximada que te dará un buen resultado.
- Genere
n-1
enteros de 0, .. 100, digamosa[i]
parai = 0, to n-2
. - Sea
total
la suma de estos números. - Calcule
b[i] = floor(100*a[i]/total)
parai = 0, to n-2
- Establecer
b[n-1] = 100 - (b[0] + ... b[n-2])
.
Entonces b es tu matriz resultante de porcentajes.
El último será parcial, pero el resto debe ser uniforme.
Por supuesto, si desea hacer esto de una manera más precisa, tendrá que usar el muestreo de Gibbs o los ajustes de Metropolis.
Primero, solución obvia.
do
int[] a = new int[n];
for (int i = 0; i < n; ++i) {
a[i] = random number between 0 and 100;
}
until sum(a) == 100;
No es perfecto en términos de complejidad (el número de iteraciones para alcanzar la suma 100 puede ser bastante grande), pero la distribución es seguramente "imparcial".
editar
Problema similar: ¿cómo generar un punto aleatorio en un círculo con radio 1 y centro en (0, 0)? Solución: continúe generando puntos aleatorios en el rango (cuadrado) [-1..1, -1..1] hasta que uno de ellos encaje en el círculo :)
Tuve un problema similar y terminé haciendo lo que dijiste, generando enteros aleatorios hasta la diferencia de la suma de los enteros existentes y el límite. Entonces aleatoricé el orden de los enteros. Funcionó bastante bien. Eso fue por un algoritmo genético.
Un par de respuestas sugieren escoger porcentajes al azar y tomar las diferencias entre ellos. Como señala Nikita Ryback, esto no dará una distribución uniforme en todas las posibilidades; en particular, los ceros serán menos frecuentes de lo esperado.
Para solucionar esto, piense en comenzar con 100 ''porcentajes'' e insertar divisores. Voy a mostrar un ejemplo con 10:
% % % % % % % % % %
Hay once lugares donde podríamos insertar un divisor: entre dos por ciento o al principio o al final. Así que inserte uno:
% % % % / % % % % % %
Esto representa elegir cuatro y seis. Ahora inserte otro divisor. Esta vez, hay doce lugares, porque el divisor ya insertado crea y extra uno. En particular, hay dos maneras de conseguir
% % % % / / % % % % % %
ya sea insertando antes o después del divisor anterior. Puede continuar el proceso hasta que tenga tantos separadores como necesite (uno menos que el número de porcentajes).
% % / % / % / / % % % / % % % /
Esto corresponde a 2,1,1,0,3,3,0.
Podemos demostrar que esto da la distribución uniforme. El número de composiciones de 100 en k partes es el coeficiente binomial 100 + k-1 elegir k-1. Es decir (100 + k-1) (100 + k-2) ... 101 / (k-1) (k-2) * ... * 2 * 1 Por lo tanto, la probabilidad de elegir cualquier composición en particular es la recíproca de esta. A medida que insertamos los divisores uno a la vez, primero elegimos entre 101 posiciones, luego 102, 103, etc. hasta que obtenemos 100 + k-1. Entonces, la probabilidad de cualquier secuencia particular de inserciones es 1 / (100 + k-1) * ... * 101. ¿Cuántas secuencias de inserción dan lugar a la misma composición? La composición final contiene divisores k-1. Se podrían haber insertado en cualquier orden, así que hay (k-1)! Secuencias que dan lugar a una composición dada. Así que la probabilidad de cualquier composición particular es exactamente lo que debería ser.
En el código real, probablemente no representarías tus pasos de esta manera. Deberías poder mantener solo los números, en lugar de las secuencias de porcentajes y divisores. No he pensado en la complejidad de este algoritmo.
Una vez que elija los números con el método que describe, mezcle el orden de los números. De esta manera la lista final de números tiene una distribución más uniforme.
Sin embargo, tenga en cuenta que no importa lo que haga, no puede obtener una distribución perfectamente uniforme, ya que una vez que comienza a elegir números, sus pruebas aleatorias no son independientes. Ver la respuesta de ataylor.
Tenga en cuenta también que el algoritmo que describe puede que no le proporcione el resultado requerido. El último número no puede ser aleatorio ya que debe hacer que la suma sea igual a 100.