簡體   English   中英

用小數寫自己的指數冪函數

[英]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,因此循環中的階段是

  • bitMask = 1011,evenPower = 3,結果= 3
  • bitMask = 101,evenPower = 3 * 3 = 9,結果= 3 * 9 = 27
  • bitMask = 10,evenPower = 9 * 9 = 81,結果= 27
  • bitMask = 1,evenPower = 81 * 81 = 6561,結果= 27 * 6561 = 177147

那是每個循環的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

https://zh.wikipedia.org/wiki/對數#Power_series

https://rads.stackoverflow.com/amzn/click/0521431085

這些是一些非常好的示例,這也是一個簡單的示例。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM