Учитывая r ^ 2, есть ли эффективный способ вычислить r ^ 3?

double r2 = dx * dx + dy * dy;
double r3 = r2 * sqrt(r2);

Можно ли вторую строчку заменить на что-нибудь более быстрое? Что-то, что не касается sqrt?


person fredoverflow    schedule 09.12.2011    source источник
comment
Кажется, ваш код противоречит вашей подписи: Где в вашем коде на самом деле есть r ^ 2? У вас есть только r2, которое содержит нечто совершенно иное, чем r в квадрате ... или, я думаю, я неправильно понял, ^ не должно означать в степени?   -  person codeling    schedule 09.12.2011
comment
@nyarlathotep: Предположим, r это sqrt(dx * dx + dy * dy).   -  person GManNickG    schedule 09.12.2011
comment
@nyarlathotep, ты упустил суть. Если r = (dx dx + dy dy) ^ (1/2), то r2 = r ^ 2. Но проблема не в этом.   -  person Luchian Grigore    schedule 09.12.2011
comment
Я просто запутался, ^ - это обычное обозначение в степени, насколько мне известно.   -  person codeling    schedule 09.12.2011
comment
Я собираюсь рискнуть и сказать, что, кстати, вопрос поставлен, технически ответ - НЕТ. Простая причина в том, что если вам дано r ^ 2, вы не знаете знака r, так как же вычислить r ^ 3? Я думаю, вы действительно спрашиваете, можете ли вы эффективно найти эту норму в квадрате вектора?   -  person dantswain    schedule 09.12.2011
comment
В зависимости от необходимой точности и соотношения dx / dy вы можете попробовать ряд Тейлора (1 + x) ^ (1/2) = 1 + (1/2) * x - (1/8) * x ^ 2 + .. . где x = (dx / dy) ^ 2 ‹= 1.   -  person lapk    schedule 09.12.2011
comment
В зависимости от желаемой точности и относительных размеров dx и dy вы можете выполнить расширение Тейлора: например, если вы знаете, что dy мало по сравнению с dx, тогда вы можете приблизить r3 как dx(dx^2 + 3/2 * dy^2) (я думаю, что у меня есть это право). РЕДАКТИРОВАТЬ: Вау, это странно, AzzA!   -  person James    schedule 09.12.2011
comment
Насколько быстро sqrt, чтобы люди знали, что они пытаются победить? Использует ли ваш компилятор SSE (или, возможно, какой-то эквивалент для других архитектур)?   -  person Steve Jessop    schedule 09.12.2011
comment
@SteveJessop: пока 3 ответа, и ни одной скамейки / разборки. Я не думаю, что люди действительно заинтересованы в поиске более быстрого ответа, они просто бросают все, что могут придумать, мафии ...   -  person Matthieu M.    schedule 09.12.2011
comment
@Matthieu: Я не возражаю против этого - не зная настройки Фреда, невозможно сказать, что будет быстрее, и тест на настройку ответчика ничего не доказывает. Я думаю, будет справедливо предложить вещи, которые правдоподобно могут быть быстрее, чтобы он мог их проверить, но если то, что мы пытаемся превзойти, это sqrtss, за которым следует mul, то в любом случае мало что может быть правдоподобным. Частично суть моего вопроса заключается в том, что исправление может заключаться в использовании параметра компилятора -m.   -  person Steve Jessop    schedule 09.12.2011
comment
О, и, конечно, вопрос имел бы гораздо больше смысла, если бы ответ был положительным. Конечно, есть способ получше, ваш код без нужды ходит по домам, вот простая вещь, которую вы пропустили. Жалобы на то, почему вы не выполняете профилирование, были бы абсурдными, если бы Фред Богосортировал 100 элементов и спрашивал, знает ли кто-нибудь лучший алгоритм сортировки - нам не нужно профилировать, чтобы знать, что Богосорт ошибается. Но поскольку код Фреда близок к оптимальному, а sqrt или что-то подобное неизбежно, это похоже на вопрос микрооптимизации.   -  person Steve Jessop    schedule 09.12.2011


Ответы (3)


Я думаю, что другой способ взглянуть на ваш вопрос был бы «как вычислить (или приблизить) sqrt (n)». Оттуда ваш вопрос будет тривиальным (n * sqrt (n)). Конечно, вам нужно будет определить, с какой ошибкой вы можете жить. Википедия предлагает множество вариантов:

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

person Pedery    schedule 09.12.2011

Как насчет

double r3 = pow(r2,1.5);

Если sqrt реализован как частный случай pow, это сэкономит вам умножение. По большому счету, не так уж много!

Если вы действительно хотите повысить эффективность, подумайте, действительно ли вам нужно r ^ 3. Если, например, вы только тестируете его (или что-то на его основе), чтобы увидеть, превышает ли он определенный порог, то вместо этого проверьте r2, например.

const double r3_threshold = 9;

//don't do this
if (r3 > r3_threshold)
    ....

//do do this
const double r2_threshold = pow(r3_threshold,2./3.); 
if (r2 > r2_threshold)
    ....

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

