Вычисление дробных показателей в C

Я пытаюсь оценить a ^ n, где a и n - рациональные числа. Я не хочу использовать какие-либо предопределенные функции, такие как sqrt() или pow()

Итак, я пытаюсь использовать метод Ньютона, чтобы получить приблизительное решение, используя этот подход:

3^0.2 = 3^(1/5) , so if x = 3^0.2, x^5 = 3.

Вероятно, лучший способ решить эту проблему (без калькулятора, но все еще используя основные арифметические операции) - это использовать «метод Ньютона». Метод Ньютона для решения уравнения f (x) = 0 состоит в том, чтобы установить последовательность чисел xn, определенную путем взятия x0 в качестве некоторого начального "предположения", а затем xn + 1 = xn- f (xn / f '(xn), где f '(x) - производная от f.

Размещено на физическом форуме

Проблема с этим методом в том, что если я хочу вычислить 5.2^0.33333, мне нужно будет найти корни для этого уравнения x^10000 - 5.2^33333 = 0. Я получаю огромные числа и большую часть времени получаю ошибки inf и nan.

Может кто-нибудь посоветует, как решить эту проблему? Или может кто-нибудь предоставить другой алгоритм для вычисления ^ n?


person user3787097    schedule 04.07.2014    source источник
comment
Есть ли причина, что вы не хотите использовать стандартные математические функции? Любой другой подход, вероятно, будет значительно медленнее и менее точен.   -  person R.. GitHub STOP HELPING ICE    schedule 04.07.2014
comment
@R .. Нет веских причин для этого. Я просто развлекаюсь и смотрю, что я могу сделать. Это не домашнее задание или что-то в этом роде. Я закончил колледж, программирование - это хобби. Но я могу подумать, что если я программирую примитивный микроконтроллер, он может не иметь этих функций.   -  person user3787097    schedule 04.07.2014
comment
Как вы пришли к 3.2 = 3^(1/5)?   -  person R Sahu    schedule 04.07.2014
comment
@RSahu Это была ошибка, исправлена.   -  person user3787097    schedule 04.07.2014
comment
@R Sahu Хмммм метод Ньютона ... но не для оценки ... sqrt (x). Лучший способ мотивировать инженера - сказать, что что-то невозможно сделать. en.wikipedia.org/wiki/Newton's_method   -  person chux - Reinstate Monica    schedule 04.07.2014
comment
@RSahu Конечно, может! Я рекомендую вам погуглить математику. Я уже сделал это, к сожалению, это плохой подход для программирования.   -  person user3787097    schedule 04.07.2014
comment
оценка 5,2 ^ 0,001 означает решение x ^ 1000 = 5,2. тогда t1 = x ^ 3 дает 5,2 ^ 0,003. t2 = t1 ^ 10 = x ^ 30 дает 5,2 ^ 0,03. t3 = t2 ^ 10 = x ^ 300 дает 5,2 ^ 0,3. наконец, t1 * t2 * t3 дает вам 5,2 ^ 0,333. это звучит как x ^ 333.   -  person Cheers and hth. - Alf    schedule 04.07.2014
comment
@ user3787097: Я никогда не видел процессор / процессор со встроенной рабочей функцией Pow. pow - очень сложная библиотечная функция (вероятно, самая сложная и непонятная из всех математических библиотечных функций), которую трудно воспроизвести и которая совершенно непрактична в качестве отдельной аппаратной операции.   -  person R.. GitHub STOP HELPING ICE    schedule 04.07.2014
comment
@ Cheersandhth.-Альф, очень хорошее предложение. Но, x ^ 1000, где x, например, 5, это уже огромное число --- ›9,33x10 ^ 698 Я не думаю, что смогу использовать этот подход.   -  person user3787097    schedule 04.07.2014
comment
Я реализовал такую ​​возможность и много раз ею пользовался. Он предоставляет класс для хранения натурального числа любого мыслимого размера и класс для хранения рационального числа любого мыслимого размера (с использованием натурального числителя и натурального знаменателя). Оба класса поддерживают любые арифметические операции, предоставляемые C ++. Класс рациональных чисел также реализует степень и корень естественным показателем. Если вы хотите вычислить степень с помощью рациональной экспоненты, вы можете использовать числитель показателя степени и корень с помощью знаменателя показателя степени. См. github.com/barakman/Num.   -  person barak manos    schedule 04.07.2014
comment
@ user3787097: у меня все получилось, когда я подготовил этот пример. нет проблем.   -  person Cheers and hth. - Alf    schedule 04.07.2014
comment
Если показатель степени может быть представлен в виде дроби: верхний / нижний, тогда основание ^ (верхнее / нижнее) дает результат: основание ^ верхнее / основание ^ нижнее.   -  person user3629249    schedule 06.07.2014


Ответы (3)


Вроде твоя задача - посчитать

⎛ xN ⎞(aN / aD)
⎜⎼⎼⎼⎼⎟           where xN,xD,aN,aD ∈ ℤ,  xD,aD ≠ 0
⎝ xD ⎠

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

Уравнение, которое мы пытаемся решить (для y):

             (aN / aD)
y = (xN / xD)            where y ∈ ℝ

Метод Ньютона находит корень функции. Если мы хотим использовать его для решения вышеуказанной проблемы, мы вычитаем правую часть из левой, чтобы получить функцию, ноль которой дает нам желаемое y:

                  (aN/aD)
f(y) = y - (xN/xD)        = 0

Не очень помогло. Я думаю, это все, что вам нужно? Дело здесь в том, чтобы пока не формировать эту функцию, потому что у нас нет способа вычислить рациональную степень рационального числа!

Во-первых, давайте решим, что оба значения aD и xD положительны. Мы можем сделать это, просто отрицая и aN, и aD, если aD было отрицательным (так что знак aN / < em> aD не изменяется), и отрицание значений xN и xD, если xD было отрицательным. Помните, что по определению ни xD, ни aD не равны нулю. Затем мы можем просто возвести обе стороны в ад степени:

 aD            aN     aN     aN
y   = (xN / xD)   = xN   / xD

Мы даже можем исключить деление, умножив обе части на последний член:

 aD     aN     aN
y   × xD   = xN

Теперь это выглядит многообещающе! Функция, которую мы получаем из этого,

        aD   aN     aN
f(y) = y   xD   - xN

Метод Ньютона также требует производной, которая, очевидно,

f(y)            aD   aN
⎼⎼⎼⎼ = df(y) = y   xD   y / aD
 dy

Сам метод Ньютона основан на повторении

             f(y)
y    = y  - ⎼⎼⎼⎼⎼⎼
 i+1    i    df(y)

Если вы разберетесь с математикой, вы обнаружите, что итерация просто

                                 aD
                y[i]      y[i] xN
y[i+1] = y[i] - ⎼⎼⎼⎼ + ⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼
                 aD           aD   aN
                       aD y[i]   xD

Вам не нужно хранить все значения y в памяти; достаточно запомнить последний и прекратить повторение, когда их разница достаточно мала.

У вас все еще есть возведение в степень выше, но теперь это только целочисленное возведение в степень, т.е.

  aD
xN   = xN × xN × .. × xN
       ╰───────┬───────╯
              aD times

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

double ipow(const double base, const int exponent)
{
    double result = 1.0;
    int    i;
    for (i = 0; i < exponent; i++)
        result *= base;
    return result;
}

Существуют более эффективные методы для выполнения целочисленного возведения в степень, но указанная выше функция должна быть вполне приемлемой для этого.

Последняя проблема - выбрать начальное y, чтобы получить сходимость. Вы не можете использовать 0, потому что (степень) y используется в качестве знаменателя при делении; вы получите деление на нулевую ошибку. Лично я проверял, должен ли результат быть положительным или отрицательным, а также быть меньше или больше единицы по величине; два общих правила, чтобы выбрать безопасное начальное y.

Вопросы?

person Nominal Animal    schedule 04.07.2014
comment
@ DavidC.Rankin: Я не знаю, поддерживается ли MathML для Markdown в Stackoverflow, поэтому я обратился к графике Unicode. Если есть лучший способ представления формул, будет приветствоваться редактирование этого ответа! Пользователь Linux, которому нужно делать простые заметки в простых текстовых файлах (комментарии C, часто1), глифах UTF-8 и Unicode (с использованием виджета «Карта символов» для поиска), делает это довольно легко. Я задавался вопросом, может ли специальный виджет GTK + для упрощения рисования типичных диаграмм быть полезным для других .. - person Nominal Animal; 04.07.2014
comment
О нет, я думал, что это превосходно. Опять же молодец, без сарказма :) - person David C. Rankin; 04.07.2014

