Logaritmo sin librería Math.t

Iniciado por norris, 5 Noviembre 2012, 14:14 PM

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

do-while

#10
¡Buenas!

Creo que lo mas intuitivo es trabajar con la funcion inversa y aplicar Bolzano:

x = loga(b) -> ax = b -> ax - b = 0

Por lo tanto se trata de encontrar el cero de la función f(x) = ax - b

Aplicando un poco de analisis sabemos que ax es siempre positivo (siempre que a sea positivo), por lo tanto tal cero existirá si y solo si b es positivo.

En estas condiciones tendrás que buscar dos puntos x0 y0 en los que la función tenga signo distinto. Así sabrás que el cero de la función se encuentra entre estos dos puntos.

Solo te queda iterar. En cada paso tendrás que buscar el punto medio del intervalo y evaluarlo para saber si la función toma un valor positivo o negativo en dicho punto. Una vez sepas el valor de la función al evaluarlo en el punto medio podrás sustituir la cota superior o inferior que tenias inicialmente por el punto medio.

Este proceso parará cuando:
- La evaluación de la función en el punto medio de exactamente cero.
- La diferencia entre la cota superior e inferior sea menor a un valor que tu consideres un error aceptable para la solución del problema.
- Cuando entre una iteración y la siguiente no cambie el punto medio (esto sucede debido al incremento mínimo que existe en la representación interna de números en coma flotante)

El valor que devolverá la función logaritmo será el punto medio del intervalo en el que han acabado las iteraciones, ya que será el valor de x que ha hecho que la función f(x) = ax - b se aproxime mas a cero (el valor mas proximo a loga(b))

¡Saludos!
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!

Puntoinfinito

Hey! Tienes dos opciones, o copiar la función de log() directamente desde la librería math.h  :xD

Ó hacerlo de la siguiente manera;

Debes aplicar un algoritmo matemático y hacerlo de manera estándar.



Por lo tanto



Como ves se ha de pasar de un calculo logarítmico hacía uno exponencial.

log 10 (a) = ? ; 10[sup]x[/sup] = a

La x esta porque no sabemos el resultado de ese logaritmo, en caso de que lo tuviéramos sería tal como el ejemplo de la imagen anterior. Como no es así, elevamos una incógnita y factorizamos  a
. Imaginemos que a nos ha dado 5. Entonces, ahora tenemos 10x = 10[ sup]5[/sup]. Comparamos antes de factorizar y después de hacerlo y vemos que las bases son las mismas, entonces al ser las mismas las ignoramos y el número que nos ha la factorización es el resultante. x = 5

PD: Creo que era así
AHORA EN SOFTONIC || CLICK HERE!!
Base64: QWNhYmFzIGRlIHBlcmRlciAxIG1pbnV0byBkZSB0dSB2aWRhLiBPbOkh



HACK AND 1337 : http://hackandleet.blogspot.com
WEBSITE: http://www.infiniterware.

avesudra

