Spanish Generar números aleatorios que sumen una cifra concreta

ivavilagu

Member
Licensed User
Hola,

necesito crear una función a la que se le pasa dos valores:
  • Número de cifras aleatorias
  • Cantidad total que han de sumar dichas cifras
Es decir, si por ejemplo paso (5,75) me ha de devolver 5 números aleatorios comprendidos entre 1 y 75 que sumen 75.

Ninguno de los números aleatorios ha de ser cero ni negativo.

Saludos
 

JordiCP

Well-Known Member
Licensed User
De hecho, este problema es más de matemáticas que de programación.

Al fijar la suma, los resutados entre sí ya no son independientes, sino que están ligados de alguna manera (probabilidad condicionada)
Cada uno debe ser como mínimo =1, por lo tanto, ninguno de ellos debe superar 71
(A tener en cuenta: la funcion rnd(a,b) devuelve un numero entre a y b-1)

Una aproximación NO uniforme, que serviría por ejemplo en un caso como el siguiente: tenemos 75 bolas en una caja, viene una primera persona y saca un numero definido entre 1 y 71, luego una segunda, saca un numero aleatorio entre uno y las que queden menos 3, etc....

num_1=rnd(1,76-4)
num_2=rnd(1,76-3-num_1)
num_3=rnd(1,76-2-(num_1+num_2))
num_4=rnd(1,76-1-(num_1+num_2+num_3))
num_5=rnd(1,76-(num_1+num_2+num_3+num_4))

En general
B4X:
Sub Lista_Aleatorios(cuantos As Int, maximo As Int) As Int()

  If cuantos>maximo Then Return Null
  Dim numeros(cuantos) As Int
  Dim k As Int
  Dim parcial_acumulado As Int=0
  For k=1 To cuantos
    numeros(k-1)=Rnd(1,maximo+1-(cuantos-k)-parcial_acumulado)
    parcial_acumulado = parcial_acumulado + numeros(k-1)
  Next
  Return numeros
End Sub

Sin embargo, si quieres distribuciones realmente independientes (que cada conjunto de 5 numeros cuya suma sea 75 tenga la misma probabilidad que otro conjunto), la aproximación dada aquí es muy buena:
Por ejemplo, si tenemos: suma=75 y num_valores=5
Crea una lista
Genera 4 ( = num_valores-1) numeros aleatorios entre 1 y 74 ( = suma-1) --> lo generaremos mediante rnd(1,75) , ya que rnd(a,b) genera numeros entre a y b-1
Si no está en la lista, lo añadimos
Si ya lo está, generamos otro hasta que no esté en la lista.
Ordena la lista de menor a mayor.
Nuestros numeros serán:
numero(0) = lista(0)
numero(1) = lista(1)-lista(0)
numero(2) = lista(2)-lista(1)
numero(3) = lista(3)-lista(2)
numero(4) = 75 - lista(3)

Ya que estamos, añado el código
B4X:
Sub Lista_Aleatorios2(cuantos As Int, maximo As Int) As Int()

  If cuantos>maximo Then Return Null
  Dim l_dist As List
  l_dist.Initialize
  Dim k As Int =1
  Dim Num As Int
  Do While k<cuantos
      Num = Rnd(1,maximo)
    If l_dist.IndexOf(Num)=-1 Then  'Si no existe, lo añadimos a la lista
        l_dist.Add(Num)
        k=k+1
    End If
  Loop
  l_dist.Add(maximo)    'per al bucle que ve
  l_dist.Sort(True)
  Dim myNumbers(cuantos) As Int
  myNumbers(0)=l_dist.Get(0)
  For k=1 To cuantos-1
    myNumbers(k)=l_dist.Get(k)-l_dist.Get(k-1)
  Next

  Return myNumbers

End Sub
 

ivavilagu

Member
Licensed User
Joder, menuda respuesta. Es un placer leer réplicas tan extensas y detalladas ;)

Estoy realizando pruebas y funciona a la perfección excepto que si aleatoriamente se genera un número elevado y próximo a la suma, el resto de números pendientes quedan MUY condicionados a la baja.

Saludos y muchas gracias!!!
 

JordiCP

Well-Known Member
Licensed User
Jajaj, reconozco que me animé un poquito con el tema :D

Con la primera opción es mucho más probable que te salgan grupos tipo {28,31,7,4,5},{45,23,2,4,1},....,está más sesgado.

Con la segunda también puede suceder pero la distribución es más uniforme. Yo trabajaría con ésta.
 
Top