[英]Writing your own exponential power function with decimals
因此,我想使用某種算法在代碼中編寫一個函數,以計算任意冪的任何數字,包括小數。 我使用JavaScript,它已經具有內置的pow函數:
Math.pow(2, 0.413) // 2^0.413 = 1.331451613236371, took under 1 second.
現在我要這樣寫我自己的:
function pow(x, y) {
// Algorithm
}
此函數可計算任何數字(x ^ 0.5)的平方根,並且只有10個循環的精度很高:
function sqrt(x, p) { // p = precision (accuracy)
var a = 1;
var b = x;
while (p--) {
a = (a + b) / 2
b = x / a
}
return a
}
有沒有簡單的公式可以計算指數?
如果沒有簡單的方法,那么有沒有困難的方法?
如果解決方案很慢,那么如何在一秒鍾內估算JavaScript的功耗?
對於正整數冪,這是一個不錯的算法,它從處理一些簡單的情況開始,然后使用循環測試指數的二進制位。 例如,以二進制找到3^11
11是1011,因此循環中的階段是
那是每個循環的evenPower平方,如果最低位是1,則結果乘以evenPower。代碼已從Patricia Shanahan的方法http://mindprod.com/jgloss/power.html提取,該方法又具有它的根源於Kunth,可以追溯到公元前200年的印度。
/**
* A fast routine for computing integer powers.
* Code adapted from {@link <a href="http://mindprod.com/jgloss/power.html">efficient power</a>} by Patricia Shanahan pats@acm.org
* Almost identical to the method Knuth gives on page 462 of The Art of Computer Programming Volume 2 Seminumerical Algorithms.
* @param l number to be taken to a power.
* @param n power to take x to. 0 <= n <= Integer.MAX_VALUE
* Negative numbers will be treated as unsigned positives.
* @return x to the power n
*
*/
public static final double power(double l,int n)
{
assert n>=0;
double x=l;
switch(n){
case 0: x = 1.0; break;
case 1: break;
case 2: x *= x; break;
case 3: x *= x*x; break;
case 4: x *= x; x *= x; break;
case 5: { double y = x*x; x *= y*y; } break;
case 6: { double y = x*x; x = y*y*y; } break;
case 7: { double y = x*x; x *= y*y*y; } break;
case 8: x *= x; x *= x; x *= x; break;
default:
{
int bitMask = n;
double evenPower = x;
double result;
if ( (bitMask & 1) != 0 )
result = x;
else
result = 1;
bitMask >>>= 1;
while ( bitMask != 0 ) {
evenPower *= evenPower;
if ( (bitMask & 1) != 0 )
result *= evenPower;
bitMask >>>= 1;
} // end while
x = result;
}
}
return x;
}
對於真正的指數,您基本上需要找到exp和log的方法。 您可以使用最簡單的泰勒級數,但有更好的方法。 我們有
exp(x) = 1 + x + x^2/2 + x^3/6 + x^4/24 + x^5/120 + x^6/6! + ...
ln(1+x) = x - x^2 /2 + x^3 /3 - x^4 / 4 + x^5 / 5 - x^6/6 + ... |x|<1
為了找到x ^ y,請注意ln(x^y) = y*ln(x)
。 現在我們需要將參數設置在正確的范圍內,以便可以使用冪級數。 令x = m * 2 ^ ex,選擇尾數和指數,因此1 / sqrt(2)<= m <sqrt(2)且ln(m*2^ex) = ln(m) + ex*ln(2)
。 令h = m-1並找到ln(1 + h)。
使用java和floats有一種簡單的方法來查找IEEE表示的內部結構(我使用了float因為有較少的位要應付)
int b = Float.floatToIntBits(x);
int sign = (b & 0x80000000) == 0 ? 1 : -1;
int mattissa = b & 0x007fffff;
int ex = ((b & 0x7f800000) >> 23 ) - 127;
在javascript中,對我們來說Number.toExponential並解析結果可能是最簡單的。
接下來構造期望范圍1 / sqrt(2)<z <sqrt(2)中的數字z
int bits = mattissa | 0x3f800000;
float z = Float.intBitsToFloat(bits);
if(z>root2) {
z = z/2;
++ex;
}
使用此函數使用泰勒級數查找1 + x的對數
static float ln1px(float x) {
float x_2 = x*x; // powers of x
float x_3 = x_2 * x;
float x_4 = x_3 * x;
float x_5 = x_4 * x;
float x_6 = x_5 * x;
float res = x - x_2 /2 + x_3 /3 - x_4 / 4 + x_5 / 5 - x_6/6;
return res;
}
這似乎對三個有效數字都很好,通常在x接近0時好得多。
然后可以找到我們的數字x的對數
float w = z - 1;
float ln_z = ln1px(w);
float ln_x = ln_z + ln2 * ex;
System.out.println("ln "+ln_x+"\t"+Math.log(x));
現在我們將y = n + a寫成實際乘方,其中n是整數,a是分數。 所以x^y=x^(n+a) = x^n * x^a
。 在此答案中使用第一種算法找到x^n
。 寫x=m*2^ex
然后ln((m*2^ex)^a) = y ln(m) + y ex*ln(2)
和
x^a=exp(ln((m*2^ex)^a)) = exp(a * ln(m)) * exp(a * ln(2))^ex
這兩個指數項的值相當小,因此taylor級數應該很好。
指數函數的泰勒級數需要一個函數
static float exp(float x) {
float x_2 = x*x; // powers of x
float x_3 = x_2 * x;
float x_4 = x_3 * x;
float x_5 = x_4 * x;
float x_6 = x_5 * x;
float res = 1+ x + x_2 /2 + x_3 /6 + x_4 / 24 + x_5 / 120 + x_6/ 720;
return res;
}
最終我們可以將各個部分放在一起
// Get integer and fractional parts of y
int n = (int) Math.floor(y);
float a = y-n;
float x_n = power(x,n); // x^n
float a_ln_m = a * ln_z; // ln(m^a) = a ln(m)
float a_ln_2 = a * ln2; // ln(2^a) = a ln(2)
float m_a = exp(a_ln_m); // m^a = exp(a ln(m))
float _2_a = exp(a_ln_2); // 2^a = exp(a ln(2))
float _2_a_ex = power(_2_a,ex); // (2^ex)^a = 2^(a*ex) = (2^a)^ex
float x_a = m_a * _2_a_ex; // x^a = m^a * 2^(a*ex)
float x_y = x_n * x_a; // x^y = x^n * x^a
System.out.println("x^y "+x_y+"\t"+Math.pow(x,y));
那應該是完整的程序,您需要一些技巧來應對否定參數等。
請注意,這並不是特別准確,因為我只使用了taylor系列的幾個術語。 其他SO問題有更詳細的答案我如何自己編寫冪函數?
我檢查了這篇文章,但它僅適用於整數(1,2,3 ...而不是0.1、0.3 ...)
然后,
我是從這里得到的: pow(float,float)的算法
function power(x,n) {
if(n === 0) return 1;
if(n === -1) return 1/x;
if(n === 1) return x;
return Math.exp(n*Math.log(x))
}
console.log(power(2,3.5));
我添加了一些基本檢查(n === 0)...以防萬一。
柔印總結一下:
通用算法傾向於將浮點功率作為整數冪和剩余根的組合來計算。 整數冪很簡單,可以使用牛頓-拉夫森方法或泰勒級數計算根。 C中的IIRC數字配方對此有一些說明。 也有其他(可能更好)的方法來執行此操作,但這將為實現一個令人驚訝的復雜問題提供一個合理的起點。 還要注意,某些實現使用查找表和許多技巧來減少所需的計算。
http://mathworld.wolfram.com/NewtonsMethod.html
http://mathworld.wolfram.com/TaylorSeries.html
這些是一些非常好的示例,這也是一個簡單的示例。
function exponential(a,b){
var c = 1;
for(var i=1; i<=b; i++){
c = c * a;
}
return c;
}
現在調用函數:
exponential(2,4);
編輯:它僅適用於整數,但它簡單而又快捷。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.