Поиск log2() с помощью sqrt()

#logarithm #sqrt

#логарифм #sqrt

Вопрос:

Это вопрос из интервью, который я видел на каком-то сайте.

Упоминалось, что ответ включает в себя формирование повторения log2() следующим образом:

 double log2(double x )
{
if ( x<=2 ) return 1;
 if ( IsSqureNum(x) )
   return log2(sqrt(x) ) * 2;
 return log2( sqrt(x) ) * 2   1; // Why the plus one here.
}
  

что касается повторения, очевидно, что 1 неверно. Кроме того, базовый вариант также ошибочен.
Кто-нибудь знает ответ получше?
Как log() и log10() на самом деле реализованы в C.

Комментарии:

1. 1 за вопрос. Я только что попробовал ввести 100 в качестве входных данных, и он вернул 30. Метод не завершен.

2. В Википедии есть алгоритм en.wikipedia.org/wiki/Binary_logarithm

Ответ №1:

Возможно, я нашел точные ответы, которые искали интервьюеры. Со своей стороны, я бы сказал, что это немного сложно вывести под давлением собеседования. Идея в том, что, допустим, вы хотите найти log2(13) , вы можете знать, что оно лежит между 3-4. Также 3 = log2(8) and 4 = log2(16) ,

из свойств logarithm мы знаем, что log( sqrt( (8*16) ) = (log(8) log(16))/2 = (3 4)/2 = 3.5

Теперь, sqrt(8*16) = 11.3137 и log2(11.3137) = 3.5 . Поскольку 11.3137<13 мы знаем, что наш искомый log2 (13) будет находиться между 3.5 и 4, мы приступаем к его поиску. Легко заметить, что это решение для бинарного поиска, и мы выполняем итерации до того момента, когда наше значение сходится к значению, log2() которое мы хотим найти. Код приведен ниже:

 double Log2(double val)
{
    int lox,hix;
    double rval, lval;
    hix = 0;
    while((1<<hix)<val)
        hix  ;
    lox =hix-1;
    lval = (1<<lox) ;
    rval = (1<<hix);
    double lo=lox,hi=hix;
   // cout<<lox<<" "<<hix<<endl;
    //cout<<lval<<" "<<rval;
    while( fabs(lval-val)>1e-7)
    {
        double mid = (lo hi)/2;
        double midValue = sqrt(lval*rval);

        if ( midValue > val)
        {
             hi = mid;
             rval = midValue;
        }
        else{
            lo=mid;
            lval = midValue;
        }
    }
    return lo;

}
  

Ответ №2:

Прошло много времени с тех пор, как я писал чистый C, поэтому вот он на C (я думаю, единственное отличие — это функция вывода, так что вы должны быть в состоянии следовать за ней):

 #include <iostream>
using namespace std;

const static double CUTOFF = 1e-10;

double log2_aux(double x, double power, double twoToTheMinusN, unsigned int accumulator) {
     if (twoToTheMinusN < CUTOFF)
       return accumulator * twoToTheMinusN * 2;
     else {
       int thisBit;
       if (x > power) {
            thisBit = 1;
            x /= power;
       }
       else
            thisBit = 0;
       accumulator = (accumulator << 1)   thisBit;
       return log2_aux(x, sqrt(power), twoToTheMinusN / 2.0, accumulator);
     }
}

double mylog2(double x) {
     if (x < 1)
       return -mylog2(1.0/x);
     else if (x == 1)
       return 0;
     else if (x > 2.0)
       return mylog2(x / 2.0)   1;
     else
       return log2_aux(x, 2.0, 1.0, 0);
}

int main() {
     cout << "5 " << mylog2(5) << "n";
     cout << "1.25 " << mylog2(1.25) << "n";
     return 0;
}
  

Функция ‘mylog2’ выполняет некоторые простые операции с журналом, чтобы получить связанное число, которое находится между 1 и 2, затем вызывает log2_aux с этим номером.

log2_aux более или менее следует алгоритму, на который Scorpi0 ссылался выше. На каждом шаге вы получаете 1 бит результата. Когда у вас будет достаточно битов, остановитесь.

Если вы можете достать копию, Лекции Фейнмана по физике, номер 23, начинаются с отличного объяснения журналов и более или менее того, как выполнить это преобразование. Значительно превосходит статью в Википедии.

Комментарии:

1. Я думаю, вам не хватает некоторых угловых случаев, для 4 я получаю 1.5, где явно должно быть 2.