Codigo fuente de la funcion pow de math.h?

Iniciado por prometheus48, 28 Enero 2012, 14:36 PM

0 Miembros y 2 Visitantes están viendo este tema.

prometheus48

Hola,

Me gustaria hacer un programa que hiciera eso, elevar un numero base a una potencia, pero sin utilizar un bucle o la libreria math.h .

¿Alguien tiene el source code de la funcion pow?

¿O alguien sabe como hacerlo?

Salu2!
"Si tú tienes una manzana, y yo otra, y las intercambiamos, tu sigues teniendo una manzana, y yo sigo teniendo una manzana.
Pero, si tu tienes una idea, y yo otra, y nos las intercambiamos, tu tienes dos ideas, y yo tengo dos ideas"
The knowledge is free

eleon

#1
No sé cuál será el código en la librería "math.h" pero es tan sencillo como un bucle:

Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

int main()
{
int x1, exponente, resultado;

cout << "Base: ";
cin >> x1;
cout << endl << "Exponente: ";
cin >> exponente;

resultado = x1;

if (exponente = 0) resultado = 1;
else if (exponente = 1) resultado = x1;
else {
for (int i = 1; i < exponente; i++)
{
resultado *= x1;
}

if (exponente < 0) resultado = 1 / resultado; //Si el exponente es negativo...

cout << endl << "Resultado: " << resultado;

return 0;
}
}


Ahora mismo no tengo compilador para probarlo, pero creo que no tiene ningún error. Saludos.

Edito: Vale, me doy cuenta de que faltaría modificar el matiz para un exponente negativo, voy a ver como sería y comento.

Edito 2: Corregido, si el exponente es negativo basta con dividir 1 entre el resultado.

Akai

#2
@eleon: un pequeño apunte. A parte de utilizar enteros con lo cual dividir 1 entre el resultado de la potencia te va a dar un 0 como una casa, aportas un código que lo hace utilizando un bucle, cosa que prometheus48 intentaba evitar.


@prometheus48:

El source code de la función pow lo encontrarás entre el código fuente del compilador que utilices.
Por otro lado, suena a ejercicio de clase o algo por el estilo. Para qué querrías hacer una potencia sin utilizar un bucle?
Si eso mírate recursividad.

Xandrete

Cita de: eleon en 28 Enero 2012, 20:07 PM
Edito: Vale, me doy cuenta de que faltaría modificar el matiz para un exponente negativo, voy a ver como sería y comento.

Edito 2: Corregido, si el exponente es negativo basta con dividir 1 entre el resultado.

Y te faltaría también el caso general de un exponente real. Lo cierto es que no me voy a currar una explicación muy detallada si no tengo la seguridad de que de verdad te interesa el caso general del exponente real (en mi solución entrarían polinomios de Taylor, por ejemplo, y no es algo sobre lo que me apetezca escribir después de medianoche sin la certeza de que no se lo van a pasar por el forro, xD). Si te interesa (y por alguna razón sigues queriendo reinventar la rueda), ya nos lo dirás. Por otro lado, el caso particular de los exponentes enteros es una chorrada (tanto iterativa como recursivamente). Con lo cual no trato de decirte nada malo, sino que encontrarás muchos ejemplos, tanto en este foro como en cualquier otro sitio web. Además, ya que quieres que no haya bucles en tu programa, has de saber que es muy fácil y muy mecánico convertir una función iterativa (que seguramente ya sabes como se haría para este caso) en una recursiva. Un ejemplo con el factorial:

Código (cpp) [Seleccionar]
int factorial1(int n) { // Version iterativa
int result = 1;
for (int i = 2; i <= n; ++i) result *= i;
return result;
}

int factorial2(int n) { // Version recursiva
if (n < 2) return 1;
else return n*factorial2(n-1);
}


Sólo tienes que aplicar lo mismo en tu caso (si sólo contemplas potencias enteras).

¡Saludos!

prometheus48

GRacias chicos, si me interesa. No es un ejercicio de clase, porque como he dicho ya varias veces tengo 14 años.

