如何将零向量归一化

30
假设您有一个名为「normalize」的函数,它以数字列表(表示向量)作为输入,并返回已归一化的向量。当向量全为零或其组件的总和为零时,结果应该是什么?
10个回答

41

从数学角度来看,零向量无法被规范化。它的长度将始终为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


2
我认为一个小但重要的改进是提到它不能被规范化的实际原因是,最终需要尝试除以被规范化的零长度向量,这部分实际上是不可能/定义的。仍然赞同+1。 - code_dredd
2
“v=0的归一化形式确实是v=0。”这没有意义:正如您自己所说,零向量无法被归一化。 - Théophile
1
normalize(vector(0)) = vector(0)*(1/0) = vector(0)*NaN = NaN @Yuval Adam 另外需要注意的是,为了确保一致性,不应返回NaN或任何其他空值。向量v=0的归一化形式确实是v=0。 - user16829600

14

比Yuval所说的还要糟糕。

从数学上讲,给定一个向量x,你要寻找一个新的向量x/||x||

其中||.||是范数,你可能认为它是带有欧几里得范数的

||.|| = sqrt(dot(v,v)) = sqrt(sum_i x_i**2)

这些都是浮点数,所以仅仅防止除以零是不够的,如果x_i都很小(可能会下溢而失去幅度),也会出现浮点问题。

基本上,如果您真的需要能够正确处理小向量,那么您将需要做更多的工作。

如果在您的应用程序中小向量和零向量没有意义,则可以针对向量的幅度进行测试并执行适当的操作。

(请注意,一旦您开始处理浮点数,而不是实数,执行诸如平方然后取平方根或它们的和之类的操作在可表示范围的两端都存在问题)

底线:在所有情况下正确地进行数值计算比看起来更加棘手。

例如,以下是对这个(归一化)操作进行的天真方式的潜在问题:

  • 所有组件(x_i)都太小
  • 任何单个组件太大(超过可表示的最大平方根)都将返回无穷大。 这会逐个分量地削减可用的幅度。
  • 如果大分量与小分量的比值太大,则如果不小心,您可能会失去小分量的方向。
  • 等等。

6
从数学角度来说,零向量不能被归一化。这是计算几何中所谓的“退化情况”的一个例子,这是一个非常大的主题,给几何算法设计者带来了很多困扰。
以下是我可以想到的解决方法:
1. 对于零向量情况不做任何特殊处理。如果您的向量类型具有浮点类型的坐标,则结果中将得到零或无限坐标(由于除以零)。 2. 抛出一个“退化情况异常”。 3. 在您的过程中引入一个布尔型的“is_degenerate_case”输出参数。
个人而言,在我的代码中,我使用第三种方法。其中一个优点是它不会让程序员忘记处理退化情况。
请注意,由于浮点数的范围受限制,即使输入向量不等于零向量,您仍可能在输出向量中得到无限坐标。因此,我认为第一种方法并不是一个糟糕的设计决策。
我可以建议您避免抛出异常的解决方案。如果退化情况在其他情况下很少见,那么抛出异常不会减慢程序。但问题是,在大多数情况下,您无法知道退化情况是否罕见。

4

正如已经多次提到的那样,您无法对零向量进行归一化。因此,您有以下选择:

  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)

如果您选择选项a(返回0),那么对于您的结果的“正确性”会有什么影响? - Graham P Heath
一个额外的位很容易被忽略,调用者可能只会执行 myVec.normalize(&result); 并丢弃 success 位。一个好的方法是依赖于“语言本地的0/0方式”,即, vector normalize(){ | if(this->length()==0)return vector(0/0); | // ...计算... | } - user16829600

3
几乎就像0/0一样。应该抛出异常或返回NaN。

0

(0,0,0) 应该是 (0,0,0) 归一化加上一个警告(或异常)也许。
从数学上讲,我想它是没有定义的。


当然。它没有数学定义,因为零向量是没有方向的。 - user16829600

0

嗯,你必须除以零,而这是不可能的,所以我认为大多数编程语言都会有某种NaN值。

参考资料:

  • XNA
  • Apple(你还必须选择一个任意方向的向量)
  • Blender(使用Python)

0

零向量已经被规范化,根据我遇到的任何矢量范数定义,这就是一个已处理的情况。

至于分量之和为零的向量-好吧,这取决于您使用的范数定义。 对于普通的L2范数(原点和向量之间的欧几里德距离),计算规范化向量的标准公式应该可以正常工作,因为它首先平方了每个分量。


0向量并未被归一化;它的长度为0,而不是归一化向量所要求的1。 - user16829600

0

给定一个向量v,将其归一化意味着保持其方向并通过乘以一个精心选择的因子使其成为单位长度。

对于零向量来说,这显然是不可能的,因为它实际上没有方向,或者因为它的长度不能通过乘以某个因子来改变(它总是等于零)。

我建议,无论您想要使用向量的任何过程,并且需要将该向量归一化,都不适用于零向量。


-2

这完全取决于你如何定义“归一化”。该术语的一个可能扩展是说,此操作的结果是任何单位长度向量(我在这里主要使用(1,0,0))。例如,当您需要将归一化返回到给定点的圆边界方向时,这非常有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接