Java ME 環境缺乏 Math.pow()、Math.log() 等數學函式,開發行動裝置應用時需自行實作冪運算。本文提出三種演算法——幾何衰減法、Math.sqrt() 迭代法、泰勒級數逼近法——並以實測數據比較精度與效能,助您選擇最佳方案。
標準 Math.pow(x, y) 接受底數 x 與指數 y。若限定整數參數,實作極簡單:
int pow(int x, int y) {
int z = x;
for (int i = 1; i < y; i++) z *= x;
return z;
}
但實際常需非整數指數(如 8^(2/3))。若有 Math.log() 可用 exp(y * ln(x)) 解決,但 Java ME 兩者皆無,須另闢蹊徑。
核心思想:從一個大於真解的初始值開始,以固定比率(如 0.999)遞減,直到逼近目標值。
y 轉為分數 num/den(分母設大值如 1000)。s = (num/den) + 1,計算 z = x^num 作為上界(如 8^2 = 64)。n 從 x 開始衰減,每輪計算 a = n^den,若 |a - z| < 0.00001 則回傳 n。x = 1/x; y = -y 轉為正指數問題。double pow(double x, double y) {
int den = 1000, num = (int)(y * den), s = (num/den) + 1;
double z = x; for (int i=1; i<num; i++) z *= x; // 計算 x^num
double n = x; for (int i=1; i<s; i++) n *= x; // 初始估計值
while (n > 0) {
double a = n; for (int i=1; i<den; i++) a *= n; // n^den
if (Math.abs(a - z) < 0.00001) return n;
n *= 0.999; // 0.1% 衰減率
}
return -1;
}
缺點:大底數/大分子時迭代次數極多(如 8^(5/2) 需 5196 次),效能差。
將指數轉為分母為 2 的冪次分數(如 1024),反覆開平方根即可快速收斂。
den = 1024(2^10),分子 num = (int)(y * den)。n = x^num,若溢位則縮減分母/迭代次數。n 連續呼叫 Math.sqrt() 10 次(對應 2^10),得到最終估計值。double pow(double x, double y) {
int den = 1024, num = (int)(y * den), iterations = 10;
double n = Double.MAX_VALUE;
while (n >= Double.MAX_VALUE && iterations > 1) {
n = x; for (int i=1; i<num; i++) n *= x;
if (n >= Double.MAX_VALUE) { iterations--; den /= 2; num = (int)(y * den); }
}
for (int i=0; i<iterations; i++) n = Math.sqrt(n);
return n;
}
優勢:固定 ≤11 次迭代收斂,精度通常達小數點後 3 位,效能大幅優於衰減法。
利用數學恆等式 x^y = exp(y * ln(x)),分別以泰勒級數逼近 ln(x) 與 exp()。
ln(x) = (x-1) - (x-1)^2/2 + (x-1)^3/3 - ... (|x-1| ≤ 1)ln(x) = 1/(x/(x-1)) + 1/(x/(x-1))^2 + ... (|x| > 1)exp(z) = 1 + z + z^2/2! + z^3/3! + ...ln(x),乘以指數 y 得 ln(result),再代入 exp 級數。double pow(double a, double b) {
boolean gt1 = Math.abs(a-1) > 1;
int oc = -1, iter = 20;
double x = gt1 ? a/(a-1) : a-1;
double sumX = gt1 ? 1/x : x;
for (int i=2; i<iter; i++) {
double p = x; for (int j=1; j<i; j++) p *= x;
double term = gt1 ? 1/(i*p) : (p/i) * oc;
sumX += term; oc *= -1;
}
double x2 = b * sumX;
double sumY = 1 + x2;
for (int i=2; i<=iter; i++) {
double p = x2; for (int j=1; j<i; j++) p *= x2;
int fact = 2; for (int j=i; j>2; j--) fact *= j;
sumY += p / fact;
}
return sumY;
}
表 1 以多組隨機正實數測試三方法與真實值誤差:
底數,指數 真實值 泰勒級數 Math.sqrt() 衰減法 8.0,0.75 4.75682846 4.74233532 4.75682846 4.75128682 8.0,0.667 4.002774 3.99193551 3.99458845 3.99445366 16.0,8.0 4294967296 4294967296 4294967296 4294752931 5.0,0.05 1.08379839 1.08378838 1.08345776 1.08205432 0.06282,0.325784 0.40591915 0.41327102 0 0.06282
結論:泰勒級數整體精度最佳,計算週期少但極小值不穩;Math.sqrt() 穩健且精度足夠(通常 3 位有效數字);衰減法僅適合極小問題。實務建議優先 Math.sqrt() 或泰勒級數,並依應用場景權衡精度與資源消耗。
Math.sqrt() 法為大多數應用最佳平衡點。