¿Qué porque quiero hacerlo sin bucle?
Bueno, porque segun mi padre es AVSOLUTAMENTE IMPOSIBLE hacerlo sin bucle y sin funcion pow. ( mi padre no sabe programar pero sabe de lo que hablo porque de pequeño programó en BASIC ). Entonces es exactamente por eso.

El usuario que puso un comentario arriba, no me acuerdo el nombre lo siento, me interesa de lo que hablas...
¿POdrías explicar un poco?

Salu2!
"Si tú tienes una manzana, y yo otra, y las intercambiamos, tu sigues teniendo una manzana, y yo sigo teniendo una manzana.
Pero, si tu tienes una idea, y yo otra, y nos las intercambiamos, tu tienes dos ideas, y yo tengo dos ideas"
The knowledge is free

Anastacio

A, otro programador joven. Pues yo tengo 13, y me estoy liando con este post.

El codigo que se puso arriba, no serviria solo en caso de que los exponentes fuesen 0 o 1, y todos los demas???

Nota, no entendi el ultimo codigo, no puedo entender la sentencia for, me cuesta un Peru. Alguien me explica plisssss

Saludos!!!!
You, stop to close my post, you were novice too!!!!!!!!!!!!

Ferno

#6
Cita de: Anastacio en  1 Febrero 2012, 13:42 PM
Nota, no entendi el ultimo codigo, no puedo entender la sentencia for, me cuesta un Peru. Alguien me explica plisssss

Saludos!!!!

Para entender ese tipo de códigos es mejor agarrar lápiz y papel y hacerse un cuadro de seguimiento del algoritmo con varios ejemplos, y uno se va dando cuenta. Sin embargo, al menos en el método iterativo, el código está mal.

for (int i = 1; i < n; ++i) result *= i; /*Si se mantiene el pre-incremente (incrementar antes de entrar al for), entonces i debe comenzar en 1, y la condición de corte debe ser menor estricto (sino entraría al for cuando i = 6 y no daría el resultado correcto*/

for (int = 2; i <= n; i++) result *= i; /*Sino debe incrementarse posterior a cada ciclco*/

ghastlyX

Respecto a lo de elevar a exponentes reales, si soy sincero no me lo había planteado y no sé la mejor manera de hacerlo. Si tuviera que programarlo ahora lo haría en términos de la definición usando variable compleja de la potencia, que es términos de la exponencial y del logaritmo y que por tanto implican (sin usar esas dos funciones) series de Taylor. Pero si tienes 14 años, entrar en series de Taylor puede ser algo duro, necesitarías probablemente mucha base matemática de cosas que no conoces.

Respecto a lo que comenta Ferno, no sé por qué dices que está mal. Es lo mismo hacer
Código (cpp) [Seleccionar]
for (int i = 1; i < n; ++i)
que
Código (cpp) [Seleccionar]
for (int i = 1; i < n; i++)

El postincremento difiere del preincremento si se usa combinándolo con algo más, si no el resultado es equivalente. Como prueba simplemente hace falta considerar el siguiente código:
Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

int factorial1(int n) { // Version iterativa
    int result = 1;
    for (int i = 2; i <= n; ++i) result *= i;
    return result;
}

int factorial2(int n) { // Version iterativa
    int result = 1;
    for (int i = 2; i <= n; i++) result *= i;
    return result;
}

int main() {
    int n;
    while (cin >> n) cout << factorial1(n) << " " << factorial2(n) << endl;
}

Por ejemplo, la entrada
0
1
2
3
4
5
6
7
8


Produce la siguiente salida:
1 1
1 1
2 2
6 6
24 24
120 120
720 720
5040 5040
40320 40320

Ferno

Lo dije porque estaba seguro de que el pre-incremento justamente incrementaba la variable antes de llamar a las funciones del bucle for.
Perdón entonces!

Xandrete

#9
Cita de: prometheus48 en 31 Enero 2012, 22:53 PM
GRacias chicos, si me interesa. No es un ejercicio de clase, porque como he dicho ya varias veces tengo 14 años.

¿Qué porque quiero hacerlo sin bucle?
Bueno, porque segun mi padre es AVSOLUTAMENTE IMPOSIBLE hacerlo sin bucle y sin funcion pow. ( mi padre no sabe programar pero sabe de lo que hablo porque de pequeño programó en BASIC ). Entonces es exactamente por eso.

