Ejercicio básico de combinaciones

Iniciado por luis456, 25 Abril 2021, 11:21 AM

0 Miembros y 1 Visitante están viendo este tema.

Serapis

#20
Hola.
Hoy he llegado tarde del trabajo, pero todavía he sacado un tiempito para calcular las cantidades...

Dado que (ahora) varía la cantidad de unidades y decenas que pueden aparecer, lo adecuado es categorizar, pués a unidades iguales las firmas son las mismas, resumiendo:

- Con 0 decenas y 6 unidades: No puede darse: ej: 5,6,7,8,9 ?
  Falta 1 dígito para poder formar 6 'apuestas'.

- Con 1 decenas y 5 unidades: Puede o no aceptarse (técnicamente no, pues estaríamos inventando un dígito 0): ej: 3;  5,6,7,8,9
35, 36, 37, 38, 39, 03?
En cualquier caso si se aceptara, sería una sola combinación y por tanto puede 'calcularse' manualmente. Luego puede dejarse fera.

- Con 2 decenas y 4 unidades: Salen 210 firmas distintas (son las calculadas anteriormente).
XY-RSTU
-------------------
XY XR XS XT XU
YX YR YS YT YU
Ej: 0,1,___6,7,8,9
10, 08, 09, 16, 17, 19

- Con 3 decenas y 3 unidades: Salen 5.005 firmas.
XYZ-RST
-------------------
XY XZ XR XS XT
YX YZ YR YS YT
ZX ZY ZR ZS ZT
Ej: 0,1,2,___ 7,8,9
  20, 17, 28, 09, 27, 12

- Con 4 decenas y 2 unidades: Salen 38760 143.080 firmas.
WXYZ-RS
-------------------
WX WY WZ WR WS
XW XY XZ XR XS
YW YX YZ YR YS
ZE ZX ZY ZR ZS
Ej: 0,1,2,3,___ 8,9
38,39,13, 01,29, 12

- Con 5 decenas y 1 unidad: Salen 177100 402.895 firmas.
VWXYZ-R    
-------------------    
VW VX VY VZ VR
WV WX WY WZ WR
XV XW XY XZ XR
YV YW YX YZ YR
ZV ZW ZX ZY ZR

Ej: 0,1,2,3,4,___ 9
  20,31,49, 43, 40

En total se aproximan al 1/4 de millón suben por encima del medio millón (y solo son las firmas).

p.d.: Mañana que vengo con más tiempo veré si me da tiempo a hacerlo.
p.d.2: Actualizado los valores de las combinaciones (había un error).

Serapis

#21
Me ha dado tiempo a rehacer lo que habíamos hecho, el resto es más de lo mismo y mañana en otro ratito lo completo... Al lío...

Dado que ahora tendremos varias categorías, es más eficiente crear una interfaz e implementar una clase por cada categoría, que andar complicando algoritmos con condicionales.

El código que tenías del proyecto previo, guardalo o bórralo... los cambios manuales son demasiados y es preferible copiar y pegar.

Añade un interfaz al proyecto. Este es el código de la interfaz:
Código (vbnet) [Seleccionar]

