2015-01-15 4 views
3

Я не могу понять, почему этот алгоритм входит в бесконечный цикл, если введенное число более 12 цифр. Кто-нибудь может понять, почему это никогда не закончится? Благодарю. Я просто обновил алгоритм, чтобы использовать функцию fabs(), и все равно получаю бесконечный цикл.алгоритм квадратного корня C++

double squareroot(double x) 

{ /* computes the square root of x */ 

/* make sure x is not negative .. no math crimes allowed! */ 
assert(x >= 0); 
if (x==0) return 0; 

/* the sqrt must be between xhi and xlo */ 
double xhi = x; 
double xlo = 0; 
double guess = x/2; 

/* We stop when guess*guess-x is very small */ 

while (abs(guess*guess-x) > 0.00001) 
{ 
    if (guess*guess > x){ 
     xhi = guess; 
    } 

    else { 
     xlo = guess; 
    } 

    guess = (xhi + xlo)/2; 
} 
return guess; 
} 
+5

'abs' функция для целых чисел. Вы имели в виду 'fabs'? –

+0

12 цифр 'guess * guess'? что было бы большим .. – Ankur

+0

http://floating-point-gui.de/ –

ответ

5

Я считаю, что вы должны использовать относительную погрешность для прекращения, а не абсолютная погрешность.

while (abs((guess*guess-x)/guess) > 0.00001) 

В противном случае это займет очень много времени (это не бесконечный цикл) для вычисления квадратного корня из очень длинных значений.

http://en.wikipedia.org/wiki/Approximation_error

Ура!

EDIT: кроме того, как указано ниже в комментариях, он достоин того, чтобы проверить, если guess уже догадались, с тем чтобы избежать бесконечного цикла с некоторыми конкретными случаями угловых.

+2

Цикл может стать бесконечным, когда числа велики: когда« xhi »и« xlo »смежны, средняя точка всегда будет одной из них Когда «xhi-xlo» больше, чем выбранный эпсилон, вы получаете бесконечный цикл. –

+0

@MOehm Хорошая точка. Это можно проверить дополнительно, т.е. цикл должен быть завершен, когда новое предположение уже было догадано раньше ('guess = = xhi || guess == xlo') – leemes

+0

@MOehm Согласен. Ответ TonyD исправляет эту проблему, как отмечал Leemes в своем комментарии. (хотя было бы здорово получить фактическое x, чтобы это произошло: D) – ale64bit

0

http://floating-point-gui.de/errors/comparison/

Хех, похоже, вы не должны использовать абс(), как это. Есть некоторые случаи, когда он должен остановиться, но я не буду, поскольку это ограничена точность.

Вместо этого используйте FABS()

http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm

+0

Спасибо, я просто обновил его, чтобы использовать функцию fabs(), и я все еще получаю бесконечный цикл. –

+4

Видимо, есть несколько программистов на С ++, которые не знают о перегруженной функции 'std :: abs'. –

+0

@ н.м .: Touché. Я пришел сюда через тег _algorithm_, и я не видел тег _C++ _. Просто посмотрел на код и увидел, что это C. Нет потоков, нет пространств имен, нет ''. –

0

Я бы сказал, что, когда числа достаточно велики, вы не можете использовать абсолютное значение эпсилона, потому что оно не вписывается в точность.

Попробуйте вместо этого использовать относительное сравнение. Рассмотрим следующую функцию, чтобы проверить, если 2 двойники равны:

bool are_eql(double n1, double n2, double abs_e, double rel_e) 
{ 
    // also add check that n1 and n2 are not NaN 
    double diff = abs(n1 - n2); 
    if (diff < abs_e) 
    return true; 
    double largest = max(abs(n1), abs(n2)); 
    if (diff < largest * rel_e) 
    return true; 
    return false; 
} 
+0

* «Вы должны быть умнее и ...» *? Чувак, это звучит жестоко. – ale64bit

+0

@kaktusito, не хотел никого обижать :) Я удалю эту часть ... –

+0

Первым lijne, очевидно, должно быть 'abs (n1-n2)'. Я полагаю, что 'наибольший' должен быть' abs (max (n1, n2)) '- что, если оба они отрицательные? В вашем коде 'наибольший' может быть отрицательным, и в этом случае' diff> наибольший' (как +0.01> -100.0) – MSalters

2

Это не является прямым ответом на ваш вопрос, но альтернативное решение.

Вы можете использовать Newton's method for finding roots:

assert(x >= 0); 
if (x == 0) 
    return 0; 

double guess = x; 
for (int i=0; i<NUM_OF_ITERATIONS; i++) 
    guess -= (guess*guess-x)/(2*guess); 
return guess; 

24 итераций вы должны получить достаточно хорошее приближение, но вы также можете проверить абсолютную разницу.

3

Я предлагаю подождать, пока вы не получили стабильный ответ, а не возиться со значениями эпсилон:

double squareroot(double x) 
{ 
    double xhi = x; 
    double xlo = 0; 
    double guess = x/2; 

    while (guess * guess != x) 
    { 
     if (guess * guess > x) 
      xhi = guess; 
     else 
      xlo = guess; 

     double new_guess = (xhi + xlo)/2; 
     if (new_guess == guess) 
      break; // not getting closer 
     guess = new_guess; 
    } 
    return guess; 
} 
+1

Очень хорошо! Средство к бесконечному циклу, которое возникает из-за того, что одно и то же предположение используется снова и снова, конечно, требует другого угадывания. –

+1

Это работает для x = 0,25? Поскольку алгоритм, по-видимому, предполагает, что 'sqrt (x)' лежит в диапазоне '[0, x]', в то время как 'sqrt (0.25)> 0.25'. Первое предположение - 0.125, второе - 0.1875 и т. Д. Похоже, xlo сходится к xhi, и цикл заканчивается, когда xlo = xhi = guess = 0.25, что, очевидно, не 0,5 – MSalters

+0

@MSalters: нет ... и это заслуживает упоминания; Я не пытаюсь исправить этот алгоритм, просто имея дело с проблемой конвергенции/точности в вопросе. –

Смежные вопросы