Programación General > Visual Basic para principiantes

 Numeros perfecto

<< < (2/2)

m0skit0:
Jimbenit, me vas a perdonar, pero tu función EsPrimo está mal codificada. ¿Para qué quieres seguir buscando "submúltiplos" (supongo que querrás decir divisores) si al encontrar uno ya sabemos que el número no es primo? ¿Además, para qué conviertes el número en Integer cuando lo aceptas como Long en el parámetro?


--- Código: Visual Basic ---Private Function EsPrimo(ByVal N as Long) As Boolean    Dim i As Long    Dim Mitad As Long        EsPrimo = True    i = 2    Mitad = N/2    Do While (i <= Mitad) And EsPrimo        If (N mod i = 0) Then            EsPrimo = False        Else            i = i + 1        End If    LoopEnd Function Además, en tu función Nro_Primos creas un vector con tamaño Lim_Sup, cuando los primos son evidentemente muchos menos que Lim_Sup. Así desperdicias mucha memoria y eso es una mala práctica. Tendrías que poner:


--- Código: Visual Basic ---If EsPrimo(i) Then    Contador = Contador + 1    ReDim Preserve Vector(Contador - 1)        Vector(Contador - 1) = i    Texto = Texto & i & vbCrLfEnd If Saludos y  :comp:

Jimbenit:

--- Cita de: "m0skit0" ---Jimbenit, me vas a perdonar, pero tu función EsPrimo está mal codificada. ¿Para qué quieres seguir buscando "submúltiplos" (supongo que querrás decir divisores) si al encontrar uno ya sabemos que el número no es primo? ¿Además, para qué conviertes el número en Integer cuando lo aceptas como Long en el parámetro?


--- Código: Visual Basic ---Private Function EsPrimo(ByVal N as Long) As Boolean    Dim i As Long    Dim Mitad As Long        EsPrimo = True    i = 2    Mitad = N/2    Do While (i <= Mitad) And EsPrimo        If (N mod i = 0) Then            EsPrimo = False        Else            i = i + 1        End If    LoopEnd Function Además, en tu función Nro_Primos creas un vector con tamaño Lim_Sup, cuando los primos son evidentemente muchos menos que Lim_Sup. Así desperdicias mucha memoria y eso es una mala práctica. Tendrías que poner:


--- Código: Visual Basic ---If EsPrimo(i) Then    Contador = Contador + 1    ReDim Preserve Vector(Contador - 1)        Vector(Contador - 1) = i    Texto = Texto & i & vbCrLfEnd If Saludos y  :comp:
--- Fin de la cita ---
Si, tienes razón...
 :lol:  :lol:  :lol: Veo que mi respuesta fue un poco a la ligera....  :lol:  :lol:  :lol:  
Mi respuesta fue solo para dar una idea...
A la próxima lo pienso un poquito mas...
Saludos Mosquito...

STROKERT:
PORFAS AYUDA CON ESTE ALGORITMO!
CHEQUENLO SI ESTA BIEN Y ME DICEN EN DONDE ESTA MAL PORFAS!
 Diseñar un algoritmo que me permita calcular los 5 primeros números perfecto (un número es perfecto, cuando la suma de sus divisores, sin incluirlo al número es exactamente el mismo número). El 6 es un número perfecto ya que sus divisores son 1,2 y 3 suman 6. La solución hace que el computador tome un buen tiempo para completar los 5 números.

CLS

Num = 5

C = 0

DO WHILE C < 5

        Num = Num + 1

        Suma = 0

               FOR i = 1 TO INT(Num / 2)

                       IF Num MOD i = 0 THEN

                              Suma = Suma + i

                       END IF

               NEXT

               IF Num = Suma THEN

                       C = C + 1

                       PRINT "El Numero", Num

               END IF

LOOP

Nebire:
El algoritmo es totalmente correcto. Adolece simplemente de que es lento.
Es lento porque le pides que calcule todos los divisores posibles para un número contando desde 1 hasta la mitad del número a calcular...

Ejecutando el código obtengo estos valores:


