sub examples define create excel vba excel-vba user-defined-functions

excel - examples - VBA: prueba de cubos perfectos



public function vba excel (4)

Intento escribir una función simple en VBA que pruebe un valor real y genere un resultado de cadena si es un cubo perfecto. Aquí está mi código:

Function PerfectCubeTest(x as Double) If (x) ^ (1 / 3) = Int(x) Then PerfectCubeTest = "Perfect" Else PerfectCubeTest = "Flawed" End If End Function

Como puede ver, estoy usando una instrucción if simple para probar si la raíz cúbica de un valor es igual a su parte entera (es decir, no hay residuo). Intenté probar la función con algunos cubos perfectos (1, 8, 27, 64, 125), pero solo funciona para el número 1. Cualquier otro valor escupe el caso "defectuoso". ¿Alguna idea de lo que está mal aquí?


@ScottCraner explica correctamente por qué obtuviste resultados incorrectos, pero hay algunas otras cosas que señalar aquí. Primero, supongo que está recibiendo un Double como entrada porque el rango de números aceptables es más alto. Sin embargo, según su definición implícita de un cubo perfecto, solo se deben evaluar los números con una raíz cúbica entera (es decir, excluiría 3.375). Solo probaría esto por adelantado para permitir una salida anticipada.

El siguiente problema con el que te encuentras es que 1/3 no se puede representar exactamente con un Double . Como estás elevando a la potencia inversa para obtener tu raíz cúbica, también estás agravando el error de coma flotante. Hay una manera realmente fácil de evitar esto: tomar la raíz del cubo, hacer un cubo y ver si coincide con la entrada. Obtienes el resto de los errores de coma flotante volviendo a tu definición de cubo perfecto como un valor entero; simplemente redondea la raíz del cubo al entero siguiente más alto y al siguiente entero más bajo antes de volver a formar el cubo:

Public Function IsPerfectCube(test As Double) As Boolean ''By your definition, no non-integer can be a perfect cube. Dim rounded As Double rounded = Fix(test) If rounded <> test Then Exit Function Dim cubeRoot As Double cubeRoot = rounded ^ (1 / 3) ''Round both ways, then test the cube for equity. If Fix(cubeRoot) ^ 3 = rounded Then IsPerfectCube = True ElseIf (Fix(cubeRoot) + 1) ^ 3 = rounded Then IsPerfectCube = True End If End Function

Esto arrojó el resultado correcto hasta 1E + 27 (mil millones de cubos) cuando lo probé. Dejé de ir más alto en ese momento porque la prueba tardaba tanto en ejecutarse y, en ese momento, probablemente estés fuera del rango que razonablemente necesitarías para ser exacto.


Está probando si el cubo es igual al doble suministrado.

Entonces para 8 estarías probando si 2 = 8.

EDITAR: También encontró un problema de coma flotante. Para resolver, redondearemos los decimales un poco para tratar de superar el problema.

Cambie a lo siguiente:

Function PerfectCubeTest(x As Double) If Round((x) ^ (1 / 3), 10) = Round((x) ^ (1 / 3), 0) Then PerfectCubeTest = "Perfect" Else PerfectCubeTest = "Flawed" End If End Function

O (gracias a Ron)

Function PerfectCubeTest(x As Double) If CDec(x ^ (1 / 3)) = Int(CDec(x ^ (1 / 3))) Then PerfectCubeTest = "Perfect" Else PerfectCubeTest = "Flawed" End If End Function


Para divertirse, aquí hay una implementación de un método basado en la teoría de números que se describe aquí . Define una función de valor booleano (en lugar de valor de cadena) llamada PerfectCube() que prueba si una entrada entera (representada como Long) es un cubo perfecto. Primero ejecuta una prueba rápida que arroja muchos números. Si la prueba rápida no puede clasificarlo, invoca un método basado en factoraje. Factoriza el número y comprueba si la multiplicidad de cada factor primo es un múltiplo de 3. Probablemente podría optimizar esta etapa sin molestarme en encontrar la factorización completa cuando se encuentra un factor malo, pero ya tenía un algoritmo de factorización VBA:

Function DigitalRoot(n As Long) As Long ''assumes that n >= 0 Dim sum As Long, digits As String, i As Long If n < 10 Then DigitalRoot = n Exit Function Else digits = Trim(Str(n)) For i = 1 To Len(digits) sum = sum + Mid(digits, i, 1) Next i DigitalRoot = DigitalRoot(sum) End If End Function Sub HelperFactor(ByVal n As Long, ByVal p As Long, factors As Collection) ''Takes a passed collection and adds to it an array of the form ''(q,k) where q >= p is the smallest prime divisor of n ''p is assumed to be odd ''The function is called in such a way that ''the first divisor found is automatically prime Dim q As Long, k As Long q = p Do While q <= Sqr(n) If n Mod q = 0 Then k = 1 Do While n Mod q ^ k = 0 k = k + 1 Loop k = k - 1 ''went 1 step too far factors.Add Array(q, k) n = n / q ^ k If n > 1 Then HelperFactor n, q + 2, factors Exit Sub End If q = q + 2 Loop ''if we get here then n is prime - add it as a factor factors.Add Array(n, 1) End Sub Function factor(ByVal n As Long) As Collection Dim factors As New Collection Dim k As Long Do While n Mod 2 ^ k = 0 k = k + 1 Loop k = k - 1 If k > 0 Then n = n / 2 ^ k factors.Add Array(2, k) End If If n > 1 Then HelperFactor n, 3, factors Set factor = factors End Function Function PerfectCubeByFactors(n As Long) As Boolean Dim factors As Collection Dim f As Variant Set factors = factor(n) For Each f In factors If f(1) Mod 3 > 0 Then PerfectCubeByFactors = False Exit Function End If Next f ''if we get here: PerfectCubeByFactors = True End Function Function PerfectCube(n As Long) As Boolean Dim d As Long d = DigitalRoot(n) If d = 0 Or d = 1 Or d = 8 Or d = 9 Then PerfectCube = PerfectCubeByFactors(n) Else PerfectCube = False End If End Function


Se corrigió el error de división de enteros gracias a @Comintern. Parece ser correcto hasta 208064 ^ 3 - 2

Function isPerfectCube(n As Double) As Boolean n = Abs(n) isPerfectCube = n = Int(n ^ (1 / 3) - (n > 27)) ^ 3 End Function