РЕДАКТИРОВАТЬ. Если вам нужно каждый раз пересчитывать порог, я думаю, что ответ, касающийся Q_rsqrt, заслуживает внимания и, вероятно, заслуживает того, чтобы превзойти этот

person Sideshow Bob    schedule 09.12.2011
comment
А что насчет r2*r2*r2 > r3_thresh*r3_thresh? - person dantswain; 09.12.2011
comment
@dantswain Ну, это позволяет избежать pow, но требует дополнительных умножений и код менее понятен - person Sideshow Bob; 09.12.2011
comment
Разве пара (три) дополнительных мульта не будет быстрее, чем пауза? Я думаю, что удобочитаемость здесь субъективна ... Я обычно использую квадратный регистр как dx*dx + dy*dy < r_thresh*r_thresh; кубическая версия меня бы не бросила. Кроме того, если читабельность действительно была проблемой, вы могли бы скрыть ее в макросе или встроенном. пожать плечами - person dantswain; 09.12.2011
comment
Если ваша математическая библиотека действительно не работает, sqrt () не будет реализован через pow (x, .5), и он будет намного быстрее, чем pow () (IIRC Я тестировал это в какой-то момент и с libm в glibc sqrt () был примерно на порядок быстрее). Но да, я полагаю, не повредит попробовать ... - person janneb; 09.12.2011
comment
@dantswain Я думаю, что Сайдшоу Боб говорит о повторной проверке по одному и тому же порогу, и в этом случае один сигнал будет лучше, чем множество множителей (хотя это зависит от количества итераций). Но конечно для разовой проверки лучше ваша мультиверсия. Я также считаю, что удобочитаемость имеет второстепенное значение, учитывая микрооптимизирующий характер этой проблемы. - person Christian Rau; 09.12.2011
comment
@dantswain нет, если pow вычисляется только во время компиляции или один раз при запуске программы - person Sideshow Bob; 09.12.2011
comment
Достаточно честно :) В моем случае порог обычно меняется между итерациями и устанавливается пользователем, поэтому я оставляю его в удобных для пользователя единицах до проверки. - person dantswain; 09.12.2011
comment
@janneb gcc -S -O сообщает мне, что некоторые компиляторы (gcc 4.5.1 в моем случае) фактически оптимизируют pow (r2,1.5) до r2 * sqrt (r2). - person wolfgang; 12.12.2011

Используйте быстрый обратный sqrt (возьмите функцию Q_rsqrt).

У вас есть:

float r2;
// ... r2 gets a value
float invsqrt = Q_rsqrt(r2);
float r3 = r2*r2*invsqrt; // x*x/sqrt(x) = x*sqrt(x)

ПРИМЕЧАНИЕ. Для double типов существует такая константа, как 0x5f3759df, которая может помочь вам написать функцию, которая обрабатывает также double типы данных.

ПОЗДНЕЕ РЕДАКТИРОВАНИЕ: похоже, что метод уже обсуждался здесь.

LATER EDIT2: константа для double была в ссылке:

Ломонт указал, что «магическим числом» для 64-битного типа IEEE754 size type double является 0x5fe6ec85e7de30da, но на самом деле оно близко к 0x5fe6eb50c7aa19f9.

person INS    schedule 09.12.2011
comment
@SideshowBob Поскольку он используется в коде Quake3, я считаю его самым быстрым - person INS; 09.12.2011
comment
С момента написания Quake 3 времена изменились. Например, SSE имеет аппаратную инструкцию sqrt, которая работает быстрее. - person Steve Jessop; 09.12.2011
comment
См. здесь для получения дополнительной информации. - person GManNickG; 09.12.2011
comment
@ IulianŞerbănoiu: он далеко не самый быстрый. Это быстрее, чем инструкция извлечения квадратного корня на некоторых платформах, но большинство основных процессоров могут вычислить более точную оценку обратного квадратного корня в одной инструкции, требующей задержки всего в пару циклов (rsqrtss на Intel, frsqrte на КПП, vrsqrte на ARM). - person Stephen Canon; 09.12.2011
comment
@SteveJessop Я полностью согласен, но OP ничего не упомянул об инструкциях HW, которые явно быстрее, чем эти битовые трюки. К сожалению, я не знаю, использует ли стандартная библиотека C функции SSE. - person INS; 09.12.2011
comment
Вопрос действительно раскрывает некоторые очень интересные факты. - person INS; 09.12.2011
comment
Идея r^2*r^2/sqrt(r^2) по-прежнему актуальна, независимо от того, используете ли вы трюк Quake или аппаратную 1/sqrt инструкцию. - person MSalters; 09.12.2011
comment
@drhirsch: и этот код обладает особенно желательным свойством неснижаемой нечитаемости: единственный способ сделать код читаемым - это заменить его совершенно другой реализацией, потому что используемый алгоритм в значительной степени непонятен. - person Steve Jessop; 09.12.2011
comment
ИМХО, если бы вам пришлось ограничить процессор до 4 математических операций FP, я бы выбрал + - * и обратный квадратный корень. - person phkahler; 09.12.2011