--- Código: Text --- Nº Perfecto ______Timer_______iteraciones (valor absoluto)6             48265.89           328            48265.89           190 496           48266          614988128          48401.82       16516090 Como ves a cada hallazgo el número de iteraciones crece exponencialmente. El siguiente número es  33550336, si en mi ordenador quickbasic ha tardado 35 segundos para hallar el 8128, para hallar el 33.550.336 a 'grosso modo' podría tardar 6 días.

Este ejercicio te lo ponen no para probar que eres capaz de encontrar una solución, sino para probar cuan eficaz eres en encontrar una solución. Es un ejercicio excelente para demostrar que la 'fuerza bruta' resulta ineficaz o por lo menos te cuestiona sobre si debe o no buscarse un método alternativo más eficaz.

La solución de este algoritmo ya la expliqué más arriba. La eficacia del mismo se reduce a con qué velocidad podemos hallar números primos. sin embargo para qué hallar números primos cuando ya se conocen tablas ?.

Ahora bien si parte del problema lo escalcular los primos, no te dejarán usar una tabla de primos desde fichero. Por tanto, lo ideal será dividir el algoritmo en 2 partes.

En la 1ª parte (que más adelante lo sugerimos como la 3ºfunción) se debería calcular una tabla de primos lo suficientemente grande como para 'alcanzar' al 5º número  buscado. Como a priori no  sabríamos cual es ese valor, lo adecuado sería calcular primos en rondas condicionales según se vayan necesitando, los primos hallados se deberían meter en una matriz. por ejemplo calculamos los primos existentes hasta el número solicitado, luego localizamos si el 5º número perfecto se puede hallar en ese rango, si no se encuentra buscamos primos hasta la siguiente tanda, se añaden a una matriz, nuevamente verificamos si el 5º númeroperfecto se sencuentra en la matriz ... repetimos el cilcoo hasta que se encuentre. Por supuesto puedes alterar el código sugerido para que por ejemplo busque cada 1000 primos ú otro sistema que prefieras... yo en ese sentido sólo te ofrezco una sugerencia de solución.

en seudocódigo sería algo como...
Previamente decidimos dividir la búsqueda de primos 'sobre la marcha', para ello necesitamos algunas variables que nos permitan controlar estos condicionantes:


--- Código: Text ---Primos() ' Matriz de alojamiento de los primos halladosPrimos(0 to 999) ' Redimensionar la matriz cada 1000 hallados, la matriz es dinámica y acabamos de darle 1000 elementosNPrimos ' contiene números primos hallados, aunque la matriz tenga 1000 elementos nPrimos indica cuantos elementos realmente están ocupados  BuscarnumerosPerfectos(Cuantos) no devuelve nada    Primos(0)=1    Primos(1)=2     NPrimos =2     iniciar bucle BuscarPerfectos 2, cuantos         candidato= (2^BuscarPerfectos ) -1        si EsPrimoBuscarEnMatriz(candidato)=true luego                NumPerfecto= (2^ (BuscarPerfectos -1)) * candidato                print NumPerfecto,        fin si    fin Bucle fin BuscarnumerosPerfectos   EsPrimoBuscarEnMatriz(Candidato) tipo buleano   PrimoMayorEnMatriz  =  matriz( NPrimos-1)   hacer mientras PrimoMayorEnMatriz < candidato        llamar a la función BuscarPrimos(LimiteMayorActualDeLaMatriz, int(SQR(candidato)))        PrimoMayorEnMatriz  =  matriz( NPrimos-1)    repetir         iniciar bucle LocalizarPrimoEnMatriz 2, LimiteMayorActualDeLaMatriz           si candidato= primos(LocalizarPrimoEnMatriz) luego                     devolver EsPrimoBuscarEnMatriz=true                    salir de la función           fin si     fin bucle     ' si llega aquí devolver falsefin función EsPrimoBuscarEnMatriz  BuscarPrimos(desde, hasta)   si desde es par, le añadimos 1, porque excepto el 2 el resto de primos es impar.   iniciar bucle Recorrido desde, hasta en pasos de 2         n=Recorrido         EsPrimo = true  '         ' buscar divisores, un búmero no es primos si tiene divisores diferentes de 1 y sí mismo        iniciar bucle CalcularSiNEsPrimo:  2, int(sqr(n))              si n mod CalcularSiNEsPrimo = 0 es divisor luego EsPrimo=false, salir del bucle          siguiente en el bucle        si EsPrimo = true luego meterlo en la matriz              nPrimos= nPrimos +1           Primos(nPrimos)= n       fin si             si matriz esta llena redimensionamos conservando contenido, en otros 1000 elementos más   fin bucle recorridofin función BuscarPrimos 
Comentando el código...

