计算无平方根的向量单位
Calculate vector unit without square root
我需要计算任意向量的单位向量。线索是我不想引入浮点数错误,所以我用不起 sqrt() 函数。为了进行算术运算并且不引入浮点错误,我使用了 Rational 数据类型。
public class Vector3
{
public Rational X { get; set; }
public Rational Y { get; set; }
public Rational Z { get; set; }
public Vector3(Rational x, Rational y, Rational z)
{
X = x;
Y = y;
Z = z;
}
.....
我的矢量单位计算如下所示:
public Vector3 Unit()
{
if (this.Length() == 0)
return new Vector3(0, 0, 0);
return this.DividedBy(this.Length());
}
public Rational Length()
{
return Sqrt(Dot(this).ToDouble());
}
public Rational Dot(Vector3 a)
{
return this.X * a.X + this.Y * a.Y + this.Z * a.Z;
}
问题出在 Sqrt() 方法上,因为我必须将我的 Rational 转换为 double。此外,Sqrt() 生成无理数。所以我在 sqrt 函数的输入和输出中可能存在舍入误差。
所以我需要计算不带平方根的向量的长度。这可能吗?
抱歉,没有平方根是做不到的。您对舍入错误的担心被夸大了。
您可以通过这样的方式进行计算以尽量减少错误,例如(Java-ish 语法;JDK 中没有这样的 Math.max,但您得到想法):
public double magnitude(double x, double y, double z) {
double maxComponent = Math.max(Math.abs(x), Math.abs(y), Math.abs(z));
double rx = x/maxComponent;
double ry = y/maxComponent;
double rz = z/maxComponent;
return maxComponent*Math.sqrt(rx*rx + ry*ry + rz*rz);
}
如果您要解决的问题对浮点错误非常敏感(我之前处理过一些),您是否可以指定您愿意接受的容忍度?例如设置为输入数据集的精度?
假设这是可能的,我的建议是将 sqrt(x) 调用替换为已知在一定公差范围内准确的近似值。
例如,您可以将其近似为求根问题 X^2 - S = 0 的解。鉴于该函数严格递增并且在正半平面上表现良好,您可以实现说二分法或 Newton-Raphson 法并根据公差终止它。
即使它不能解决您的问题,也希望您觉得它有用。
不幸的是,您不能在这里做您想做的事,因为平方根通常(而且众所周知)是无理数。
如果您想坚持精确算术,您可以通过将高阶值保持原样,在一种扩展的有理数据结构中来处理它——一种代数几何的东西。此路径有其局限性,因为结果的多项式顺序通常是参数顺序的总和...
我认为 CGAL 提供了类似这样的选项。无论如何,如果您对几何数学感兴趣,但担心浮点数错误,那么看看 CGAL 应该是一种教育。
有时可以删除 sqrt:
对于通常需要单位向量的向量函数,您有时可以通过一些巧妙的数学运算来绕过 sqrt。矢量投影就是一个很好的例子。
用 sqrt 将向量 a 投影到向量 b 上:
- (a / sqrt( 点( b, b ) ) * b
在没有 sqrt 的情况下将向量 a 投影到向量 b 上:
- ( 点( a, b ) / 点( b, b ) ) * b
额外的点 (a,b) 消除了对 sqrt 的需要。
但是确实需要 3 次乘法和 2 次加法(对于向量 3),所以它不是免费的。
我需要计算任意向量的单位向量。线索是我不想引入浮点数错误,所以我用不起 sqrt() 函数。为了进行算术运算并且不引入浮点错误,我使用了 Rational 数据类型。
public class Vector3
{
public Rational X { get; set; }
public Rational Y { get; set; }
public Rational Z { get; set; }
public Vector3(Rational x, Rational y, Rational z)
{
X = x;
Y = y;
Z = z;
}
.....
我的矢量单位计算如下所示:
public Vector3 Unit()
{
if (this.Length() == 0)
return new Vector3(0, 0, 0);
return this.DividedBy(this.Length());
}
public Rational Length()
{
return Sqrt(Dot(this).ToDouble());
}
public Rational Dot(Vector3 a)
{
return this.X * a.X + this.Y * a.Y + this.Z * a.Z;
}
问题出在 Sqrt() 方法上,因为我必须将我的 Rational 转换为 double。此外,Sqrt() 生成无理数。所以我在 sqrt 函数的输入和输出中可能存在舍入误差。
所以我需要计算不带平方根的向量的长度。这可能吗?
抱歉,没有平方根是做不到的。您对舍入错误的担心被夸大了。
您可以通过这样的方式进行计算以尽量减少错误,例如(Java-ish 语法;JDK 中没有这样的 Math.max,但您得到想法):
public double magnitude(double x, double y, double z) {
double maxComponent = Math.max(Math.abs(x), Math.abs(y), Math.abs(z));
double rx = x/maxComponent;
double ry = y/maxComponent;
double rz = z/maxComponent;
return maxComponent*Math.sqrt(rx*rx + ry*ry + rz*rz);
}
如果您要解决的问题对浮点错误非常敏感(我之前处理过一些),您是否可以指定您愿意接受的容忍度?例如设置为输入数据集的精度?
假设这是可能的,我的建议是将 sqrt(x) 调用替换为已知在一定公差范围内准确的近似值。
例如,您可以将其近似为求根问题 X^2 - S = 0 的解。鉴于该函数严格递增并且在正半平面上表现良好,您可以实现说二分法或 Newton-Raphson 法并根据公差终止它。
即使它不能解决您的问题,也希望您觉得它有用。
不幸的是,您不能在这里做您想做的事,因为平方根通常(而且众所周知)是无理数。
如果您想坚持精确算术,您可以通过将高阶值保持原样,在一种扩展的有理数据结构中来处理它——一种代数几何的东西。此路径有其局限性,因为结果的多项式顺序通常是参数顺序的总和...
我认为 CGAL 提供了类似这样的选项。无论如何,如果您对几何数学感兴趣,但担心浮点数错误,那么看看 CGAL 应该是一种教育。
有时可以删除 sqrt:
对于通常需要单位向量的向量函数,您有时可以通过一些巧妙的数学运算来绕过 sqrt。矢量投影就是一个很好的例子。
用 sqrt 将向量 a 投影到向量 b 上:
- (a / sqrt( 点( b, b ) ) * b
在没有 sqrt 的情况下将向量 a 投影到向量 b 上:
- ( 点( a, b ) / 点( b, b ) ) * b
额外的点 (a,b) 消除了对 sqrt 的需要。 但是确实需要 3 次乘法和 2 次加法(对于向量 3),所以它不是免费的。