Вы можете использовать обобщенную биномиальную теорему. Заменить y=1 и x=a-1. Вы можете усечь бесконечный ряд после достаточного количества членов, исходя из желаемой точности. Чтобы иметь возможность связать количество терминов с точностью, вам необходимо убедиться, что x^r термины уменьшаются по абсолютной величине. Итак, в зависимости от значений a и n, вы должны применить формулу для вычисления одного из a^n и a^(-n) и использовать это для получения желаемого результата.

person Pradhan    schedule 04.07.2014

Решение возведения целого числа в степень:

int poweri (int x, unsigned int y)
{
    int temp;
    if (y == 0)
        return 1;

    temp = poweri (x, y / 2);
    if ((y % 2) == 0)
        return temp * temp;
    else
        return x * temp * temp;
}

Однако квадратный корень не дает такого чистого закрытого решения. На странице wikipedia-square root и Алгоритмы квадратного корня Wolfram Mathworks Оба предоставляют несколько методов, которые будут удовлетворить ваши потребности, вам просто нужно выбрать тот, который соответствует вашим целям.

С небольшими изменениями эта процедура из Википедии (измененная для возврата квадратного корня и повышения точности) возвращает удивительно точный квадратный корень. Да, будут вопли об использовании объединения, и это действительно только тогда, когда целочисленное хранилище и хранилище с плавающей запятой эквивалентны, но если вы взламываете свой собственный квадратный корень, это относительно эффективно:

float sqrt_f (float x)
{
        float xhalf = 0.5f*x;
        union
        {
            float x;
            int i;
        } u;
        u.x = x;
        u.i = 0x5f3759df - (u.i >> 1);
        /* The next line can be repeated any number of times to increase accuracy */
        // u.x = u.x * (1.5f - xhalf * u.x * u.x);
        int i = 10;
        while (i--)
            u.x *= 1.5f - xhalf * u.x * u.x;

        return 1.0f / u.x;
}
person David C. Rankin    schedule 04.07.2014
comment
насчет обратного sqrt: из личного опыта и тестирования, итераций в 3 ньютона достаточно, чтобы получить машинную точность с плавающей / одинарной точностью. Версия с двойной точностью вместо этого требует 4. 10 итераций - это просто (бесполезное) излишество. - person Federico; 01.02.2016