Как я могу записать функцию питания сам?

Я всегда задавался вопросом, как я могу сделать функцию, которая вычисляет питание (например, 23) сам. На большинстве языков они включены в стандартную библиотеку, главным образом как pow(double x, double y), но как я могу записать это сам?

Я думал о for loops, но это думает, что мой мозг вошел в цикл (когда я хотел сделать питание с экспонентой нецелого числа, как 54,5 или отрицательные стороны 2-21), и я сошел с ума ;)

Так, как я могу записать функцию, которая вычисляет питание вещественного числа?Спасибо


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

49
задан 5 May 2011 в 21:27
поделиться

9 ответов

Отрицательные степени не проблема, они просто инверсия ( 1 / x ) положительной мощности.

Степени с плавающей запятой немного сложнее; как вы знаете, дробная степень эквивалентна корню (например, x ^ (1/2) == sqrt (x) ), и вы также знаете, что умножение степеней с одинаковым основанием эквивалентно сложению их показателей .

С помощью всего вышеперечисленного вы можете:

  • Разложить показатель степени на целую и рациональную части .
  • Вычислите целочисленную степень с помощью цикла (вы можете оптимизировать его, разложив на множители и повторно используя частичные вычисления).
  • Вычислите корень с помощью любого алгоритма, который вам нравится (может работать любое итеративное приближение, например, деление пополам или метод Ньютона).
  • Умножьте результат.
  • Если показатель был отрицательным, примените обратное.

Пример:

2^(-3.5) = (2^3 * 2^(1/2)))^-1 = 1 / (2*2*2 * sqrt(2))
48
ответ дан 7 November 2019 в 11:30
поделиться

По определению:

a^b = exp(b ln(a))

где exp(x) = 1 + x + x^2/2 + x^3/3! + x^4/4! + x^5/5! + ...

где n! = 1 * 2 * ... * n.

На практике можно хранить массив из первых 10 значений 1/n! , а затем приближенно

exp(x) = 1 + x + x^2/2 + x^3/3! + ... + x^10/10!

потому что 10! - огромное число, поэтому 1/10! очень мало (2.7557319224⋅10^-7).

7
ответ дан 7 November 2019 в 11:30
поделиться

Функции Вольфрама дают большое количество формул для вычисления степеней. Некоторые из них было бы очень просто реализовать.

4
ответ дан 7 November 2019 в 11:30
поделиться

Лучшим алгоритмом для эффективного вычисления положительных целых чисел является многократное возведение в квадрат основания с учетом дополнительных остатков от умножения. Вот пример решения на языке Python, который будет относительно легко понять и перевести на ваш язык:

def power(base, exponent):
  remaining_multiplicand = 1
  result = base

  while exponent > 1:
    remainder = exponent % 2
    if remainder > 0:
      remaining_multiplicand = remaining_multiplicand * result
    exponent = (exponent - remainder) / 2
    result = result * result

  return result * remaining_multiplicand

Чтобы заставить его работать с отрицательными экспонентами, достаточно вычислить положительную версию и разделить 1 на результат, так что это будет простой модификацией приведенного выше кода. Дробные экспоненты значительно сложнее, поскольку это означает вычисление n-го корня из основания, где n = 1/abs(exponent % 1) и умножение результата на результат вычисления мощности целой части:

power(base, exponent - (exponent % 1))

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

1
ответ дан 7 November 2019 в 11:30
поделиться

A B = Log -1 (Log (A) * B)

Изменить: да, это определение действительно дает кое-что полезное. Например, на x86 он почти напрямую преобразуется в FYL2X (Y * Log 2 (X)) и F2XM1 (2 x ] -1):

fyl2x
fld st(0)
frndint
fsubr st(1),st
fxch st(1)
fchs
f2xmi
fld1
faddp st(1),st
fscale
fstp st(1) 

Код оказывается немного длиннее, чем вы могли ожидать, в первую очередь потому, что F2XM1 работает только с числами в диапазоне -1.0..1.0. Часть fld st (0) / frndint / fsubr st (1), st вычитает целую часть, поэтому остается только дробь. Мы применяем к нему F2XM1 , снова добавляем 1, затем используем FSCALE для обработки целой части возведения в степень.

