所以显然计算平方根不是很有效率,这让我想知道找出两个圆之间的距离(我称之为范围)的最佳方法是什么?
通常情况下我会解决:
a^2 + b^2 = c^2
dy^2 + dx^2 = h^2
dy^2 + dx^2 = (r1 + r2 + range)^2
(dy^2 + dx^2)^0.5 = r1 + r2 + range
range = (dy^2 + dx^2)^0.5 - r1 - r2
当你只是在“范围”为0时寻找碰撞的情况时,试图避免平方根工作正常:
if ( (r1 + r2 + 0 )^2 > (dy^2 + dx^2) )
但是,如果我试图计算出距离范围,我最终会得到一些笨拙的等式:
range(range + 2r1 + 2r2) = dy^2 + dx^2 - (r1^2 + r2^2 + 2r1r2)
这不会去任何地方。至少我不知道如何从这里解决范围......
显而易见的答案是trignometry并首先找到theta:
Tan(theta) = dy/dx
theta = dy/dx * Tan^-1
然后找到hypotemuse
Sin(theta)= dy / h
h = dy / Sin(theta)
最后确定范围 范围+ r1 + r2 = dy / Sin(theta) range = dy / Sin(theta) - r1 - r2
这就是我所做的,并且有一个看起来像这样的方法:
private int findRangeToTarget(ShipEntity ship, CircularEntity target){
//get the relevant locations
double shipX = ship.getX();
double shipY = ship.getY();
double targetX = target.getX();
double targetY = target.getY();
int shipRadius = ship.getRadius();
int targetRadius = target.getRadius();
//get the difference in locations:
double dX = shipX - targetX;
double dY = shipY - targetY;
// find angle
double theta = Math.atan( ( dY / dX ) );
// find length of line ship centre - target centre
double hypotemuse = dY / Math.sin(theta);
// finally range between ship/target is:
int range = (int) (hypotemuse - shipRadius - targetRadius);
return range;
}
所以我的问题是,使用tan和sin比找到平方根更有效吗?
我可能能够重构我的一些代码来从另一个方法中获取theta值(我必须解决这个问题)是否值得做?
或者还有另一种方式吗?
请原谅我,如果我问的是显而易见的,或者犯了任何基本的错误,我用了很多高中数学做了很长时间......
欢迎任何提示或建议!
**** **** EDIT
具体而言,我正在尝试在游戏中创建一个“扫描仪”设备,以检测敌人/障碍物何时接近/离开等。扫描仪将通过音频音调或图形条等传递此信息。因此,虽然我不需要确切的数字,但理想情况下我想知道:
我希望有一些聪明的优化/近似可能(dx + dy +(更长的dx,dy?),但有了所有这些要求,也许不是......
答案 0 :(得分:12)
Math.hypot
旨在更快,更准确地计算sqrt(x^2 + y^2)
形式。所以这个应该只是
return Math.hypot(x1 - x2, y1 - y2) - r1 - r2;
我无法想象任何比这更简单的代码,也不会更快。
答案 1 :(得分:9)
如果你真的需要准确的距离,那么你就无法真正避开平方根。三角函数至少与平方根计算一样糟糕,如果不是更糟的话。
但是,如果您只需要近似距离,或者如果您只需要相对距离来进行各种圆圈组合,那么您肯定可以做些事情。例如,如果您只需要相对距离,请注意平方数与平方根的关系大于关系。如果您只是比较不同的对,请跳过平方根步骤,您将得到相同的答案。
如果您只需要近似距离,那么您可能会认为h
大致等于较长的相邻边。这种近似值永远不会超过两倍。或者你可以使用三角函数的查找表 - 这比任意平方根的查找表更实用。
答案 2 :(得分:1)
我是否厌倦了在使用tan,sine时是否与我们使用sqrt函数时的答案相同。
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
double shipX = 5;
double shipY = 5;
double targetX = 1;
double targetY = 1;
int shipRadius = 2;
int targetRadius = 1;
//get the difference in locations:
double dX = shipX - targetX;
double dY = shipY - targetY;
// find angle
double theta = Math.toDegrees(Math.atan( ( dY / dX ) ));
// find length of line ship centre - target centre
double hypotemuse = dY / Math.sin(theta);
System.out.println(hypotemuse);
// finally range between ship/target is:
float range = (float) (hypotemuse - shipRadius - targetRadius);
System.out.println(range);
hypotemuse = Math.sqrt(Math.pow(dX,2) + Math.pow(dY,2));
System.out.println(hypotemuse);
range = (float) (hypotemuse - shipRadius - targetRadius);
System.out.println(range);
}
我得到的答案是: 4.700885452542996
1.7008854
5.656854249492381
2.6568542
现在看来,sqrt值与更正确的值之间存在差异。
我计算了表演时间 - 以:
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
long lStartTime = new Date().getTime(); //start time
double shipX = 555;
double shipY = 555;
double targetX = 11;
double targetY = 11;
int shipRadius = 26;
int targetRadius = 3;
//get the difference in locations:
double dX = shipX - targetX;
double dY = shipY - targetY;
// find angle
double theta = Math.toDegrees(Math.atan( ( dY / dX ) ));
// find length of line ship centre - target centre
double hypotemuse = dY / Math.sin(theta);
System.out.println(hypotemuse);
// finally range between ship/target is:
float range = (float) (hypotemuse - shipRadius - targetRadius);
System.out.println(range);
long lEndTime = new Date().getTime(); //end time
long difference = lEndTime - lStartTime; //check different
System.out.println("Elapsed milliseconds: " + difference);
}
答案 - 639.3204215458475, 610.32043, 经过的毫秒:2
当我们尝试使用sqrt root one时:
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
long lStartTime = new Date().getTime(); //start time
double shipX = 555;
double shipY = 555;
double targetX = 11;
double targetY = 11;
int shipRadius = 26;
int targetRadius = 3;
//get the difference in locations:
double dX = shipX - targetX;
double dY = shipY - targetY;
// find angle
double theta = Math.toDegrees(Math.atan( ( dY / dX ) ));
// find length of line ship centre - target centre
double hypotemuse = Math.sqrt(Math.pow(dX,2) + Math.pow(dY,2));
System.out.println(hypotemuse);
float range = (float) (hypotemuse - shipRadius - targetRadius);
System.out.println(range);
long lEndTime = new Date().getTime(); //end time
long difference = lEndTime - lStartTime; //check different
System.out.println("Elapsed milliseconds: " + difference);
}
答案 - 769.3321779309637, 740.33215, 经过的毫秒:1
现在,如果我们检查差异,两个答案之间的差异也很大。
因此我想说,如果你让游戏更准确,那么数据对用户来说会更有趣。
答案 3 :(得分:1)
“硬”几何软件中通常带有sqrt
的问题不是它的性能,而是随之而来的精度损失。在你的情况下,sqrt
很适合这个账单。
如果您发现sqrt
确实会带来性能损失 - 您知道,只在需要时进行优化 - 您可以尝试使用线性近似值。
f(x) ~ f(X0) + f'(x0) * (x - x0)
sqrt(x) ~ sqrt(x0) + 1/(2*sqrt(x0)) * (x - x0)
因此,您计算sqrt
的查找表(LUT),并且给定x
,使用最近的x0
。当然,这会限制您的可能范围,当您应该回退到常规计算时。现在,一些代码。
class MyMath{
private static double[] lut;
private static final LUT_SIZE = 101;
static {
lut = new double[LUT_SIZE];
for (int i=0; i < LUT_SIZE; i++){
lut[i] = Math.sqrt(i);
}
}
public static double sqrt(final double x){
int i = Math.round(x);
if (i < 0)
throw new ArithmeticException("Invalid argument for sqrt: x < 0");
else if (i >= LUT_SIZE)
return Math.sqrt(x);
else
return lut[i] + 1.0/(2*lut[i]) * (x - i);
}
}
(我没有测试此代码,请原谅并更正任何错误)
此外,在完成所有这些之后,可能已经有一些近似,高效的替代数学库。您应该寻找它,但如果您发现性能确实是必要的话,只能 。