El usuario que puso un comentario arriba, no me acuerdo el nombre lo siento, me interesa de lo que hablas...
¿POdrías explicar un poco?

Salu2!

Tampoco hacía falta recordar mi nombre... lo podías copiar y pegar, xD.

A ver, dile a tu padre que sí que se puede hacer sin bucle. Ejemplo;

Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

int pow_aux(int b, int ex) {
if (ex == 0) return 1;
else return b*pow_aux(b,ex-1);
}

double pow(int b, int ex) {
if (ex > 0) return pow_rec(b,ex);
else return 1.0/pow_rec(b,-ex);
}

int main() {
int a,b;
cin >> a >> b;
cout << pow(a,b) << endl;
}


Y bueno, como te han comentado, hace falta tener base matemática para comprender como se haría en el caso del exponente real. Este es mi código:

Código (cpp) [Seleccionar]
#include <iostream>
#include <cmath>
#include <vector>
#define GRADMACLAURINSERIES 20
using namespace std;

vector<double> PolyExp;

vector<double> MacLaurinExp(int grad) {
vector<double> Poly(grad+1);
Poly[0] = 1;
for (int i = 1; i <= grad; ++i)
Poly[i] = Poly[i-1]/i;
return Poly;
}

double evalPoly(double x, vector<double> Poly) {
double result = 0;
for (int i = Poly.size()-1; i >= 0; --i)
result = result*x + Poly[i];
return result;
}

double pow_aux(int ex) {
if (ex == 0) return 1;
else return M_E*pow_aux(ex-1);
}

double pow(double b, double ex) {
double result;
ex *= log(b);
if (ex > 0) result = pow_aux(int(ex));
else result = 1.0/pow_aux(int(-ex));
result *= evalPoly(ex-int(ex),PolyExp);
return result;
}

int main() {
double x,y;
PolyExp = MacLaurinExp(GRADMACLAURINSERIES);
cin >> x >> y;
cout.precision(10);
cout << pow(x,y) << endl;
}


MacLaurinExp devuelve los coeficientes de la serie de MacLaurin (polinomio de Taylor en x=0) de la función exponencial (ex) en un vector. polyEval evalúa el polinomio para un valor de x mediante el algoritmo de Horner.

En mi código, lo que hago básicamente es plantearme una potencia xy de la siguiente manera: eln(x)*y (sólo funciona para bases mayores que 0, claro, para bases menores que 0 el resultado es, en general, complejo, y se tendría que calcular de otra manera). ln(x)*y consta de parte decimal y parte entera. Pongamos que ln(x)*y = 5.64. Como e5.64=e5e0.64la serie de MacLaurin (polinomio de Taylor en x=0) de la función exponencial, lo que hago es evaluar e5 de la manera tradicional (mediante bucles o mediante una función recursiva, como en este caso con pow_aux) y e0.64 lo obtengo mediante el polinomio de Taylor de la función exponencial. Luego multiplico los dos resultados y listo, tengo xy. Insisto en que, para comprender esto, deberías saber lo que es el polinomio de Taylor de una función. Además, considerando que tu nivel de programación todavía no es muy alto, quizás deberías centrarte en seguir aprendiendo antes de preocuparte por estas cosas.

EDITO: Oh, y M_E es una macro definida en cmath, que contiene el número e con la máxima precisión que admite la máquina.

Saludos

EI: juntando mensajes.

Ah, aquí una implementación mucho más eficiente del algoritmo para exponentes enteros. Si bien en los algoritmos dados hasta ahora el coste era Θ(n), en éste el coste es Θ(log n), lo cual es una mejora MUY importante.

Código (cpp) [Seleccionar]
#include <iostream>
using namespace std;

double pow_aux(double b, int e) {
if (e == 0) return 1;
else {
double x = pow_aux(b, e/2);
if (e%2) return x*x*b;
else return x*x;
}
}

double pow(double b, int e) {
if (e < 0) return 1/pow_aux(b,-e);
else return pow_aux(b,e);
}

int main() {
double b;
int e;
cin >> b >> e;
cout << pow(b,e) << endl;
}