22
ответ дан 7 November 2019 в 11:30
поделиться

Обычно реализация функции pow (double, double) в математических библиотеках основана на идентификаторе:

pow(x,y) = pow(a, y * log_a(x))

Используя этот идентификатор, вам нужно только знать, как возвести единичное число a в произвольный показатель степени и как взять основание логарифма a . Вы эффективно превратили сложную функцию с несколькими переменными в две функции одной переменной и умножение, которое довольно легко реализовать. Наиболее часто выбираемые значения a - это e или 2 - e , поскольку e ^ x и log_e (1 + x) имеет несколько очень хороших математических свойств, а 2 , потому что он имеет несколько хороших свойств для реализации в арифметике с плавающей запятой.

Загвоздка этого способа заключается в том, что (если вы хотите получить полную точность) вам нужно вычислить член log_a (x) (и его произведение с y ) с более высокой точностью, чем представление с плавающей запятой x и y .Например, если x и y являются двойными, и вы хотите получить результат с высокой точностью, вам нужно придумать способ хранения промежуточных результатов (и выполнять арифметические операции ) в формате более высокой точности. Формат Intel x87 является обычным выбором, как и 64-битные целые числа (хотя, если вам действительно нужна высококачественная реализация, вам нужно будет выполнить пару 96-битных целочисленных вычислений, которые в некоторых случаях немного болезненны. языков). С этим намного легче справиться, если вы реализуете powf (float, float) , потому что тогда вы можете просто использовать double для промежуточных вычислений. Я бы рекомендовал начать с этого, если вы хотите использовать этот подход.


Описанный мной алгоритм - не единственный возможный способ вычисления pow . Он просто наиболее подходит для получения высокоскоростного результата, который удовлетворяет фиксированному априорному пределу точности. Он менее подходит в некоторых других контекстах и, безусловно, намного сложнее реализовать, чем алгоритм вычисления квадратного корня, который предлагали некоторые другие.

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

19
ответ дан 7 November 2019 в 11:30
поделиться

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

Для целочисленных показателей вы можете использовать возведение в степень возведением в квадрат.

def pow(base, exponent):
    if exponent == 0:
        return 1
    elif exponent < 0:
        return 1 / pow(base, -exponent)
    elif exponent % 2 == 0:
        half_pow = pow(base, exponent // 2)
        return half_pow * half_pow
    else:
        return base * pow(base, exponent - 1)

Второй «elif» - это то, что отличает эту функцию от наивной функции pow. Это позволяет функции выполнять рекурсивные вызовы O (log n) вместо O (n).

Для дробных показателей вы можете использовать тождество a ^ b = C ^ (b * log_C (a)). Удобно взять C = 2, поэтому a ^ b = 2 ^ (b * log2 (a)). Это сводит проблему к написанию функций для 2 ^ x и log2 (x).

Причина, по которой удобно использовать C = 2, заключается в том, что числа с плавающей запятой хранятся в базе 2 с плавающей запятой. log2 (a * 2 ^ b) = log2 (a) + b. Это упрощает написание вашей функции log2: вам не нужно, чтобы она была точной для каждого положительного числа, только на интервале [1, 2). Точно так же, чтобы вычислить 2 ^ x, вы можете умножить 2 ^ (целая часть x) * 2 ^ (дробная часть x). Первую часть легко сохранить в виде числа с плавающей запятой, для второй части вам просто понадобится функция 2 ^ x в интервале [0, 1).

Сложнее всего найти хорошее приближение 2 ^ x и log2 (x). Простой подход - использовать ряд Тейлора .

9
ответ дан 7 November 2019 в 11:30
поделиться

Для положительных целых степеней посмотрите на возведение в квадрат и возведение в степень .

4
ответ дан 7 November 2019 в 11:30
поделиться

Это интересное упражнение. Вот несколько советов, которые вы должны попробовать в следующем порядке:

  1. Используйте цикл.
  2. Используйте рекурсию (не лучше, но тем не менее интересно)
  3. Значительно оптимизируйте рекурсию, используя принцип «разделяй и властвуй» методы
  4. Использование логарифмов
1
ответ дан 7 November 2019 в 11:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: