Spanish Generar números aleatorios que sumen una cifra concreta

ivavilagu

Member
Licensed User
Longtime 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

Expert
Licensed User
Longtime 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
Longtime 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

Expert
Licensed User
Longtime 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.
 

gainax00

Member
Hola!!!! pues copie el código como tal y agregue un:
log y un EditText1.text
con la opción de:
#BridgeLogger: True

pero no me entrega ningún dato.
los datos se los mando así:

Sub Button1_Click
xui.MsgboxAsync("Hello world!", "B4X")
Lista_Aleatorios2(1,25)
End Sub

pero no obtengo ningun dato, alguna idea del porque???

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
Log(myNumbers)
Return myNumbers
EditText1.text=myNumbers
End Sub

Gracias!!
y
F3l1s c0d1f1c4c10n
 

JordiCP

Expert
Licensed User
Longtime User
Buenas,

Es mejor que cuando postees código, lo pongas entre [ CODE] ...aqui el código... [ /CODE], así es más legible
(sin los espacios después del "[" )

Hay un par de cosas a modificar en tu código

B4X:
Sub Button1_Click
   xui.MsgboxAsync("Hello world!", "B4X")
   Lista_Aleatorios2(4,25)               '<-- con Lista_Aleatorios(1,25) funcionaba, pero siempre devuelve un solo elemento que vale 25
End Sub

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

   'Log(myNumbers)                                      '<-- Para hacer un log de un array, se debe hacer de cada uno de sus elementos
   Dim resultString As String
   For k=0 to myNumbers.length-1              '<-- Convertimos uno a uno y lo ponemos a un string
       resultString = resultString & myNumbers(k) & ","
   Next
   Log( resultString)

   'Return myNumbers                '<-- Lo comentamos, o ya no se ejecutará lo de después

   'EditText1.text=myNumbers    '<-- Lo mismo que el Log(), no se puede convertir todo un array a texto, se tiene que hacer elemento a elemento o con alguna funcion intermedia
   EditText1.text = resultString

  Return myNumbers                  '<-- Lo ponemos aquí
End Sub
 

gainax00

Member
Buenas,

Es mejor que cuando postees código, lo pongas entre [ CODE] ...aqui el código... [ /CODE], así es más legible
(sin los espacios después del "[" )

Hay un par de cosas a modificar en tu código

B4X:
Sub Button1_Click
   xui.MsgboxAsync("Hello world!", "B4X")
   Lista_Aleatorios2(4,25)               '<-- con Lista_Aleatorios(1,25) funcionaba, pero siempre devuelve un solo elemento que vale 25
End Sub

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

   'Log(myNumbers)                                      '<-- Para hacer un log de un array, se debe hacer de cada uno de sus elementos
   Dim resultString As String
   For k=0 to myNumbers.length-1              '<-- Convertimos uno a uno y lo ponemos a un string
       resultString = resultString & myNumbers(k) & ","
   Next
   Log( resultString)

   'Return myNumbers                '<-- Lo comentamos, o ya no se ejecutará lo de después

   'EditText1.text=myNumbers    '<-- Lo mismo que el Log(), no se puede convertir todo un array a texto, se tiene que hacer elemento a elemento o con alguna funcion intermedia
   EditText1.text = resultString

  Return myNumbers                  '<-- Lo ponemos aquí
End Sub

Muchas Gracias!!!!
Con eso de que el post es del 2015
no pensé que alguien responderia...
estoy muy agradecido, me has ayudado a generar números para generar una " licencia"
así cómo se hacia con los seriales de windows jejejje
Gracias de nuevo y feliz día!!!
 

gainax00

Member
Hola!! de nuevo
y para el caso en que yo tenga esos números 2 4 6 8 16 (que suman 36) dentro de un edittext.text
y quiera sumarlos, cómo tendría que proceder??
Gracias
y
F3l1z c0d1f1c4c10n
 
Top