6 febrero, 2025

GENERAR NÚMEROS PRIMOS CON VBA USANDO UN ARRAY

Hola a todos:

Hace tiempo que no publicaba en el blog, he estado muy entretenido escribiendo publicaciones en LinkedIn, pero ya iba siendo hora de hacerlo en mi web.

Y no todo va a ser Power Apps, Automate o Typescript, hoy me ha apetecido escribir sobre el cálculo de números primos y, especialmente, sobre la idea de usar un array para almacenar dichos cálculos de forma más eficiente en Excel VBA.

¿Por qué usar un array para almacenar los números primos?

Cuando escribimos datos celda por celda en Excel (por ejemplo, en un bucle For i = 1 To 1000000), estamos haciendo cientos o miles de llamadas a la API interna de Excel. Esto puede ralentizar muchísimo la ejecución.
En cambio, si acumulamos los datos primero en un array en memoria (que es muy rápido de manejar dentro de VBA) y después volcamos todo el contenido de una sola vez a la hoja, logramos una mejora sustancial en el rendimiento.

¿Cómo funciona el proceso?

  1. Pedimos parámetros al usuario: A través de un InputBox, solicitamos dos números separados por coma, por ejemplo 50,600.
    • El primero (LargoColumna) indica cuántos primos queremos por cada columna.
    • El segundo (Limite) es hasta qué número calcularemos los primos.
  2. Buscamos y guardamos los primos:
    • Recorremos todos los enteros desde 2 hasta Limite.
    • Comprobamos si cada número es primo (usando divisiones hasta la raíz cuadrada).
    • Cada vez que encontramos un primo, lo agregamos a un array dinámico llamado ListaPrimos().
  3. Reorganizamos el resultado en un array bidimensional:
    • Una vez que tenemos todos los primos en un array unidimensional, calculamos cuántas columnas necesitaremos para distribuirlos en bloques de LargoColumna filas.
    • Luego volcamos estos datos a un array bidimensional (FinalArray), que reflejará el formato que queramos en la hoja (con las filas y columnas adecuadas).
  4. Asignamos el array bidimensional a la hoja:
    • Por último, indicamos el rango (por ejemplo, A1 con un ancho y alto en función de nuestros datos) y lo rellenamos de golpe con FinalArray.
    • Esta acción reduce drásticamente el tiempo de ejecución comparado con el enfoque de ir escribiendo celda por celda en un bucle.

Ejemplo de código

Aquí os dejo el código comentado:

Option Explicit

Sub NumerosPrimosConArray()

    Dim ArrParams As Variant
    Dim LargoColumna As Long
    Dim Limite As Long
    Dim ListaPrimos() As Long
    Dim CountPrimos As Long
    Dim i As Long
    Dim j As Long
    Dim Raiz As Double
    Dim EsPrimo As Boolean
    Dim TotalCols As Long
    Dim TotalRows As Long
    Dim FinalArray() As Variant
    Dim cont As Long
    Dim r As Long
    Dim c As Long
    Dim RangoDestino As Range
    
    ArrParams = Split(InputBox("Introduce LargoColumna,Limite (p.e. 50,600)"), ",")
    If UBound(ArrParams) < 1 Then
        MsgBox "Parámetros no válidos."
        Exit Sub
    End If
    
    'Validamos
    On Error GoTo ErrorHandler
    LargoColumna = CLng(ArrParams(0))
    Limite = CLng(ArrParams(1))
    On Error GoTo 0
    
    If LargoColumna < 1 Or Limite < 2 Then
        GoTo ErrorHandler
    End If
    
    'Limpiamos hoja
    ActiveSheet.Cells.Clear
    
    'Generamos primos y almacenamos en array
    ReDim ListaPrimos(1 To 1)
    CountPrimos = 0
    
    For i = 2 To Limite
        EsPrimo = True
        
        If i = 2 Then
            ' 2 es primo
        ElseIf i Mod 2 = 0 Then
            EsPrimo = False
        Else
            Raiz = Sqr(i)
            For j = 3 To Int(Raiz) Step 2
                If i Mod j = 0 Then
                    EsPrimo = False
                    Exit For
                End If
            Next j
        End If
        
        If EsPrimo Then
            CountPrimos = CountPrimos + 1
            If CountPrimos > UBound(ListaPrimos) Then
                ReDim Preserve ListaPrimos(1 To CountPrimos)
            End If
            ListaPrimos(CountPrimos) = i
        End If
    Next i
    
    'Controlamos columnas totales a generar
    TotalCols = Application.WorksheetFunction.Ceiling(CountPrimos / LargoColumna, 1)
    TotalRows = LargoColumna
    
    'Creamos array final
    ReDim FinalArray(1 To TotalRows, 1 To TotalCols)
    
    cont = 1
    For c = 1 To TotalCols
        For r = 1 To LargoColumna
            If cont <= CountPrimos Then
                FinalArray(r, c) = ListaPrimos(cont)
                cont = cont + 1
            Else
                Exit For
            End If
        Next r
    Next c
    
    'Volcamos el array a la hoja
    Set RangoDestino = ActiveSheet.Range("A1").Resize(TotalRows, TotalCols)
    RangoDestino.Value = FinalArray
    
    'Mensaje final
    MsgBox "Proceso terminado. Se encontraron " & CountPrimos & " primos hasta " & Limite & ".", vbInformation
    Exit Sub

'Gestión de errores
ErrorHandler:
    MsgBox "Datos inválidos o proceso cancelado. Asegúrate de escribir algo como 50,600", vbExclamation
End Sub

Comparte este post

Si te ha gustado o tienes alguna duda, puedes dejar aquí tu comentario.

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información.plugin cookies

ACEPTAR
Aviso de cookies