简体   繁体   中英

In java, will the program “remember” the result if you call the same function for the second time?

This question came up when I was thinking about the algorithm to fast compute the power of a number, say compute x^n .

In Java, the recursive part is something like:

return power(x,n/2) * power(x,n/2);

So after the program has returned the value of power(x,n/2) , will it go through the whole recursive process again to compute the value for the second power(x,n/2) ?

If so, should we store the value in some variable first, then return the square of that value?

What you have in mind is called memoization : A pure function (ie a function whose return value depends only on the argument values) can remember the result for arguments that it has previously encountered.

Memoization is performed by some high-level special-purpose languages like Maple. However, general-purpose languages don't have this (rather complex) behaviour by default.

However, in your case this isn't necessary. Rather than using recursion, your algorithm is better implemented as iteration . Binary exponentiation by repeated squaring is the standard algorithm.

Here's some C-like pseudo-code (my Java isn't 100%):

// compute pow(base, exponent)

int result = 1;
int term = base;

while (exponent != 0)
{
  if (exponent % 2 != 0) { result *= term; }
  term = term * term;
  exponent /= 2;
}

return result;

For some examples, 7 is 111 in binary, so b 7 = b 1 × b 2 × b 4 , and we only need to keep track of one copy of the running term. Another one: 5 = 101b, so b 5 = b 1 × 1 × b 4 .

In C++ it's quite easy to build a generic memoizing wrapper for any function R f(T1, T2, ..., TN) that stores the memory in a hash map std::unordered_map<std::tuple<T1, ..., TN>, R> ; upon invocation the wrapper checks if the argument-tuple already exists, and if yes, it returns the value from the map, and if no it performs the computation and inserts the result into the map. I'm sure something similar can be rigged up in Java.

In non-functional languages (where functions can (and often do) have side effects) the compiler can't know (or at least has a very hard time of knowing) whether the function will produce the same value every time.

So, the particular implementation of power might know enough to cache the value of a particular power function. (I suspect that is unlikely.) OR, a java implementation may "just know" that power works that way, and do special shortcuts with that knowledge.

Otherwise, the answer is basically "No".

This is a general problem with a lot of recursive solutions. (for the fibonacci series, as well).

Typically you would use something called "Dynamic Programming" where you recurse, but check whether the value was computed before ("memoization"). It's extremely doubtful the compiler will optimize this away for you.

I disagree with goto10, because you're doubly calling yourself, the computation time will grow exponentially, so if you really must use code like this, it's far from a premature optimization.

is this a homework question?

Java, like most programming languages, does not memoize results of method calls by default, as the method call may have other side effects. In other words, the JVM cannot assume that these two statements are equivalent:

 return power(x, n/2) * power(x, n/2)

and

 int sqrtPower = power(x, n/2);
 return sqrtPower * sqrtPower;

The JVM cannot make this optimization - you have to do it explicitly.

In general, Java does not cache the value of method calls. The JIT compiler might optimize some expressions if the code is simple enough to reason over, but this cannot be done in the general case, since there may be side effects (As an aside, some functional languages like Haskell that don't allow side-effects can avoid this problem).

However, if you have an algorithm that is doing these kinds of calculations a lot, you may want to look into memoization to cache the results of previous computations. Dynamic programming uses this a lot.

The evaluation of an arithmetic expression such as that one, is stored in a stack. There is a stack for the different recursive calls and then for each call there is a stack used to evaluate the expression inside the function. That is the mechanism to "remember" partial results at every point of the evaluation.

Is that what you are asking?

You can achieve this generically using AOP.Here it goes,

  • Intercept call to a method
  • Return result if it can be found based on class, method and argument values in cache.
  • If not, proceed with call and place the return value in cache.
  • Return the result.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM