【IT168 技术】使用 Java 开发移动设备应用程序时,可能需要用到特定 Java VM 所没有的数学方法。本文将专门解决 Java ME 没有“幂”方法 Math.pow() 的问题。我们将演示使用三种不同的方法开发同一个 ME 应用程序,并从中选出非常好的的编程解决方案。
要讨论此问题,我们先考察整数和分数幂参数,将我们的分析限于正实数。我们将演示求整数问题和小数问题的解集相对而言比较容易(而不考虑指数的符号)。在大多数情况下,我们将使用示例问题 n = 82/3,其中我们会求出 n 的良好估计或实际解。如果初始指数事先不可用,则此问题的其他解(包括牛顿法和割线法)不易编程。虽然二分法是可行的解决方案,但我们将关注传统上不为人所探究的三个方法。第一个是简单的(不过有时效率低下)几何衰变算法;而第二个方法将利用 Math.sqrt() 方法并保证在不超过 11 次迭代中收敛到一个近似解。第三个方法将使用泰勒级数逼近法求对数并对泰勒级数进行欧拉转换。
产生整数解的 ME Math.pow() 方法
传统上,Java Math.pow() 方法包含两个参数。这两个参数包括底数和指数。我们假定(最初)这两个参数均为整数,然后求出 ME 中与 Java 方法使用相同参数的 Math.pow() 方法的可编程解。此处,可编程解相当简单,如示例 1 所示。在本例中,我们仅运行以指数值为指标的倍乘循环。
示例 1
base x and power y (i.e., x^y)*/
{
int z = x;
for( int i = 1; i < y; i++ )z *= x;
return
}
当然,有人可能会发现需要求出非整数幂的值。正实数的简单解(无需访问 Math.pow() 方法)可能涉及使用 Math.log()。例如,请考虑 82/3 的情况。利用 2/3*ln(8) = 1.386294361 中自然对数的结果。要得到最终解,需要利用指数 1.386294361(特别指出 e1.386294361 = 4)。在这种情况下,可能不需要使用幂函数。遗憾的是,Java ME 也不支持 Math.log() 方法。没有 Math.pow() 或 Math.log() 方法时,我们会考虑使用朴素的“强力”试探性方法,应用 Math.sqrt() 方法以及自然对数(和欧拉 e)的泰勒级数逼近来求得 Java ME 问题的解。
使用几何衰变算法作为强力解的 ME Math.pow()
Java ME 的早期实现包括浮点主数据类型 float 和 double。最近,已添加了这些类型。现在我们将 Math.pow() 声明中的整型参数替换为 double 数据类型。
可能需要在 Java ME Math.pow() 幂方法中使用小数指数。我们提供的生成 Math.pow() 的第一种方法是使用几何衰变算法的朴素的“强力”试探性方法。简单而言,衰变算法以一个大于已知解的值开始,然后应用某个方法来衰变该值,直到该值非常逼近该解(有关简单线性衰变算法的演示,请参见示例 2)。在我们的例子中,将进一步演示向上述解收敛的几何形式。
示例 2
converges to our desired solution (a positive integer) */
int n; // assume that n is the solution to the number we are trying to find
int varX = 1000; //assume that we know the solution is less than or equal to 1000
while( varX > 0 )
{
varX -= 1; // decrement by 1
if( varX == n)return varX;
}
在示例 2 中,我们从 1000 开始递减,直到找到预期的数字,假定预期数字是一个正整数。这种类型的算法构成了强力试探性方法的基础。
使用类似的方法,我们可在遇到小数时应用此算法。假定我们需要求出 n 的值,其中 n = 82/3。要使用衰变算法,我们必须首先找到一个合适的起点,该点要等于或大于解本身。这对于带有正指数的正实数很容易做到。对于我们的示例,要对此解进行编程,对方法两边求立方,得到 n3=82 。当然,此方程与 n3=64 等效。之后,我们的起始值将变为 64,我们知道 n 必须小于 64(因为 n3 = 64)。注意,如果限于正实数,则此推导方法同样适用于任何正指数值。现在,我们可能需要设计一个循环来产生 n 的“充分接近”预期数字的解。我们再来看示例 3,它适合于所有正底数和正指数。
示例 3
{
int den = 1000; // specify arbitrary denominator
int num = (int)(y*den); // find numerator
int s = (num/den)+1;
/***********************************************************************
** Variable 's' provides the power for which we multiply the base to find
** our starting search value. For example, if we seek a solution for
** n = 8^(2/3), then we will use 8^2 or 64 as our starting value (which is
** generated in our next section of code.) Why? The solution for our
** problem (given that the base is positive) will always be less than or
** equal to the base times the numerator power.
************************************************************************/
/***********************************************************************
** Because we set the denominator to an arbitrary high value,
** we must attempt to reduce the fraction. In the example below,
** we find the highest allowable fraction that we can use without
** exceeding the limitation of our primitive data types.
************************************************************************/
double z = Double.MAX_VALUE;
while( z >= Double.MAX_VALUE )
{
den -=1; // decrement denominator
num = (int)(y*den); // find numerator
s = (num/den)+1; // adjust starting value
// find value of our base number to the power of numerator
z = x;
for( int i = 1; i < num; i++ )z *= x;
}
/***********************************************************************
** Now we are going to implement the decay algorithm to find
** the value of 'n'.
************************************************************************/
/***********************************************************************
** We now find 'n' to the power of 's'. We will then decrement 'n',
** finding the value of 'n' to the power of the denominator. This
** value, variable 'a', will be compared to 'z'. If the 'a' is nearly
** equal to 'z', then we will return 'n', our desired result.
************************************************************************/
double n = x; // We define 'n' as our return value (estimate) for 'x'.
// find 'n' to the power of 's'.
for( int i = 1; i < s; i++)n *= x;
// Begin decay loop
while( n > 0 )
{
double a = n; //proxy for n
// find 'a', the value of 'n' to the power of denominator
for( int i = 1; i < den; i++ )a *= n;
// compare 'a' to 'z'. Is the value within the hundred-thousandth?
// if so, return 'n'.
double check1 = a-z;
double check2 = z-a;
if( check1 < .00001|| check2 > .00001 ) return n;
n *= .999;// We arbitrarily use a decay of .1% per iteration
}
// value could not be found, return -1.
return -1.0;
}
本示例演示了衰变算法的使用方法。您会注意到,n 的值(解的估计值)将按 1% 强制递减。您可能需要根据编程精度要求来改变此值。也可能考虑包括编程逻辑,该逻辑用于将前一迭代解与当前迭代进行比较,然后,如果有改善继续进行迭代,但是,如果解已回归,则返回前一个值。