#math #analysis
Вопрос:
Я пытаюсь определить асимптотическое время выполнения одного из моих алгоритмов, который использует показатели, но я не уверен в том, как показатели вычисляются программно.
Я специально ищу алгоритм pow (), используемый для чисел с плавающей запятой двойной точности.
Комментарии:
1. После первой правки этот вопрос все еще остается неясным. Вы упомянули «алгоритм, использующий показатели» и «алгоритм, используемый для чисел с плавающей запятой двойной точности». Алгоритм для чего? Кратно двум таким числам? Вычислить 3D-триангуляцию из N точек с двойной точностью x-y-z?
Ответ №1:
У меня была возможность ознакомиться с реализацией fdlibm. В комментариях описан используемый алгоритм:
* n
* Method: Let x = 2 * (1 f)
* 1. Compute and return log2(x) in two pieces:
* log2(x) = w1 w2,
* where w1 has 53-24 = 29 bit trailing zeros.
* 2. Perform y*log2(x) = n y' by simulating muti-precision
* arithmetic, where |y'|<=0.5.
* 3. Return x**y = 2**n*exp(y'*log2)
затем следует список всех обработанных особых случаев (0, 1, inf, nan).
Наиболее интенсивные разделы кода, после всей обработки особых случаев, включают log2
2**
вычисления и. И ни в том, ни в другом нет петель. Таким образом, несмотря на сложность примитивов с плавающей запятой, это выглядит как алгоритм с асимптотически постоянным временем.
Эксперты с плавающей запятой (к которым я не отношусь) могут прокомментировать. 🙂
Комментарии:
1. Как вы узнаете, имеет ли шаг 1 постоянное время? Вы читали код для log2? А как насчет шага 3, это действительно exp на основе электронной почты или exp2, и вы читали код для него?
2. Код не вызывает log2, exp или exp2. Он реализует все эти вещи встраиваемыми.
3. Я могу отчасти понять этот псевдокод, но я не знаю, каковы входные данные. Также что такое
**
?4. @DanF:
x ** y
естьpow(x, y)
, за исключением случая2 ** n
, когдаn
это целое число, вы можете использовать поиск по таблице. Входными данными являютсяx
(число с плавающей запятой, разделенное на показательn
степени и мантиссуf
) иy
.5. @DanF Кульминация в моем ответе. Предпоследнее предложение. 🙂
Ответ №2:
Если они не нашли лучшего способа сделать это, я считаю, что приблизительные значения тригонометрических, логарифмических и экспоненциальных функций (например, для экспоненциального роста и затухания) обычно вычисляются с использованием арифметических правил и разложений в ряд Тейлора для получения приблизительного результата с точностью до требуемой точности. (Смотрите любую книгу по математическому исчислению для получения подробной информации о разложениях функций в степенные ряды, ряды Тейлора и ряды Маклорена.) Пожалуйста, обратите внимание, что прошло некоторое время с тех пор, как я делал что-либо из этого, поэтому я не мог сказать вам, например, как точно рассчитать количество терминов в серии, которые вам нужно включить, чтобы гарантировать ошибку, достаточно малую, чтобы быть незначительной при расчете с двойной точностью.
Например, разложение в ряд Тейлора/Маклорена для e^x таково:
inf [ x^k ] x^2 x^3 x^4 x^5
e^x = SUM [ --- ] = 1 x --- ----- ------- --------- ....
k=0 [ k! ] 2*1 3*2*1 4*3*2*1 5*4*3*2*1
Если вы возьмете все члены (k от 0 до бесконечности), это расширение будет точным и полным (без ошибок).
Однако, если вы не возьмете все термины, идущие до бесконечности, но остановитесь, скажем, после 5 терминов или 50 терминов или чего-то еще, вы получите приблизительный результат, который отличается от фактического значения функции e^x на остаток, который довольно легко вычислить.
Хорошая новость для экспоненты является то, что она сходится красиво и сроки его полиномиальное расширение достаточно просто код итеративно, так что вам , возможно (повторяю, возможно, — помню, это было давно) даже не нужно заранее подсчитать, сколько условий необходимо для гарантии погрешность меньше, чем точность, потому что вы можете измерить размер взноса на каждой итерации, и прекращать, когда он будет достаточно близко к нулю. На практике я не знаю, жизнеспособна ли эта стратегия или нет — я должен был бы попробовать ее. Есть важные детали, о которых я давно забыл. Такие вещи, как: точность станка, ошибка станка и ошибка округления и т. Д.
Кроме того, обратите внимание, что если вы не используете e^x, но выполняете рост/спад с другой базой, такой как 2^x или 10^x, аппроксимирующая полиномиальная функция изменяется.
Комментарии:
1. Да, они нашли лучшие способы сделать это, для подходящих определений «они».
Ответ №3:
Обычный подход, чтобы поднять a до b для целочисленного показателя, выглядит примерно так:
result = 1
while b > 0
if b is odd
result *= a
b -= 1
b /= 2
a = a * a
Как правило, он логарифмичен по размеру показателя. Алгоритм основан на инварианте «a^b*результат = a0^b0», где a0 и b0-начальные значения a и b.
Для отрицательных или нецелочисленных показателей необходимы логарифмы, аппроксимации и численный анализ. Время выполнения будет зависеть от используемого алгоритма и от того, на какую точность настроена библиотека.
Редактировать: Поскольку, похоже, есть некоторый интерес, вот версия без дополнительного умножения.
result = 1
while b > 0
while b is even
a = a * a
b = b / 2
result = result * a
b = b - 1
Комментарии:
1. Похоже, в вашем псевдокоде есть пара ошибок. результат обновляется только тогда, когда b нечетно и когда (b == 1) в верхней части цикла while произойдет дополнительное умножение.
2. Я думаю, что ваш подход был бы хорош для возведения в степень большого числа, но не совсем применим к возведению в степень с плавающей запятой, для которого существует способ вычисления с постоянным временем.
Ответ №4:
Вы можете использовать exp(n*ln(x)) для вычисления x n. И x, и n могут быть числами двойной точности с плавающей запятой. Натуральный логарифм и экспоненциальная функция могут быть вычислены с использованием рядов Тейлора. Здесь вы можете найти формулы: http://en.wikipedia.org/wiki/Taylor_series
Ответ №5:
Если бы я писал функцию pow, предназначенную для Intel, я бы вернул exp2(log2(x) * y). Микрокод Intel для log2, безусловно, быстрее, чем все, что я смог бы закодировать, даже если бы я мог вспомнить свой первый курс математики и численный анализ в аспирантуре.
Ответ №6:
e^x = (1 дробь) * (2^показатель степени), 1 <= 1 дробь
x * log2(e) = log2(1 дробь) показатель степени, 0 <= log2(1 дробь)
показатель = этаж(x * log2(e))
1 дробь = 2^(x * log2(e) — показатель) = e^((x * log2(e) — показатель) * ln2) = e^(x — показатель * ln2), 0 <= x — показатель * ln2