Подтвердить что ты не робот

Алгоритм быстрого алгоритма Hypotenuse для встроенного процессора?

Есть ли умный/эффективный алгоритм определения гипотенузы угла (т.е. sqrt(a² + b²)), используя математику с фиксированной точкой во встроенном процессоре без аппаратного умножения?

4b9b3361

Ответ 1

Если вы делаете это при > 1 кГц, умножить даже на MCU без аппаратного обеспечения MUL не страшно. Что еще хуже, это sqrt. Я попытался бы изменить мое приложение, поэтому его вообще не нужно вычислять.

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

Ресурсы AVR

Ответ 2

Если результат не должен быть особенно точным, вы можете получить сырую нефть приближение довольно просто:

Возьмите абсолютные значения a и b и поменяйте, если необходимо, чтобы у вас a <= b. Тогда:

h = ((sqrt(2) - 1) * a) + b

Чтобы интуитивно понять, как это работает, рассмотрите способ построения мелкоугольной линии на пиксельном дисплее (например, с использованием алгоритма Брешенема). Это выглядит примерно так:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | | | | | | | | | | | | | | | |*|*|*|    ^
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |
| | | | | | | | | | | | |*|*|*|*| | | |    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |
| | | | | | | | |*|*|*|*| | | | | | | | a pixels
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |
| | | | |*|*|*|*| | | | | | | | | | | |    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+    |
|*|*|*|*| | | | | | | | | | | | | | | |    v
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 <-------------- b pixels ----------->

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

Идеальная линия от одного конца до другого может быть аппроксимирована путем, который соединяет центр каждого пикселя с центром соседнего. Это серия a сегментов длины sqrt(2) и b-a сегментов длины 1 (взятие пикселя в качестве единицы измерения). Следовательно, приведенная выше формула.

Это явно дает точный ответ для a == 0 и a == b; но дает переоценку значений между ними.

Ошибка зависит от отношения b/a; максимальная ошибка возникает, когда b = (1 + sqrt(2)) * a и оказывается 2/sqrt(2+sqrt(2)), или около 8,24% над истинным значением. Это не очень хорошо, но если это достаточно хорошо для вашего приложения, этот метод имеет то преимущество, что он прост и быстр. (Умножение на константу можно записать в виде последовательности сдвигов и добавляет.)

Ответ 3

Для записи здесь приведены еще несколько приближений, возрастающий порядок сложности и точности. Все это предполагает, что 0 ≤ a ≤ b.

  • h = b + 0.337 * a // max error ≈ 5.5 %
  • h = max(b, 0.918 * (b + (a>>1))) // max error ≈ 2.6 %
  • h = b + 0.428 * a * a / b // max error ≈ 1.04 %

Изменить: ответить на вопрос Эрира Ханы, вот как я получил эти приближения.

Первый шаг. Приближение функции двух переменных может быть сложная проблема. Таким образом, я впервые превратил это в проблему аппроксимируя функцию одной переменной. Это можно сделать, выбирая самая длинная сторона как "масштабный" коэффициент, следующим образом:

h = √ (b 2 + a 2)
  = b √ (1 + (a/b) 2)
  = b f (a/b)    , где f (x) = √ (1 + x 2)

Добавление ограничения 0 ≤ a ≤ b означает, что мы имеем дело только с аппроксимируя f (x) в интервале [0, 1].

Ниже приведен график f (x) в соответствующем интервале, вместе с аппроксимация, приведенная Мэтью Слэттери (а именно (√2-1) x + 1).

Function to approximate

Второй шаг. Следующий шаг - посмотреть на этот сюжет, задавая сам вопрос: "Как я могу приблизить эту функцию дешево?". Поскольку кривая выглядит грубо параболической, моя первая идея заключалась в том, чтобы использовать квадратичная функция (третье приближение). Но так как это все еще относительно дорого, я также посмотрел на линейные и кусочно-линейные приближения. Вот мои три решения:

Three approximations

Численные константы (0.337, 0.918 и 0.428) первоначально были свободны параметры. Конкретные значения были выбраны для минимизации максимальная абсолютная ошибка приближений. Минимизация могла конечно, нужно сделать с помощью некоторого алгоритма, но я просто сделал это "вручную", построение абсолютной ошибки и настройку константы до тех пор, пока она не будет сведено к минимуму. На практике это работает довольно быстро. Написание кода для автоматизировать это заняло бы больше времени.

Третий шаг - вернуться к исходной проблеме аппроксимации функция двух переменных:

  • h ≈ b (1 + 0,337 (a/b)) = b + 0.337 a
  • h ≈ b max (1, 0,918 (1 + (a/b)/2)) = max (b, 0,918 (b + a/2))
  • h ≈ b (1 + 0,428 (a/b) 2) = b + 0,428 a 2/b

Ответ 4

Рассмотрим методы CORDIC. У доктора Добба есть статья и связанный с ней источник библиотеки здесь. Квадратный корень, умножение и деление рассматриваются в конце статьи.

Ответ 5

Одна возможность выглядит так:

#include <math.h>

/* Iterations   Accuracy
 *  2          6.5 digits
 *  3           20 digits
 *  4           62 digits
 * assuming a numeric type able to maintain that degree of accuracy in
 * the individual operations.
 */
#define ITER 3

double dist(double P, double Q) {
/* A reasonably robust method of calculating `sqrt(P*P + Q*Q)'
 *
 * Transliterated from _More Programming Pearls, Confessions of a Coder_
 * by Jon Bentley, pg. 156.
 */

    double R;
    int i;

    P = fabs(P);
    Q = fabs(Q);

    if (P<Q) {
        R = P;
        P = Q;
        Q = R;
    }

/* The book has this as:
 *  if P = 0.0 return Q; # in AWK
 * However, this makes no sense to me - we've just insured that P>=Q, so
 * P==0 only if Q==0;  OTOH, if Q==0, then distance == P...
 */
    if ( Q == 0.0 )
        return P;

    for (i=0;i<ITER;i++) {
        R = Q / P;
        R = R * R;
        R = R / (4.0 + R);
        P = P + 2.0 * R * P;
        Q = Q * R;
    }
    return P;
}

Это все еще делает пару делений и четыре мультипликатора на итерацию, но вам редко требуется больше трех итераций (а два - достаточно адекватных) для каждого ввода. По крайней мере, с большинством процессоров, которые я видел, это будет, как правило, быстрее, чем sqrt будет само по себе.

На данный момент он написан для double s, но если вы выполнили основные операции, преобразование его в работу с фиксированной точкой не должно быть ужасно трудным.

Ответ 6

Вы можете начать с переоценки, если вам нужен sqrt вообще. Много раз вы вычисляете гипотенузу, чтобы сравнить ее с другим значением - если вы сравните значение, которое вы сравниваете, вы можете полностью исключить квадратный корень.

Ответ 7

Возможно, вы могли бы использовать некоторые из Elm Chans Ассемблерные библиотеки и адаптировать ihypot-функцию к вашему ATtiny. Вам нужно будет заменить MUL и, возможно, (я еще не проверил) некоторые другие инструкции.