1 - entramos en una función, o sub, (si no sabes tratar funciones y subs en QuickBasic (o QBasic) trátalo gono una etiqueta... al final te aclaro esto) que va a localizar los números perfectos deseados. Para ello invoca a una función que determina si un número candidato a número perfecto es primo, si dicho número es primo, hay un número que se deduce como número perfecto partiendo de dicho candidato.

2 - La función es primo, primero verifica si la matriz de primos tiene contenido (de entrada sólo fijamos 2 primos en la rutina anterior (1 y 2)) si el último primo es menor que el candidato significa que puede no bastarnos con los primos que tenemos en la matriz actual para determinar si el candidato es primo, por tanto llamamos a la función que comentaremos después para localizar más primos y meterlos en la matriz. Después lo único que hacemos es recorrer la matriz y ver si el candidato está en la matriz, si está es primo si no no. Este resultado es lo que devuelve esta función.

3 - La 3ª función localiza primos, a partir del valor indicado (el último que tenemos almacenado en la matriz) hasta la raíz  cuadrada del  candidato actual, cada vez que se halla uno se añade a la matriz y se totaliza la cantidadde primos hallados. Si la matriz  se llena se redimensiona con otros 1000 elementos más (preservando su contenido), esto se hace 1 vez con 1000 elementos no tras cada primo hallado que sería muy lento.

Finalmente, si no sabes manejar funciones y rutinas en QuickBasic-QBasic siempre se puede 'simular' con etiquetas y saltos entre ellas. He aquí un ejemplo sencillo:

--- Código: Visual Basic ---  Perfectos:   ' esto es una etiqueta' se coloca aquí el código de la 1ª función ' donde se señala: si EsPrimoBuscarEnMatriz(candidato)=true luego , hacemos previamente un saltogosub EsPrimoBuscarEnMatriz ' y luego cambiamos dicha línea por:si EsPrimo=true luego' resto del código End  ' el programa termina al llegar aquí ' esto es otra etiqueta, se puede saltar aquí con gosub nombreetiquetaEsPrimoBuscarEnMatriz :     ' aquí se coloca el código de la 2ª función      ' donde pone: llamar a la función BuscarPrimos(....) ' previamente ponemos estas líneas    Desde = LimiteMayorActualDeLaMatriz    Hasta = int(SQR(candidato)))     ' luego sustituímos dicha línea de llamada por esta otra      gosub  BuscarPrimos      'resto del código Return BuscarPrimos :   ' código de la 3ª funciónReturn  Una etiqueta, es como un punto de entrada a una función, y 'Return' es como el punto de salida que devuelve el control a la línea siguiente a la que invocó la etiqueta, para tratarse como una rutina debe usarse gosub, goto produce un salto incondicional sin esperar retorno, porlo que aparecería un error si se salta a una etiqueta usando goto y se encuentra un return diciendo algo como (Return sin Gosub ???)...

Ya depende de tu pericia del lenguaje que estimo no va muy allá... pero bueno progresa y ya nos contarás donde te estancas...

p.d.: en quickbasic para trabajar con matrices dinámicas (que superen los 64Kb), es necesario arrancarlo con los siguientes parámetros en la línea de comando: Unidad:qb45QB.EXE /ah
Donde unidad es la letra del disco donde está la carpeta qb45 , nota el parámetro "/ah"

Navegación

[0] Índice de Mensajes

[*] Página Anterior

Ir a la versión completa