【问题标题】:How do you normalize a zero vector你如何归一化零向量
【发布时间】:2010-10-17 20:23:00
【问题描述】:

假设您有一个函数“normalize”,它将一个数字列表(表示一个向量)作为输入并返回归一化的向量。当向量全为零或分量之和为零时,结果应该是什么?

【问题讨论】:

    标签: language-agnostic math vector


    【解决方案1】:

    从数学上讲,零向量不能归一化。它的长度将始终保持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

    【讨论】:

    • 我认为一个小但重要的改进是提到它不能被归一化的实际原因是,最后,试图除以被归一化的向量的零长度将是必需的,这是实际上不可能/定义的部分。不过,+1
    • "v=0 的归一化形式确实是 v=0。"这没有意义:正如您自己所说,零向量无法归一化。
    • normalize(vector(0)) = vector(0)*(1/0) = vector(0)*NaN = NaN @Yuval Adam Also important to note that to ensure consistency, you should not return NaN or any other null value. The normalized form of v=0 is indeed v=0.
    【解决方案2】:

    这比 Yuval 建议的还要糟糕。

    在数学上,给定一个向量 x,你正在寻找一个新的向量 x/||x||

    在哪里||。||是范数,你可能认为它是欧几里得范数,带有

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

    这些是浮点数,因此仅仅防止除以零是不够的,如果 x_i 都很小(它们可能下溢并且您失去幅度),您也会遇到浮点问题。

    基本上这一切都归结为,如果你真的需要能够正确处理小向量,你将不得不做更多的工作。

    如果小向量和零向量在您的应用程序中没有意义,您可以测试向量的大小并做一些适当的事情。

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

    底线:在所有情况下正确进行数值运算比看起来要复杂。

    例如,以一种幼稚的方式完成此(规范化)操作的潜在问题让我一头雾水

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

    【讨论】:

      【解决方案3】:

      从数学上讲,零向量不能归一化。这是我们在计算几何中所说的“退化情况”的一个例子,这是一个巨大的话题,让几何算法设计者非常头疼。 我可以想象以下解决问题的方法。

      1. 你没有对零向量的情况做任何特别的事情。如果您的向量类型具有浮点类型坐标,那么您将在结果中得到零坐标或无限坐标(由于除以零)。
      2. 你扔了一个degenerate_case_exception
      3. 您在过程中引入了一个布尔值is_degenerate_case 输出参数。

      我个人在我的代码中到处都使用 3 方法。它的优点之一是它不会让程序员忘记处理退化的情况。

      注意,由于浮点数的范围有限,即使输入向量不等于零向量,在输出向量中仍然可能得到无限坐标。因此,我不认为 1. 方法是一个糟糕的设计决策。

      我可以向您推荐的是避免抛出异常的解决方案。如果退化情况在其他情况下很少见,那么抛出异常不会减慢程序的速度。但问题是,在大多数情况下,你不知道退化的情况会很少见。

      【讨论】:

        【解决方案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),您的结果是否会“正确”?
        • 一个额外的位很容易被忽略,调用者可能只是执行myVec.normalize(&result); 并丢弃success 位。一个好的方法是依靠“0/0的语言原生方式”,即vector normalize(){ | if(this->length()==0)返回向量(0/0); | // ...计算它... | }
        【解决方案5】:

        很像 0/0。应该抛出异常或返回 NaN。

        【讨论】:

          【解决方案6】:

          (0,0,0) 应该是 (0,0,0) 标准化加上警告(或异常)可能。
          我猜在数学上它没有定义。

          【讨论】:

          • 当然。它没有在数学上定义,因为 0 向量是无方向的。
          【解决方案7】:

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

          参考资料:

          • XNA
          • Apple(您还必须为向量选择任意方向)
          • Blender(使用 Python)

          【讨论】:

            【解决方案8】:

            在我遇到过的向量范数的任何定义下,零向量已经被归一化,所以这是处理的一种情况。

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

            【讨论】:

            • 0 向量未归一化;它的长度是 0 而不是归一化向量属性要求的 1
            【解决方案9】:

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

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

            我建议,无论您想将向量用于什么过程,并且需要对该向量进行归一化,对于零向量都没有明确定义。

            【讨论】:

              【解决方案10】:

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

              【讨论】:

                猜你喜欢
                • 2013-04-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2013-03-30
                • 2016-05-08
                • 2011-02-20
                • 1970-01-01
                • 2018-04-09
                相关资源
                最近更新 更多