Как вы нормализуете нулевой вектор - PullRequest
25 голосов
/ 06 апреля 2009

Предположим, у вас есть функция 'normalize', которая принимает список чисел (представляющих вектор) в качестве входных данных и возвращает нормализованный вектор. Каким должен быть результат, когда вектор - все нули или сумма его компонентов равна нулю?

Ответы [ 10 ]

32 голосов
/ 06 апреля 2009

Математически говоря, нулевой вектор не может быть нормализован. Его длина всегда останется 0.

Для данного вектора v = (v1, v2, ..., vn) имеем: ||v|| = sqrt(v1^2 + v2^2 + ... + vn^2). Давайте вспомним, что нормализованный вектор - это вектор с ||v||=1.

Итак, для v = 0 имеем: ||0|| = sqrt(0^2 + 0^2 + ... + 0^2) = 0. Вы никогда не сможете это нормализовать.

Также важно отметить, что для обеспечения согласованности не следует возвращать NaN или любое другое нулевое значение. Нормализованная форма v=0 действительно v=0.

12 голосов
/ 06 апреля 2009

Это даже хуже, чем предлагает Ювал.

Математически, для заданного вектора x вы ищете новый вектор x / || x ||

где ||. || это норма, о которой вы, вероятно, думаете как о евклидовой норме с

|| ||. = sqrt (точка (v, v)) = sqrt (сумма_i x_i ** 2)

Это числа с плавающей запятой, поэтому недостаточно просто защититься от деления на ноль, у вас также есть проблема с плавающей запятой, если x_i все маленькие (они могут снизиться, и вы потеряете величину).

По сути, все сводится к тому, что если вам действительно нужно уметь правильно обрабатывать небольшие векторы, вам придется проделать еще некоторую работу.

Если малые и нулевые векторы не имеют смысла в вашем приложении, вы можете проверить величину вектора и сделать что-то подходящее.

(обратите внимание, что как только вы начинаете работать с числами с плавающей запятой, а не с действительными числами, делать такие вещи, как возведение в квадрат, а затем квадратные числа с корнем (или их суммы) становится проблематичным как для больших, так и для малых концов представимого диапазона )

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

Например, с головы до головы потенциальные проблемы с этой (нормализацией) операцией, выполненной наивным способом

  • все компоненты (x_i) слишком малы
  • любой слишком большой компонент (выше квадратного корня из представимого max) вернет бесконечность. Это сокращает доступные величины компонентно на sqrt.
  • если отношение большого компонента к маленькому компоненту слишком велико, вы можете эффективно потерять направление мелких компонентов, если не будете осторожны
  • и т.д..
5 голосов
/ 26 мая 2009

Математически говоря, нулевой вектор не может быть нормализован. Это пример того, что мы называем в вычислительной геометрии «вырожденным случаем», и это огромная тема, которая создает большую головную боль для разработчиков геометрических алгоритмов. Я могу представить следующие подходы к проблеме.

  1. Вы не делаете ничего особенного в случае нулевого вектора. Если ваш векторный тип имеет координаты с плавающей точкой, вы получите нулевые или бесконечные координаты в результате (из-за деления на ноль).
  2. Вы бросаете degenerate_case_exception.
  3. Вы вводите логический is_degenerate_case выходной параметр в вашу процедуру.

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

Обратите внимание, что из-за ограниченного диапазона чисел с плавающей запятой, даже если входной вектор не равен нулевому вектору, вы все равно можете получить бесконечные координаты в выходном векторе. Из-за этого я не считаю 1. подход плохим проектным решением.

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

4 голосов
/ 06 апреля 2009

Как уже упоминалось несколько раз, вы не можете нормализовать нулевой вектор. Итак, ваши варианты:

  1. Вернуть нулевой вектор
  2. Возврат NaN
  3. Возвращает бит, указывающий, был ли вектор успешно нормализован, в дополнение к результату в случае успеха
  4. Брось исключение

Вариант 4 не очень хорош, потому что некоторые языки (такие как C) не имеют исключений, и нормализация вектора обычно встречается в коде очень низкого уровня. Создание исключения довольно дорого, и любой код, который может захотеть обработать случай с нулевым / малым вектором, получит ненужный удар по производительности, когда это произойдет.

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

Вариант 2 имеет проблему, аналогичную варианту 1, но, поскольку NaN обычно намного более заметны, чем нули, он, вероятно, проявится легче.

Я думаю, что вариант 3 - лучшее решение, хотя он и делает интерфейс более сложным. Вместо того чтобы говорить

vec3 = myVec.normalize();

Теперь вы должны сказать что-то вроде

vec3 result;
bool success = myVec.normalize(&result);
if(success)
    // vector was normalized
else
    // vector was zero (or small)
3 голосов
/ 06 апреля 2009

Очень похоже на 0/0. Должен выбросить исключение или вернуть NaN.

1 голос
/ 06 апреля 2009

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

Что касается вектора с компонентами, сумма которых равна нулю - это зависит от того, какое определение нормы вы используете. С простой старой L2-нормой (евклидовым расстоянием между началом и вектором) стандартная формула для вычисления нормализованного вектора должна работать нормально, поскольку она сначала возводит в квадрат отдельные компоненты.

0 голосов
/ 28 декабря 2016

Все зависит от того, как вы определяете «нормализовать». Одно из возможных расширений этого термина состоит в том, чтобы сказать, что результатом этой операции является любой вектор длины единицы (здесь я в основном использую (1, 0, 0)). Это полезно, например, когда требуется нормализация для возврата направления к границе круга из заданной точки.

0 голосов
/ 06 апреля 2009

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

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

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

0 голосов
/ 06 апреля 2009

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

Ссылки:

  • XNA
  • Apple (вы также должны выбрать произвольное направление для вектора)
  • Блендер (с использованием Python)
0 голосов
/ 06 апреля 2009

(0,0,0) должно быть (0,0,0) нормализовано плюс может быть предупреждение (или исключение).
математически это не определено, я думаю.

...