• Publicidad

Números aleatorios que siempre suman 1

¿Ya sabes lo que es una referencia? Has progresado, el nível básico es cosa del pasado y ahora estás listo para el siguiente nivel.

Números aleatorios que siempre suman 1

Notapor DeaWeb » 2009-06-25 10:09 @465

Hola amigos,

Estoy tratando de hacer una rutina que genere por vez un array de números aleatorios cuyos valores siempre sumen 1. Me explicaré mejor con un ejemplo:

Código: Seleccionar todo
DATO       NÚMERO
1              0.022
2              0.023
3              0.033
.               .
.               .
.               .
21            0.064
22            0.058
23            0.049
24            0.029
                ------
SUMA  ->   1.000


¿Alguien tiene alguna idea creativa de cómo hacer esto?

Saludos,

DW
Avatar de Usuario
DeaWeb
Perlero nuevo
Perlero nuevo
 
Mensajes: 5
Registrado: 2009-06-18 14:07 @630

Publicidad

Notapor DeaWeb » 2009-06-25 10:32 @480

Ok,

Olvidé decirles que ya tengo un código para esto, pero:

"con perl hay siempre más de una forma de hacerlo"


Habrá muchas formas, claro está, pero ¿cuál sería la más eficiente?

¡Saludos amigos!

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my @a = ();
$tot = 1;

$a[0] = rand($tot);
$tot -= $a[0];

for ($i = 1; $i <= 23; $i++) {
        $a[$i] = rand($tot);
        $tot -= $a[$i]
}

$a[24] = $tot;

$i = 0;
foreach $val (@a) {
        $i++;
        $sum += $val;
        print "$i $a[$i] $sum\n";
}
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Avatar de Usuario
DeaWeb
Perlero nuevo
Perlero nuevo
 
Mensajes: 5
Registrado: 2009-06-18 14:07 @630

Notapor explorer » 2009-06-25 19:32 @856

Buenas...

La solución propuesta está bien... salvo por el detalle que el casillero 24, en algunas ocasiones, se llevará buena parte del resto no colocado en las posiciones anteriores.

Yo he escogido la solución del juego del pachinko, basado en el quincunce o Tablero de Galton: un tablero lleno de clavos y unas bolitas que van cayendo, para acabar en unos casilleros.

En esto caso tenemos 1000 bolitas que las iremos haciendo caer de forma aleatoria por los 25 casilleros. Luego, es cuestión de dividir el número de bolitas por 1000:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

my $ancho = shift() || 25;  # El usuario nos pasa el tamaño o tomamos 25 por defecto

my @array;

for (0 .. 999) {            # Tenemos 1000 bolitas,
    $array[                 # que caen en un casillero,
        rand $ancho         # elegido al azar,
    ]++;                    # aumentando el número de bolitas en el casillero.
}

my $total = 0;

print "Dato\tNúmero\n";

for my $i (0 .. $#array) {
    my $v = $array[$i] / 1000;
    printf "%d\t%4.3f\t%s\n", $i, $v, '*' x $array[$i];
    $total += $v;
}

printf "\nSUMA:\t%4.3f\n", $total;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Una salida cualquiera:
Código: Seleccionar todo
Dato    Número
0       0.038   **************************************
1       0.045   *********************************************
2       0.042   ******************************************
3       0.047   ***********************************************
4       0.050   **************************************************
5       0.038   **************************************
6       0.043   *******************************************
7       0.044   ********************************************
8       0.022   **********************
9       0.038   **************************************
10      0.034   **********************************
11      0.034   **********************************
12      0.039   ***************************************
13      0.048   ************************************************
14      0.041   *****************************************
15      0.035   ***********************************
16      0.046   **********************************************
17      0.043   *******************************************
18      0.026   **************************
19      0.042   ******************************************
20      0.046   **********************************************
21      0.034   **********************************
22      0.042   ******************************************
23      0.037   *************************************
24      0.046   **********************************************

SUMA:   1.000


Puede parecer extraño que usemos matemática de enteros para luego ofrecer el resultado como si fuera un flotante, pero... se utiliza mucho más de lo que creemos.

¿Alguien da otra solución?
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14477
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor salva » 2009-06-27 19:30 @854

