Keep a running product of the fixed base. In particular, The loop invariant is and . Initially the empty product is one. Multiplication by
1.0/n uses floating-point arithmetic, avoiding the truncation that integer division would introduce:def eaLinear(n: Int): Double = {
require(n > 0)
val base = 1.0 + 1.0 / n.toDouble
var k = 0
var result = 1.0
while (k < n) {
result *= base
k += 1
}
result
}base and incrementing k preserve the invariant. The loop variant decreases by one. On exit , so the result is , with exactly multiplications and time complexity. With Double, the operational result is the rounded recurrence, rather than an exact real power; this distinction applies to the next two power computations too.Use exponentiation by squaring, retaining the unprocessed exponent in Writing , the loop invariant is and . If the old exponent is , , multiplying the accumulator by and replacing the remaining power by preserves the invariant. When , the unused squaring may be omitted. The nonnegative integer
k:def eaBinary(n: Int): Double = {
require(n > 0)
var base = 1.0 + 1.0 / n.toDouble
var k = n
var result = 1.0
while (k > 0) {
if ((k & 1) != 0) result *= base
k = k / 2
if (k > 0) base *= base
}
result
}k is a loop variant, since for . At exit the remaining power is one, giving . There are iterations, at most one accumulator multiplication and one squaring per iteration, hence multiplications and constant space. Articles by others on the same topic
There are currently no matching articles.