Implementando de manera rápida lo que comentó @diskontrol que como he podido comprobar solo es válido para logaritmos en base 10, he sacado este código (seguro que es un churro, no me mateis   :( ). En cuanto lo último comentado por @Puntoinfinito eso solo vale para logaritmos exactos. ¿@do-while al fin y al cabo es ensayo y error no? Si yo tengo log24 -> 2x = 4 vas probando valores enteros hasta que tengas un resultado mayor que 4 o igual y vas reduciendo ... no sé tampoco tengo nivel matemático para hacerlo, implemente la serie de Taylor pero es muy lenta, dejo el código al que @diskontrol se refería:
Código (cpp) [Seleccionar]
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

#define PRECISION 1000

#define e 2.7182818284590452353602874713526624977572470936999595749669676277240766303535

double ln (double num);
double log10 (double num, std::string &str);
double pow (double base, register int exp);


int main(int argc , char * argv [])
{
    string str;
    cout <<"Resultado en la precision maxima de de double es: "<< log10(34.4,str) << endl;
    cout << "Resultado truncado a "<< PRECISION << " decimales es: " << endl<< str;
    return 0;
}

double pow (double base, register int exp)
{
    double ret = 1;
    while(exp!=0)
    {
        ret = base*ret;
        --exp;
    }
    return ret;
}
double ln (double num)
{
    string unused;
    return (log10(num,unused)/log10(e,unused));
}
double log10 (double num,std::string &str)
{
    double ret = 0;
    double decimal  = 0;

    if(num < 10)
        num = pow(num,10);
    else
    {
        while(num >= 10)
        {
            num /= 10;
            ++ret;
        }
        num = pow(num,10);
    }

    ostringstream convert;   // stream usado para la conversión.
    convert <<ret;
    str += convert.str();
    str += '.';
    for(register int i = 1 ; i != PRECISION; ++i)
    {
        while(num >= 10)
        {
            num /= 10;
            ++decimal;
        }
        ret += decimal*(1/(pow(10,i)));
        num = pow(num,10);

        str +=(char) decimal+48;
        decimal = 0;
    }
    return ret;
}

Regístrate en

diskontrol

Cita de: avesudra en  3 Abril 2013, 00:01 AM
...IImplementando de manera rápida lo que comentó @diskontrol que como he podido comprobar solo es válido para logaritmos en base 10...
Puedes usarlo para cualquier base. No está en c, que así es más rápido, pero se entiende facil

Código (matlab) [Seleccionar]
function[x]=loga(num,basenum,baselog,decimales)
    x=0;
    for i=0:decimales
        contador=0;
        while num > baselog then
            num=num/baselog;
            contador=contador+1;
        end
        num=num^basenum
        x=x+(contador/(10^i))
    end
endfunction


Código (matlab) [Seleccionar]
-->loga(12345,10,%e,4)
ans  =

    9.421 

-->log(12345)
ans  =

    9.4210064


Hay que usarlo con cuidado porque tiene limitaciones y no tiene en cuenta ni si quiera el dominio del logaritmo :-P
Siempre ten tus cosas cuando las necesites con @Dropbox. ¡Una cuenta de 2 GB es gratis! http://db.tt/YxRhsCI

do-while

#14
Cita de: avesudra en  3 Abril 2013, 00:01 AM¿@do-while al fin y al cabo es ensayo y error no?

XD, si y no. Se puede considerar una especie de ensayo y error, pero aplicando la lógica. Después del primer paso conseguimos dos puntos x0, y0 en los que la función evaluada en el primero punto, en este caso en concreto, es menor que cero y evaluada en el segundo es mayor que cero:

                                                                     f(y0)



x0------------------------------------------------------y0 (eje OX)


f(x0)

Como la función es continua, si unes f(x0) y f(y0) con una linea, siempre tendrás que atravesar el eje OX. Es decir, en algún punto entre x0 e y0 la función sera cero. Ese es el punto que buscamos y sabemos que está entre ambos valores.

Por lo tanto obtenemos el punto z0 = (x0 + y0) / 2 y evaluamos la función en dicho punto. Imagina que sale que la función es positiva en z0:

                                                                     f(y0)

                                   f(z0)

x0---------------------------z0--------------------------y0 (eje OX)


f(x0)

Sigue sucediendo lo mismo de antes, entre x0 y z0 la función se anula, pero ahora tenemos un intervalo mas pequeño que el primero. Como z0 da un valor positivo, hacemos x1 := x0 e y1 := z0. Y aplicamos el proceso anterior al intervalo [x1,y1] para obtener un punto z1 que estará todavía mas próximo al cero de la función.

A fin de cuentas se trata de eso. De aproximar en cada paso cada vez mas el cero de la función dividiendo el intervalo de búsqueda en cada iteración. Si consideramos un error e, se tendrá que:

(y0 - x0) / 2n < e si y solo si

y0 - x0 < e 2n si y solo si

(y0 - x0) / e < 2n si y solo si

log2((y0 - x0) / e) < n

Es decir, como mucho tendremos que iterar una vez mas que la parte entera de log2((y0 - x0) / e) para obtener un resultado que se aproxime a la raíz con una precisión mayor que e/2.

¡Saludos!
- Doctor, confundo los números y los colores.
- Vaya marrón.
- ¿Marrón? ¡Por el culo te la hinco!