explorer, el método que propones tiene un problema, y es que los números que genera, en realidad, no son demasiado aleatorios. Además, en contra de lo que podría parecer, cuantas mas bolitas simulas, menos aleatorios son los números.

Para probarlo he hecho unas pequeñas modificaciones a tu script para que acepte un número variable de bolitas y para que al final imprima también la desviación típica de los números generados:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

my $bolitas = shift() || 1000;
my $ancho = shift() || 25;  # El usuario nos pasa el tamaño o tomamos 25 por defecto

my @array = (0) x $ancho;

for (1 .. $bolitas) {       # Tenemos algunas bolitas,
    $array[                 # que caen en un casillero,
        rand $ancho         # elegido al azar,
    ]++;                    # aumentando el número de bolitas en el casillero.
}

my $total = 0;
my $total2 = 0;
# print "Dato\tNúmero\n";

for my $i (0 .. $#array) {
    my $v = $array[$i] / $bolitas;
    # printf "%d\t%6.5f\n", $i, $v;
    $total  += $v;
    $total2 += $v * $v;
}

printf " SUMA:\t%6.5f\n", $total;
printf "s:\t%6.5f\n", sqrt($total2 / $bolitas - ($total / $bolitas) ** 2);
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Que al ejecutarlo para 10, 100, 1000, 10000 y 100000 da estos valores:

Código: Seleccionar todo
$ perl bolitas.pl 10
SUMA:   1.00000
s:   0.04472
$ perl bolitas.pl 100
SUMA:   1.00000

$ perl bolitas.pl 1000
SUMA:   1.00000
s:   0.00631
$ perl bolitas.pl 10000
SUMA:   1.00000
s:   0.00200
$ perl bolitas.pl 100000
SUMA:   1.00000
s:   0.00063


Estadísticamente, los números siguen una distribución binomial, de $media = 1 / $ancho y varianza = 1/$ancho * (1 - 1/$ancho) / $bolitas, que tiende a cero cuando $bolitas crece.

La solución al problema no es fácil. Lo primero que hace falta es saber para qué se van a usar los números para a continuación buscar una distribución que se ajuste a ese uso y finalmente desarrollar un método que genere números según la misma.
Avatar de Usuario
salva
Perlero nuevo
Perlero nuevo
 
Mensajes: 200
Registrado: 2008-01-03 15:19 @680

Re: Números aleatorios que siempre suman 1

Notapor explorer » 2009-07-04 10:41 @486

Bueno, otra opción... el "loco repartidor de tarta ciego".

Tenemos una tarta dividida en 1000 partes. Se acercan los comensales y comienza a repartir trozos de tamaño aleatorio y los reparte de forma aleatoria.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

my $comensales = shift() || 25;    # El usuario nos pasa el número o tomamos 25 por defecto

my $tarta      = shift() || 1000;

my @array;

while ($tarta > 0) {

    my $trozo = rand $tarta;

    $array[rand $comensales] += $trozo;

    $tarta -= $trozo;
}

my $total = 0;

print "Dato\tNúmero\n";

for my $i (0 .. $#array) {
    $array[$i] = 0 if !defined $array[$i];

    my $v = $array[$i] / 1000;
    printf "%d\t%4.3f\t%s\n", $i, $v, '*' x $array[$i];
    $total += $v;
}

printf "\nSUMA:\t%4.3f\n", $total;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Un ejemplo de salida:
Código: Seleccionar todo
Dato    Número
0       0.006   ******
1       0.000
2       0.000
3       0.000
4       0.000
5       0.080   *******************************************************************************
6       0.796   *******************************************************************************
***********************************************************************************************
***********************************************************************************************
***********************************************************************************************
***********************************************************************************************
***********************************************************************************************
***********************************************************************************************
***********************************************************************************************
****************************************************
7       0.000
8       0.000
9       0.000
10      0.000
11      0.014   *************
12      0.000
13      0.000
14      0.000
15      0.023   ***********************
16      0.000
17      0.000
18      0.000
19      0.033   *********************************
20      0.000
21      0.000
22      0.041   *****************************************
23      0.005   *****
24      0.000

SUMA:   1.000
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14477
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Intermedio

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 3 invitados

cron