所以,我想在代码中编写一个函数,使用某种算法来计算任何幂的任何数,包括小数。我使用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的功耗估算如何在一秒钟内完成?
答案 0 :(得分:3)
对于正整数幂来说,这是一个很好的算法,它首先处理一些简单的情况,然后使用循环测试指数的二进制位。例如,要查找二进制中的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 note ln(x^y) = y*ln(x)
。现在我们需要在正确的范围内获得参数,以便我们可以使用我们的幂级数。令x = m * 2 ^ ex,选择的尾数和指数为1 / sqrt(2)&lt; = m&lt; sqrt(2)和ln(m*2^ex) = ln(m) + ex*ln(2)
。设h = m-1,求ln(1 + h)。
使用java和floats,因为有一种简单的方法可以找到IEEE表示的内部结构(我已经使用了浮点数,因为需要更少的位来处理)
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)<1中构建数字z。 z&lt; SQRT(2)
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) = yln(m) + yex*ln(2)
和
x^a=exp(ln((m*2^ex)^a)) = exp(a * ln(m)) * exp(a * ln(2))^ex
两个指数项具有相当小的值,因此泰勒系列应该是好的。
我们需要一个函数用于指数函数的泰勒级数
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));
这应该是完整的程序,你需要一些智慧来应对负面论点等。
请注意,这并不是特别准确,因为我只使用了泰勒系列的几个术语。其他SO问题有更详细的答案How can I write a power function myself?
答案 1 :(得分:1)
我检查了这篇文章,但它仅适用于整数(1,2,3 ......不是0.1,0.3 ......)
Recursive power function: Why does this work if there's no initial return value?
然后,
我从这里得到了这个:Algorithm for 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)......为了以防万一。
Flexo总结一下:
通用算法倾向于将浮动功率计算为 整数幂与剩余根的组合。整数 功率相当简单,可以使用任何一个来计算根 牛顿 - 拉普森方法或泰勒系列。 IIRC中的IIRC数字配方 有一些文字。还有其他(可能更好)的方法 这样做,但这将是一个合理的起点 这是一个令人惊讶的复杂问题。另请注意 一些实现使用查找表和一些技巧 减少所需的计算量。
http://mathworld.wolfram.com/NewtonsMethod.html
http://mathworld.wolfram.com/TaylorSeries.html
答案 2 :(得分:1)
这些是一些非常好的例子,这里也是一个更简单的例子。
function exponential(a,b){
var c = 1;
for(var i=1; i<=b; i++){
c = c * a;
}
return c;
}
现在调用函数:
exponential(2,4);
编辑:它只适用于整数,但它简单快捷。