Here's the least ugly forma simple demo implementation of this micro optimisationalgorithm that I could come up withuses std::vector<>
as a faux big integer:
bigint_ttypedef nth_powerstd::vector<uint32_t> fake_bigint;
fake_bigint &nth_power (bigint_tfake_bigint x&result, uint32_t base, unsigned exponent)
{
bigint_tfake_bigint current_power(1, base);
result.resize(1);
result[0] = exponent & 1 ? xbase : 1;
while (exponent >>= 1)
{
xcurrent_power *= x;current_power;
if (exponent & 1)
{
result *= x;current_power;
}
}
return result;
}
Almost exactly the same as simple version that used big integers... That's why I like C++.
Here's the implementation for operator *=
:
void operator *= (fake_bigint &multiplicand, fake_bigint const &multiplier)
{
returnfake_bigint result;result(multiplicand.size() * 2);
for (unsigned n = unsigned(multiplier.size()), i = 0; i < n; ++i)
{
addmul_1(result, i, multiplicand, multiplier[i]);
}
while (result.size() > 1 && result.back() == 0)
{
result.pop_back();
}
multiplicand.swap(result);
}
The real meat is in the addmul_1
helper function that multiplies a big integer by a single word and adds the product to a big integer, at a certain offset from its lower end to reflect the implied power of two for the multiplicand.
void addmul_1 (fake_bigint &result, unsigned offset, fake_bigint const &multiplicand, fake_bigint::value_type multiplier)
{
result.resize(std::max(result.size(), offset + multiplicand.size()));
uint64_t accu = 0;
for (fake_bigint::size_type n = multiplicand.size(), i = 0; i < n; ++i)
{
accu += result[offset + i] + uint64_t(multiplicand[i]) * multiplier;
result[offset + i] = uint32_t(accu);
accu >>= 32;
}
for (fake_bigint::size_type k = offset + multiplicand.size(); accu; ++k)
{
if (k < result.size())
result[k] = uint32_t(accu += result[k]);
else
result.push_back(uint32_t(accu));
accu >>= 32;
}
}
On my laptop this computes 9999^9999 in 6.4 ms; I put the number in my pastebin (39996 digits).