How does the function work?
That's something the OP should have asked to the authors of that snippet (assuming it was copied verbatim or close).
The intent seems to check if a whole number power
exists, such that in combination with the integral arguments number
and base
the following equation is satisfied:
number = base power
The function returns it or 0 if it doesn't exist, meaning that number
is not an integral power of some integral base. To do so,
it uses a property of the logarithms:
n = bp
log(n) = p log(b)
p = log(n) / log(b)
it rounds the number[1] to the "closest" integer, to avoid cases where the limited precision of floating-point types and operations would have yield incorrect results in case of a simple truncation.
In the comments I've already made the example of std::log(1000)/std::log(10)
, which may produce a double
result close to 3.0
, but less than 3.0
(something like 2.9999999999999996). When stored in an int
it would be truncated to 2
.
It checks if the number found is the exact power which solve the previous equation, but that comparison has the same problems I mentioned before.
pow(base, power) == number // It compares a double with an int
Just like std::log
, std::pow
returns a double
value, making all the calculations performed with those functions prone to subtle numerical errors (either by rounding or by accumulation when multiple operations are involved). It's often preferable to use integral types and operations, if possible, when accuracy (or absolute exactness[2]) is needed.
Is the algorithm correct?
I didn't know what could be the biggest value of the base number so my for loop is going from 2 to 10
That's just wrong. One of the constraints of the problem is b <= 1'000'000
, but the posted solution couldn't find any power greater than 102.
An extimate of the greatest possible base is the square root of said b
.
Are there any easier ways to solve this problem?
Easiness is subjective and we don't know all the requirements and constraints of OP's assignment. I'll describe an alternative solution without posting the code I wrote to test it[3].
OP's code considers all the numbers between a
and b
checking for every (well, up to 10) base if there exists a whole power.
My proposal uses only integral variables, of a wide enough type, say long
(any 32-bit integer is enough).
The outer loop starts from base = 2
and increments it by one at every step.
Inside this loop, exponent
is set to 2
and value
to base * base
If value
is greater than b
, the algorithm stops.
While value
is less than a
, updates it (multiplying it by base
) and the exponent (it's incremented by one). We need to find the first power of base
which is greater or equal to a
.
While value
is less than or equal to b
, store the triplet of variables value
, base
and exponent
in suitable container.
Consider a std::map<long, std::pair<long, long>>
, it lets us associate all the value
s with the corresponding pair of base
and exponent
. Also, it could be later traversed to obtain all the values in ascending order.
The assignment requires, in case of multiple powers, to present only the one with the bigger exponent. In the example, it shows 64 = 26, ignoring 64 = 43. Note the needed one is the one with the smaller base, so that it's enough to ignore any further value if it's already present in the map.
value
and exponent
are updated as before.
Note that this algorithm only consider bases up to the square root of b
(in the outer loop) and the number of iterations of the inner loop is much more limited (with base = 2
, it would be less than 20, beeing 220 > 1'000'000. Greater bases would stop sooner and sooner).
[1] See e.g. Why do lots of (old) programs use floor(0.5 + input) instead of round(input)?
[2] See e.g. The most efficient way to implement an integer based power function pow(int, int)
[3] How do I ask and answer homework questions?