我试图评估一个^ n,其中a和n是有理数。
我不想使用任何预定义的功能,例如sqrt()
或pow()
所以我尝试使用Newton的方法来使用这种方法获得近似解决方案:
3 ^ 0.2 = 3 ^(1/5),所以如果x = 3 ^ 0.2,则x ^ 5 = 3.
可能是解决这个问题的最佳方式(没有计算器但仍然如此) 使用基本算术运算)是使用"牛顿法"。 牛顿求解方程f(x)= 0的方法是建立a 通过将x0作为一些初始"猜测"定义的数字序列xn。 然后xn + 1 = xn-f(xn / f'(xn)其中f'(x)是f的导数。
该方法的问题在于,如果我想计算5.2^0.33333
,我需要找到此等式x^10000 - 5.2^33333 = 0
的根。我最终得到了大量的数字,并且大部分时间都会出现inf
和nan
错误。
有人可以就如何解决这个问题给我建议吗?或者,有人可以提供另一种算法来计算^ n吗?
答案 0 :(得分:5)
似乎你的任务是计算
⎛ xN ⎞(aN / aD)
⎜⎼⎼⎼⎼⎟ where xN,xD,aN,aD ∈ ℤ, xD,aD ≠ 0
⎝ xD ⎠
仅使用乘法,除法,加法和减法,Newton's method作为建议的实施方法。
我们试图解决的等式( y )是
(aN / aD)
y = (xN / xD) where y ∈ ℝ
Newton的方法找到函数的根。如果我们想用它来解决上述问题,我们从左侧减去右侧,得到一个函数,其零值给出了我们想要的 y :
(aN/aD)
f(y) = y - (xN/xD) = 0
没什么帮助。我想这就是你得到的?这里的要点是暂时还没有形成这个功能,因为我们没有办法计算有理数的理性幂!
首先,让我们确定 aD 和 xD 都是正面的。如果 aD 为负数,我们可以简单地通过否定 aN 和 aD 来做到这一点(所以 aN / <的符号如果 xD 为负,则em> aD 不会改变,并且否定 xN 和 xD 。请记住,根据定义, xD 或 aD 均为零。然后,我们可以简单地将双方都提升到 aD '的权力:
aD aN aN aN
y = (xN / xD) = xN / xD
我们甚至可以通过将双方乘以最后一个词来消除分裂:
aD aN aN
y × xD = xN
现在,这看起来很有希望!我们从中获得的功能是
aD aN aN
f(y) = y xD - xN
牛顿的方法也需要衍生物,显然是
f(y) aD aN
⎼⎼⎼⎼ = df(y) = y xD y / aD
dy
牛顿方法本身依赖于迭代
f(y)
y = y - ⎼⎼⎼⎼⎼⎼
i+1 i df(y)
如果计算出数学,你会发现迭代只是
aD
y[i] y[i] xN
y[i+1] = y[i] - ⎼⎼⎼⎼ + ⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼⎼
aD aD aN
aD y[i] xD
您无需将所有 y 值保留在内存中;它足以记住最后一个,并在它们的差异足够小时停止迭代。
你仍然有上面的取幂,但现在它们只是整数取幂,即
aD
xN = xN × xN × .. × xN
╰───────┬───────╯
aD times
你可以非常简单地做,例如只需将参数乘以所需的次数,例如,在C,
double ipow(const double base, const int exponent)
{
double result = 1.0;
int i;
for (i = 0; i < exponent; i++)
result *= base;
return result;
}
有更有效的方法integer exponentiation,但上述功能应完全可以接受。
最后一个问题是选择初始 y 以便收敛。你不能使用0,因为(幂) y 被用作分区中的分母;你会得到零除错误。就个人而言,我会检查结果是正面的还是负面的,小于或大于一个;总共有两条规则可以选择一个安全的初始 y 。
有问题吗?
答案 1 :(得分:2)
您可以使用generalized binomial theorem。替换y=1
和x=a-1
。您可能希望根据所需的准确度在足够的术语后截断无限级数。为了能够将术语数量与准确性相关联,您需要确保x^r
项的绝对值正在下降。因此,根据a
和n
的值,您应该应用公式来计算a^n
和a^(-n)
中的一个,并使用它来获得所需的结果。
答案 2 :(得分:2)
将整数提升为幂的解决方案是:
int poweri (int x, unsigned int y)
{
int temp;
if (y == 0)
return 1;
temp = poweri (x, y / 2);
if ((y % 2) == 0)
return temp * temp;
else
return x * temp * temp;
}
然而,平方根并不能提供封闭解决方案的清洁。在wikipedia-square root和Wolfram Mathworks Square Root Algorithms处可以找到很多背景资料。两者都提供了几种满足您需求的方法,您只需选择适合您目的的方法。
稍作修改,来自维基百科的这个例程(修改为返回平方根并提高精度)返回一个令人惊讶的准确平方根。是的,关于联合的使用会有嚎叫,它只在整数和浮点存储等价的情况下有效,但是如果你正在攻击你自己的平方根,这是相对有效的:
float sqrt_f (float x)
{
float xhalf = 0.5f*x;
union
{
float x;
int i;
} u;
u.x = x;
u.i = 0x5f3759df - (u.i >> 1);
/* The next line can be repeated any number of times to increase accuracy */
// u.x = u.x * (1.5f - xhalf * u.x * u.x);
int i = 10;
while (i--)
u.x *= 1.5f - xhalf * u.x * u.x;
return 1.0f / u.x;
}