Public Interface ICombina

   ''' <summary>
   ''' Cantidad de valores enumerados.
   ''' </summary>
   ReadOnly Property Cantidad As UInt32

   ''' <summary>
   ''' Recrea el array de las combinaciones al inicio.
   ''' </summary>  
   Sub Enumerar()

   ''' <summary>
   ''' Decodifica el valor enumerado en la posicion indicada, a 6 pares de valores numericos.
   ''' </summary>
   ''' <param name="Index">Indice de la enumeracion solicitado</param>
   ''' <param name="Decenas">Array de los digitos que (ademas) pueden actuar como decenas</param>
   ''' <param name="Unidades">Array de los digitos que solo pueden actuar como unidades</param>
   ''' <returns>Devuelve la combinacion con las 6 apuestas separadas por ', '"</returns>
   Function Tranformar(ByVal Index As Int32, ByRef Decenas() As Byte, ByRef Unidades() As Byte) As String
End Interface

Los comentarios los puedes borrar si lo prefieres...
NOTA: La interfaz debe llamarse como figura ahí... luego que esté todo el código, podrás cambiarlo si lo prefieres.

Añade un módulo de código si no tienes alguno. Algunas funciones y extensiones de métodos se han trasladado aquí. es código estático (independiente de cualquier instancia). Este es el código a añadir dentro del módulo:
Código (vbnet) [Seleccionar]

   <Extension()>
   Public Function EnRango(ByVal Valor As Byte, ByVal Minimo As Byte, ByVal Maximo As Byte) As Boolean
       Return ((Valor >= Minimo) And (Valor <= Maximo))
   End Function
   <Extension()>
   Public Function EnRango(ByVal Valor As Char, ByVal Minimo As Byte, ByVal Maximo As Byte, ByVal Tope As Byte) As Boolean
       Dim asci As Byte = (Strings.Asc(Valor) - Tope)
       Return ((asci >= Minimo) And (asci <= Maximo))
   End Function


   Public Function EnumeraComb(ByRef Inicio As String, ByRef Final As String, ByVal Cantidad As UInteger) As String()
       Dim k As UInt32 = 0
       Dim Combina As String = Inicio
       Dim Maxchar As Byte = IndexLastChar(Inicio, Final) ' Final.Chars(Final.Length-1)
       Dim Permutas(0 To Cantidad - 1) As String
       Dim digitos(0 To 5) As Byte, n As Byte = 0

       For k = 0 To 5
           digitos(k) = k
       Next

       k = 0
       Do
           Permutas(k) = Combina
           If (digitos(5) = Maxchar) Then
               n = 4
               Do While Combina.Chars(n) = Combina.Chars(n + 1).Previo   '    digitos(n) = digitos(n + 1) - 1
                   n -= 1
               Loop

               digitos(n) += 1
               For n = n + 1 To 5
                   digitos(n) = digitos(n - 1) + 1
               Next

               Combina = "" ' 0
               For n = 0 To 5
                   Combina &= CharIndexAZ(digitos(n)) ' * (10 ^ (5 - n))
               Next
           Else
               Combina = Combina.NextComb()
               digitos(5) += 1
           End If
           k += 1
       Loop While (Combina < Final)
       Permutas(k) = Combina

       Return Permutas
   End Function

#Region "Las siguiente funciones y extensiones son solo de ayuda para la función anterior (enumerar)."
   <Extension()>
   Private Function NextComb(ByRef Combina As String) As String
       Dim Last As Byte = Asc(Combina.Chars(5))  ' ojo Estamos usando siempre longitudes de 6 caracteres.
       Return Strings.Left(Combina, 5) & Strings.Chr(Last + 1)
   End Function

   ''' <summary>
   ''' Calcula el caracter previo en la secuencia ASCII.
   ''' </summary>
   ''' <param name="Ch">Caracter en el rango A-Z</param>
   ''' <returns>Devuelve el carácter previo al dado (en la secuencia ASCII).</returns>
   ''' <remarks></remarks>
   <Extension()>
   Private Function Previo(ByRef Ch As Char) As Char
       Dim n As Byte = Strings.Asc(Ch) - 1
       Return Strings.Chr(n)
   End Function

   Private Function CharIndexAZ(ByRef Index As Byte) As Char
       Return Strings.Chr(65 + Index)
   End Function

   ' Devuelve la distancia entre el primer caracter de inicio y el último de 'final'
   ' Ejemplo: "'A'BCDEF" "EFGHI'J'" = 10;  "'A'BCDEF" "MNOPQ'R'" = 17
   Private Function IndexLastChar(ByRef Inicio As String, ByRef Final As String) As Byte
       Dim F As Byte = Strings.Asc(Final.Chars(Final.Length - 1))
       Dim I As Byte = Strings.Asc(Inicio.Chars(0))

       Return (F - I)
   End Function
#End Region


Se ha añadido alguna función, la principal es 'enumerarCombinacion'.
Las extensiones de métodos, son métodos que simplemente cambia la forma en que se llaman.
Si tu creas por ejemplo una función Ordenar que ordena un array, lo llamarías así:
Código (vbnet) [Seleccionar]
call Ordenar(elArray, parametro1, parametro2)
Mediante una extensión, resulta más cómodo, pués se recurre al intellisense, para que pueda invocarse así:
 
Código (vbnet) [Seleccionar]
elArray.Odenar(parametro1, parametro2)
Con lo que parece un método nativo más del tipo de datos al que se extiende. Naturalmente la extensión solo es aplicable al proyecto actual.

Finalmente volvemos a la ventana principal... (se asume que ahora mismo está vacía).
Código (vbnet) [Seleccionar]

Public Class Form1
   Private perm24, perm33, perm42, perm51,perm60 As ICombina
   Private Decenas() As Byte, Unidades() As Byte

   Public Sub New()
       InitializeComponent()
       ' Crear las instancias una única vez.
       perm24 = New Permutas2_4
       perm33 = New Permutas3_3
       perm42 = New Permutas4_2
       perm51 = New Permutas5_1
       perm60 = New Permutas6_0
   End Sub


   Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
       Dim Nums() As String, txt As String
       Dim k As UInt16, n As Byte
       Dim Permuta As ICombina

       txt = TextBox1.Text.Replace(" ", "")
       Nums = txt.Split(",")
       If (Nums.Length = 6) Then
           Array.Sort(Nums)
           Do While (Nums(n).Chars(0).EnRango(0, 4, 48) = True) And (n < 5)
               n += 1
           Loop

           If (n.EnRango(1, 5) = True) Then
               ReDim Decenas(0 To n - 1)
               ReDim Unidades(0 To 6 - n - 1)

               For k = 0 To n - 1
                   Decenas(k) = System.Byte.Parse(Nums(k))
               Next
               For k = n To 5
                   Unidades(k - n) = System.Byte.Parse(Nums(k))
               Next

               Select Case n
                   Case 2 : Permuta = perm24
                   Case 3 : Permuta = perm33
                   Case 4 : Permuta = perm42
                   Case 5 : Permuta = perm51
                   case  6: Permuta = perm60
               End Select

               ListBox1.Items.Clear()
               For j = 0 To Permuta.Cantidad - 1
                   ListBox1.Items.Add(Permuta.Tranformar(j, Decenas, Unidades))
               Next
               Label3.Text = "Cantidad en lista: " & Permuta.Cantidad.ToString ' ListBox1.Items.Count.ToString
           Else
               MessageBox.Show("No se puede generar combinaciones si no hay 2-5 dígitos menores de 5.")
           End If
       Else
           MessageBox.Show("No, no no... Debe haber 6 y solo 6 numeros, separados por 1 coma...")
       End If
   End Sub
End Class


Se han sacado las clases fuera de la clase del formulario, por claridad.
Ahora hay una instancia de la interfaz 'ICombina', por cada categoría...
Se considera una categoría, a cada diferenciación en la relación de cantidad entre dígitos que pueden ser decenas de los que no.
Un ejemplo: Sean los dígitos 3, 6, 8, 0, 5, 1  ...hay 3 valores que pueden ser decenas: 0,1 y 3, luego pertenecen a la categoróa asociada a la intancia 'perm3_3 (3 decenas y 3 unidades, para qué complicarnos más en el nombre).
Esta diferenciaciónd e categorías es necesaria, porque cada una presenta diferente cantidad de firmas, como ya se señaló en el mensaje anterior.
en lo que al código de la función del botón respecta, para hallar a qué categoría pertenece, tras crear el array con los dígitos (nums), lo ordenamos, ahora un recorrido con un bucle nos permite contarlos, y luego selecciona la clase que operará conforme a la categoría reconocida.
Se descartan los casos en que haya 0 o 1 decena solo (5,5,6,7,8,9) y (2, 5,6,7,8,9) y también cuando sean 6 decenas (esto directamente lo he descartado, generaría varios millones de combinaciones)

En la misma ventana del formulario, bajo la clase de dicho formulario, irán la implementación de cada clase.
Como digo, de momento, solo he dejado lista, la conversión d ela primera (que es equivalente a la que ya hice), las otras son básicamente rehacer lo mismo con pequeños cambios en la función transformar, ya mañana en otro ratito, lo completo.
Código (vbnet) [Seleccionar]


Friend Class Permutas2_4
   Implements ICombina

   Const MAX_COMBINAS As UInt16 = 210
   Private s_Combinas() As String     ' UInt32

   Public Sub New()
       Call Enumerar()
   End Sub

   Public ReadOnly Property Cantidad As UInteger Implements ICombina.Cantidad
       Get
           Return MAX_COMBINAS
       End Get
   End Property

   ' Para generar las permutaciones, delega en la función que yace en el módulo.
   Private Sub Enumerar() Implements ICombina.Enumerar
       '             combinacion inicial, final, Total combinaciones
       s_Combinas = EnumeraComb("ABCDEF", "EFGHIJ", MAX_COMBINAS)
   End Sub

   ' Alfabeto = X,Y,   R,S,T,U
   '  A   B   C   D   E      F   G   H   I   J
   '  XY  XR  XS  XT  XU     YR  YS  YT  YU  YX            
   Public Function Tranformar(ByVal Index As Integer, ByRef Decenas() As Byte, ByRef Unidades() As Byte) As String Implements ICombina.Tranformar
       Dim permuta As String = s_Combinas(Index)
       Dim s(0 To 5) As String, valor As String = ""
       Dim x As Byte = 0, k As Byte

       For k = 0 To 5
           x = (Strings.Asc((permuta.Chars(k))) - 65)

           Select Case x
               Case 0 : x = ((Decenas(0) * 10) + Decenas(1))
               Case 1 To 4 : x = ((Decenas(0) * 10) + Unidades(x - 1))
               Case 5 To 8 : x = ((Decenas(1) * 10) + Unidades(x - 5))
               Case 9 : x = ((Decenas(1) * 10) + Decenas(0))
           End Select

           If (x < 10) Then s(k) = "0" & x.ToString Else s(k) = x.ToString
       Next

       Array.Sort(s)
       Return Join(s, ", ")
   End Function
End Class


Y eso es todo por hoy. Mañana lo completo.
Nota que ya se ha aprovechado para ordenar los elementos de cada combinación así además de estar el listado ordenado, también los valores de cada combinación.


p.d.: Corrijo, con 6 dígitos como decenas, se generan muchas menos firmas, cuando lo calculé ayer, creí haber leído millones... pués nada, mañana se incluye también y se hace el cambio pertienente para contener su clase y en la llamada del botón considerar el caso.

p.d.: Corregido cantidad de combinaciones para 4_2 y 5_1

Serapis

#22
Tengo un momento hoy tras comer, aprovecho para terminarlo...

Antes de nada, es conveniente aclarar que cometí un pequeño error (un '0' donde debí poner una 'O', que como es copia y pega... se tradujo al resto), pero no afecta a lo hecho si no a los que muestro. Esto implica que el numero de combinaciones para los casos de 4-2, 5-1 y 6-0, son mucho menores:
Las combinaciones del caso 4 decenas + 2 unidades son 38.760 y no 143.080
Las combinaciones del caso 5 decenas + 1 unidades son 177.100 y no 402.895
Con el de 6 decenas + 0 unidades (no hay dígitos en el rango 5-9), se dan muchísimos casos ilegales de las 177.100 9 de cada 10 son ilegales... o bien tienen 00, o bien un valor repetido dos veces. Se puedne filtrar con ligeras modificaciones, abajo lo vuelvo a mencionar cuando llegue el momento...

Código (vbnet) [Seleccionar]

Friend Class Permutas3_3
   Implements ICombina

   Const MAX_COMBINAS As UInt32 = 5005
   Private s_Combinas() As String

   Public Sub New()
       Call Enumerar()
   End Sub

   Public ReadOnly Property Cantidad As UInteger Implements ICombina.Cantidad
       Get
           Return MAX_COMBINAS
       End Get
   End Property

   Public Sub Enumerar() Implements ICombina.Enumerar
       s_Combinas = EnumeraComb("ABCDEF", "JKLMNO", MAX_COMBINAS) ' 5005
   End Sub

   ' Alfabeto = XYZ   RST
   '  A   B   C   D   E     F   G   H   I   J     K   L   M   N   O
   '  XY  XZ  XR  XS  XT    YX  YZ  YR  YS  YT    ZX  ZY  ZR  ZS  ZT        
   Public Function Tranformar(ByVal Index As Integer, ByRef Decenas() As Byte, ByRef Unidades() As Byte) As String Implements ICombina.Tranformar
       Dim permuta As String = s_Combinas(Index)
       Dim s(0 To 5) As String
       Dim v As Byte, m As Byte, n As Byte, k As Byte

       For k = 0 To 5
           v = (Strings.Asc((permuta.Chars(k))) - 65)
           m = (Decenas(v \ 5) * 10)
           n = (v Mod 5)

           If (n < 2) Then
               If (v < 5) Then
                   v = (m + Decenas(n + 1))        ' XY, XZ
               ElseIf (v < 10) Then
                   If (n = 0) Then
                       v = (m + Decenas(n))        ' YX
                   Else
                       v = (m + Decenas(n + 1))    ' YZ
                   End If
               Else
                   v = (m + Decenas(n))            ' ZX, ZY
               End If
           Else
               v = (m + Unidades(n - 2))           ' *R,*S,*T
           End If

           If (v < 10) Then s(k) = "0" & v.ToString Else s(k) = v.ToString
       Next

       Array.Sort(s)
       Return Join(s, ", ")
   End Function
End Class

Friend Class Permutas4_2
   Implements ICombina

   Const MAX_COMBINAS As UInt32 = 38760  '143080
   Private s_Combinas() As String

   Public Sub New()
       Call Enumerar()
   End Sub

   Public ReadOnly Property Cantidad As UInteger Implements ICombina.Cantidad
       Get
           Return MAX_COMBINAS
       End Get
   End Property

   Public Sub Enumerar() Implements ICombina.Enumerar
       s_Combinas = EnumeraComb("ABCDEF", "OPQRST", MAX_COMBINAS) ' 38760
   End Sub

   ' Alfabeto = WXYZ   RS
   ' A   B   C   D   E     F   G   H   I   J     K   L   M   N   O     P   Q   R   S   T
   ' WX  WY  WZ  WR  WS    XW  XY  XZ  XR  XS    YW  YX  YZ  YR  YS    ZW  ZX  ZY  ZR  ZS
   Public Function Tranformar(ByVal Index As Integer, ByRef Decenas() As Byte, ByRef Unidades() As Byte) As String Implements ICombina.Tranformar
       Dim permuta As String = s_Combinas(Index)
       Dim s(0 To 5) As String
       Dim v As Byte, m As Byte, n As Byte, k As Byte

       For k = 0 To 5
           v = (Strings.Asc((permuta.Chars(k))) - 65)
           m = (Decenas(v \ 5) * 10)
           n = (v Mod 5)

           If (n < 3) Then
               If (v < 5) Then
                   v = (m + Decenas(n + 1))        ' WX, WY, WZ
               ElseIf (v < 10) Then
                   If (n = 0) Then
                       v = (m + Decenas(n))        ' XW
                   Else
                       v = (m + Decenas(n + 1))    ' XY, XZ
                   End If
               ElseIf (v < 15) Then
                   If (n < 2) Then
                       v = (m + Decenas(n))        ' YW, YX
                   Else
                       v = (m + Decenas(n + 1))    ' YZ
                   End If
               Else
                   v = (m + Decenas(n))            ' ZW, ZX, ZY
               End If
           Else
               v = (m + Unidades(n - 3))           ' *R,*S
           End If

           If (v < 10) Then s(k) = "0" & v.ToString Else s(k) = v.ToString
       Next

       Array.Sort(s)
       Return Join(s, ", ")
   End Function
End Class

Friend Class Permutas5_1
   Implements ICombina

   Const MAX_COMBINAS As UInt32 = 177100  '402895
   Private s_Combinas() As String

   Public Sub New()
       Call Enumerar()
   End Sub

   Public ReadOnly Property Cantidad As UInteger Implements ICombina.Cantidad
       Get
           Return MAX_COMBINAS
       End Get
   End Property

   Public Sub Enumerar() Implements ICombina.Enumerar
       s_Combinas = EnumeraComb("ABCDEF", "TUVWXY", MAX_COMBINAS) ' 177.100
   End Sub

    ' Alfabeto: VWXYZ   R
   ' 01  02  03  04  0?    10  12  13  14  1?    20  21  23  24  2?    30  31  32  34  3?    40  41  42  43  4?
   ' A   B   C   D   E     F   G   H   I   J     K   L   M   N   O     P   Q   R   S   T     U   V   X   Y   Z
   ' VW  VX  VY  VZ  VR    WV  WX  WY  WZ  WR    XV  XW  XY  XZ  XR    YV  YW  YX  YZ  YR    ZV  ZW  ZX  ZY  ZR
   Public Function Tranformar(ByVal Index As Integer, ByRef Decenas() As Byte, ByRef Unidades() As Byte) As String Implements ICombina.Tranformar
       Dim permuta As String = s_Combinas(Index)
       Dim s(0 To 5) As String
       Dim v As Byte, m As Byte, n As Byte, k As Byte

       For k = 0 To 5
           v = (Strings.Asc((permuta.Chars(k))) - 65)
           m = (Decenas(v \ 5) * 10)
           n = (v Mod 5)

           If (n < 4) Then
               If (v < 5) Then
                   v = (m + Decenas(n + 1))        ' VW, VX, VY, VZ
               ElseIf (v < 10) Then
                   If (n = 0) Then
                       v = (m + Decenas(n))        ' WV
                   Else
                       v = (m + Decenas(n + 1))    ' WX, WY, WZ
                   End If
               ElseIf (v < 15) Then
                   If (n < 2) Then
                       v = (m + Decenas(n))        ' XV, XW
                   Else
                       v = (m + Decenas(n + 1))    ' XY, XZ
                   End If
               ElseIf (v < 20) Then
                   If (n < 3) Then
                       v = (m + Decenas(n))        ' YV, YW, YX
                   Else
                       v = (m + Decenas(n + 1))    ' YZ
                   End If
               Else
                   v = (m + Decenas(n))            ' ZV, ZW, ZX, ZY
               End If
           Else
               v = (m + Unidades(n - 4))           ' *R
           End If

           If (v < 10) Then s(k) = "0" & v.ToString Else s(k) = v.ToString
       Next

       Array.Sort(s)
       Return Join(s, ", ")
   End Function
End Class

Friend Class Permutas6_0
   Implements ICombina

   Const MAX_COMBINAS As UInt32 = 177100
   Private s_Combinas() As String

   Public Sub New()
       Call Enumerar()
   End Sub

   Public ReadOnly Property Cantidad As UInteger Implements ICombina.Cantidad
       Get
           Return MAX_COMBINAS
       End Get
   End Property

   Public Sub Enumerar() Implements ICombina.Enumerar
       s_Combinas = EnumeraComb("ABCDEF", "TUVWXY", MAX_COMBINAS)   ' 177.100  
   End Sub

   ' Alfabeto: VWXYZ?   _
    ' 00  01  02  03  04    10  11  12  13  14    20  21  22  23  24    30  31  32  33  34    40  41  42  43  44
    ' A   B   C   D   E     F   G   H   I   J     K   L   M   N   O     P   Q   R   S   T     U   V   W   X   Y
    ' VV  VW  VX  VY  VZ    WV  WW  WX  WY  WZ    XV  XW  XX  XY  XZ    YV  YW  YX  YY  YZ    ZV  ZW  ZX  ZY  ZZ
   Public Function Tranformar(ByVal Index As Integer, ByRef Decenas() As Byte, ByRef Unidades() As Byte) As String Implements ICombina.Tranformar
       Dim permuta As String = s_Combinas(Index)
       Dim s(0 To 6) As String
       Dim v As Byte, m As Byte, n As Byte, k As Byte

       For k = 0 To 5  ' el 0 se reserva (caso especial, para comparar un item con el previo)
           v = (Strings.Asc((permuta.Chars(k))) - 65)
           m = (Decenas(v \ 5) * 10)
           n = (v Mod 5)

           If (m = n) Then
               s(0) = " --- Combinacion ilegal --- "  ' aparece '00'
           End If
           v = (m + n)
           If (v < 10) Then s(k + 1) = "0" & v.ToString Else s(k + 1) = v.ToString
       Next

       Array.Sort(s)
       For k = 1 To 5
           If (s(k) = s(k - 1)) Then
               s(0) = " --- Combinacion ilegal --- " ' un mismo numero aparece 2 veces.
           End If
       Next

       Return Join(s, ", ")
   End Function
End Class


Realmente la clase 6_0, es casi idéntica a la clase 5_1 (la enumeración es la misma), pero la transformación de los datos difiere. Más aún puesto que alredededor de 9 de cada 10 son ilegales, creo que es inecesario, o al menos filtrar los valores ilegales. Esto es bastante sencillo, simplemente basta reducirlo desde 6 decenas y 0 unidades a 5 decenas y 0 unidades.
Habria que modificar ligeramente la función del botón, que ahora exige que haya 6 dígitos, para aceptar el caso de 5 dígitos y si son todos unidades, derivarlo a la nueva clase y si no error. El otro cambio a apalicar sería en la función de enumeración que yace en el módulo... todas las referencias a la cantidad (0-5), debería aceptar un valor sensible al tamaño que se entre, lo cual se toma del parámetro 'Inicio' (por ejemplo), reclamando su longitud -1, es 6-1 para todas excepto para esta que sería 5-1...
Si te interesa avisas y rehago esta última clase para filtrar las combinaciones ilegales... una captura de ejemplo.


Por ultimo, quizás intereses conocer el número de la combinación seleccionada en un momento dado... para cualquier referencia de interés:
Código (vbnet) [Seleccionar]

   Private Sub ListBox1_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles ListBox1.SelectedIndexChanged
       Label4.Text = "Indice: " & ListBox1.SelectedIndex.Tostring
   End Sub

luis456

Gracias Serapis

Como te comentado trabajo fuera toda las semana y para mas hoy me han vacunado contra el covid y me tiene un poco nervioso jejej me vacunaron con la estraneca o astranaca o como se llame  🤣 esperare hasta mañana para probar tus códigos (aunque no he trabajado con las clases aparte , pienso que no será difícil ,recuerda que solo soy aficionado pero me afano ya que me encanta la programación  y de simple como es el titulo del post ( Ejercicio básico de combinaciones )  esto ya es un bicho grande pero yo encantado ya que he visto mas posibilidades de mejorar los cálculos para encima otros sistemas de loterías pero ese es otro tema (para depues jejej)

te aviso
Luis
Que tu sabiduria no sea motivo de Humillacion para los demas

Serapis

Tranquilo... esto no es una red social, donde la gente (parece) exigir que le contesten no tardando más de no se qué cuanto tiempo (so pena de enfadarse). Cada uno a su ritmo...

Hice al final también el filtrado para los de 6_0, de dos maneras distintas...
Una de ellas simplemente ignora añadir al listbox, las entradas ilegales (al final quedna así alrededor de la mitad), el otro modo de fitrado es tratarlo como lo que son solo 5 dígitos, arroja unas 15500 combinaciones, pero no lo veo interesante por que contiene así solo 5 combinaciones (pero quien sabe si un oquisiera aparte poner el 6º manualmente).

Mañana lo comprimo en un zip y lo subo a alguna página de descarga...

luis456

Cita de: Serapis en  8 Mayo 2021, 21:13 PM
Tranquilo... esto no es una red social, donde la gente (parece) exigir que le contesten no tardando más de no se qué cuanto tiempo (so pena de enfadarse). Cada uno a su ritmo...

Hice al final también el filtrado para los de 6_0, de dos maneras distintas...
Una de ellas simplemente ignora añadir al listbox, las entradas ilegales (al final quedna así alrededor de la mitad), el otro modo de fitrado es tratarlo como lo que son solo 5 dígitos, arroja unas 15500 combinaciones, pero no lo veo interesante por que contiene así solo 5 combinaciones (pero quien sabe si un oquisiera aparte poner el 6º manualmente).

Mañana lo comprimo en un zip y lo subo a alguna página de descarga...

Gracias y para esta Hora todavía sigo vivo 😃 ya veremos mañana
saludos
Luis



Que tu sabiduria no sea motivo de Humillacion para los demas

luis456

Hola Serapis

Bueno pase mala noche ya que me dio fiebre esta vacuna y ahora tengo un super catarro :( me he puesto un rato con el programa pero no me aclaro que es lo que tengo que hacer o poner para armarlo pensé me seria fácil jeje .me lo llevo al trabajo a ver si por la noche lo puedo terminar

saludos
Luis
Que tu sabiduria no sea motivo de Humillacion para los demas

Serapis

...'armar' el programa... bueno, se supone que quien pide tiene alguna idea básica de lo que es un proyecto en el lenguaje que trata... y con leves explicaciones debería entenderlo bien. De todos modos, lo he comprimido en su carpeta y lo subo a una página de descargas.

También he añadido la clase para el caso de 5 decenas y 0 unidades, es decir para introducir solo 5 dígitos (los que pueden actuar como decenas): 0,1,2,3,4 y generar sus permutaciones, y la 6ª combinación introducirla manualmente...
El código de esta clase, va al final del fichero 'form1.vb' y por supuesto, tras incluirlo, s eprecisa crear una instancia para invocarlo, etc... (mira el código del proyecto que adjunto, si tienes dudas)
Código (vbnet) [Seleccionar]

Friend Class Permutas5_0
    Implements ICombina

    Const MAX_COMBINAS As UInt32 = 15504
    Private s_Combinas() As String

    Public Sub New()
        Call Enumerar()
    End Sub

    Public ReadOnly Property Cantidad As UInteger Implements ICombina.Cantidad
        Get
            Return MAX_COMBINAS
        End Get
    End Property

    Public Sub Enumerar() Implements ICombina.Enumerar
        s_Combinas = EnumeraComb("ABCDE", "PQRST", MAX_COMBINAS)   ' 15504
    End Sub

    ' Alfabeto: VWXYZ
    ' 01  02  03  04    10  12  13  14    20  21  23  24    30  31  32  34    40  41  42  43
    ' A   B   C   D     E   F   G   H     I   J   K   L     M   N   O   P     Q   R   S   T
    ' VW  VX  VY  VZ    WV  WX  WY  WZ    XV  XW  XY  XZ    YV  YW  YX  YZ    ZV  ZW  ZX  ZY
    Public Function Tranformar(ByVal Index As Integer, ByRef Decenas() As Byte, ByRef Unidades() As Byte) As String Implements ICombina.Tranformar
        Dim permuta As String = s_Combinas(Index)
        Dim s(0 To 4) As String
        Dim v As Byte, m As Byte, n As Byte, k As Byte

        For k = 0 To 4
            v = (Strings.Asc((permuta.Chars(k))) - 65)
            m = (Decenas(v \ 4) * 10)
            n = (v Mod 4)

            If (v < 4) Then
                v = (m + Decenas(n + 1))        ' VW,VX, VY, VZ
            ElseIf (v < 8) Then
                If (n = 0) Then
                    v = (m + Decenas(n))        ' WV
                Else
                    v = (m + Decenas(n + 1))    ' WX, WY, WZ
                End If
            ElseIf (v < 12) Then
                If (n < 2) Then
                    v = (m + Decenas(n))        ' XV, XW
                Else
                    v = (m + Decenas(n + 1))    ' XY, XZ
                End If
            ElseIf (v < 16) Then
                If (n < 3) Then
                    v = (m + Decenas(n))        ' YV, YW, YX
                Else
                    v = (m + Decenas(n + 1))    ' YZ
                End If
            Else
                v = (m + Decenas(n))            ' ZV, ZW, ZX, ZY
            End If

            If (v < 10) Then s(k) = "0" & v.ToString Else s(k) = v.ToString
        Next

        Array.Sort(s)
        Return Join(s, ", ")
    End Function
End Class


Nota que puede ser optimizado para ganar en velocidad de cálculo a cambio de complicar un poco más el algoritmo yacente en la función 'Transformar'.
Dado que una combinación y la siguiente suele variar la mayor parte de las veces solo 1 valor de los 6 que tiene la combinación, retener el array 's' (hacerlo estático) y calcular solo el valor o valores que cambian. Pero en lo que a mí respecta te lo dejo así, queda a tu esfuerzo optimizarlo si es de tu interés y preferencia. En general (no siempre) cualquier optimización aplicada tiende a oscurecer el mecanismo del algoritmo, lo que dificulta su entendimiento.

Descarga del proyecto: https://workupload.com/file/Nu6FVTcfH2g 15'11Kb.
Cuando lo descarges y abras verás en cada fichero el código que adjunta.
Los 3 ficheros principales son: Module1.vb, Interface1.vb y Form1.vb que son los que contienen el código y por tanto los que tienes que mirar (o copy-paste). El proyecto exige otros más para poder arrancar en el IDE de VS.
(adjunté, la operatoria de 'reset', que explicaba mensajes más arriba, pero igualmente puedes ignorarlo al menos de momento).

luis456

 ;-) ;-) ;-) ;-)

Perfecto Serapis funciona de maravilla te has brillado con este programa, aunque claro es inasumible apostar tal cantidad de apuestas ,he echo algunas apuestas "Virtuales " y si las hubiéramos jugados ya seriamos millonarios  :silbar:  pero no es motivo de desesperanza ya que es cuestión de aplicar variados filtros, algo de estadística y otros métodos personales  (aunque esta comprobado que las estadísticas para las loterías no sirven, porque será ??? )

Bien ahora es cuestión de hacer un menú ,guardar apuestas generadas por ejemplo para poder comparar resultados y aciertos con el sorteo (para no hacerlo a mano para eso esta la programación jejje) quien habrá dicho eso  :rolleyes: algunos filtros de reducción de apuestas etc ete.

Bueno a pesar de que todavía ando con los efectos de la vacuna (me callo fatal esa *****) ya creo que no muero y pienso que este programa vale la pena hacerlo muy completo y puede ser un buen programa y muy eficaz solo es cuestión de ideas y de cálculos .

Para el dia de hoy este post lo han visto 4,360 veces me imagino que hay expectativas sobre el desarrollo del programa jejje solo que no veo aportes e ideas.

Bueno seguiré probándolo y vemos que tal

De nuevo Gracias Serapis

ya te contare

Luis








Que tu sabiduria no sea motivo de Humillacion para los demas

Serapis

#29
La bonoloto, no se presta a muchas estadísticas, por la enorme cantidad de permutaciones que tolera, y como solo se juega una vez a la semana... pués aunque reúnas los sorteos de 20 años, no creo que dé para sacar conclusiones.

En los casinos (por ejemplo), pequeños fallos en la fabricación o montaje de mesas de apuestas, y la puesta en escena de otros juegos, pueden manifestar 'debilidades', precisamente porque se juegan mas apuestas en un solo día que la bonoloto en todo un año...

Si hay 'debilidades' en el sistema, puede ser atacado matemáticamente.

Mi cuñado solía jugar a la bonoloto, yo nunca he jugado, por lo que aunque conozco el juego, no los detalles de las reglas. En tú eliges 6 números y deben cincidir todos, entonces jugar alguna número más, reduce las posibilidades combinatorias (aumentan la sposibilidades de acertar), porque siguen bastando 6 combinaciones en una apuesta de 7 números, pero como no me sé las reglas, y por ello no sé si es posible apostar 7 números y si en tal caso (el precio de tal apuesta) equivale al número de apuestas que representan... y si por tanto su coste es equivalente a las apuestas que representan, si no es así, cuantos más números se apostasen aumentarían las posibilidades (de acierto) en menor cantía económica a las (posibilidades de) reducciones de permutaciones resultantes... pero, inclsuo así, es probable que el coste por apuesta unitaria impida señalar números grandes de apuestas, pués el coste total pueda llegar a ser prohibitivo par ale bolsillo.

El número de combinaciones de la bonoloto (para apuestas que exigen 6 combinacones elegibles) es de: 49 * 48 * 47 * 46 * 45 * 44, ahora bien ahí no se filtra las combinaciones que resultan de la misma pero en diferente orden, luego queda dividirla entre 1 * 2 * 3 * 4 * 5 * 6, que supone divir esas más 10 mil millonres 720 veces, lo que siguen dando una cifra elevada pero asequible de 'mantener' a raya.

Yo he sido siempre más de quinielas de fútbol... siendo resultado de una 'actividad humana', las estadisticas son más fáctibles de reflejar la realidad de los equipos (siempre que ellos mismos no apuesten y por ello falsifiquen resultados, seguro que te acuerdas que se ha dado más de un caso incluso con detenciones por delitos de este tipo, no solo en España, también en otros países)...

De todos modos, lo que a mi me apasiona de este tipo de juegos son las matemáticas tras ellos.
Tengo una intuición en la quiniela que aún no me he demostrado (a favor o en contra), que más o menos resumo así: "elegidas 2187 apuestas específicas (no cualesquiera, evidentemente), garantizan siempre un acierto de 14 y varios de 13, amén de los de 12 y 11...". La demostración por exhaustación es inviable en tiempo computacional, así que requiere mucho esfuerzo de cálculo para reducir por combinatoria la búsqueda de tales apuestas (algún día me pondré con ello  :silbar: :silbar: :silbar:).

Para cualquier interesado en la combinatoria, deben verlo como un sistema que autocontiene o da soporte a una infinitud de bases numéricas con condiciones más o menos 'caprichosas'. Pero que, conocidas tales condiciones uno puede enumerar correlativamente en la base decimal (1,2,3,4,5,6...) para referirse al número enésimo en dicha base numérica caprichosa, vamos como cualquier otra base numérica, y por tanto poder elaborar una fórmula para tomando la enésima combinación requerida (el enésimo número en esa base numérica), obtener el equivalente con los datos que uno pretende que le represente... vamos una abstracción igual que usamos los números para contar monedas, personas, y todo en general. Es justamente la forma en que los algoritmos están representados en este proyecto, donde cada uno (por ser condionantes distintos), mantienen un alfabeto propio, al ser representados por bases numéricas distintas. Un condicionante es la no repetición de elementos, algo deseable a filtrar siempre que sea suficiente mantener un sola copia de dicho elemento, de otro modo el número de permutaciones se dispara...

Baste ver la difrencia de combinaciones totales de la bonoloto, 10.068.347.520 frente a las mismas sin repetición: 13.983.816, o dicho de otra manera dada una combinación cualquiera, por ejemplo: 05, 11, 18, 25, 37, 41, se pueden reordenar de 720 maneras distintas, pero de cara a la bonoloto, son la misma apuesta que si alguien decide apostar: 37, 18, 25, 41, 05, 11.
Merece la pena crear un fichero con todas esas combinaciones de casi 14millones de apuestas distintas?. La respuesta se autoresponde sola: Solo si uno no sabe luego traducir de la base numérica 13983816 a la combinación específica enumerada, supongamos que la combinación antedicha fuera la 3.100.000ª combinación de esa base numérica, es decir,

string = funcion GetCombinacion(entero32 Index)
   string Combinacion

  return Bonoloto(13983816, index)
fin funcion

entero32 index = 3100000
string comb = GetCombinacion(index)
imprimir "El resultado es: " + comb
//Salida:
"El resultado es: 05, 11, 18, 25, 37, 41"

Por lo que, si uno sabe traducir un índice de una base numérica en el valor deseado, no es necesario generar un fichero con todas las combinaciones posibles (lo mismo que con las contraseñas, que es 'mas de los mismo', bases numéricas con condicionantes más o menos capichosos), es suficiente saber el número de combinaciones que se generan. Aunque normalemente tener las combinaciones totales, puede acelerar el cálculo cuando se trata de tomar muchas combinaciones contiguas, o cambia el modo de representación de la salida (en el ppppprograma cambian los dígitos a jugar, peor mantiene una combinación equivalente, es decir son 'instancias', especifidades d ela abstracción representada en la enumeración que es a fin de cuentas la base numérica subyacente), aunque para ello deben estar en una codificación intermedia (a medio hacer entre la enumeración consecutiva y el objetivo final), de otro modo tener un fichero con: 0, 1,2,3,4,5... 10002, 10003, 10004... 13983813, 13983814, y 13983815. Pués resulta obvio que es ridículo basta conocer que esi el numero máximo de combinaciones son 13983816, el rango quedará definido entre 0 y 13983816-1, luego puede mantenerse en un programa como constante... que es lo que se hace en el programa.

Saludos.