我试图制作一个不使用Java递归的斐波那契数字计算器。但是,当我运行程序时,它会生成一个无限循环。我已经输入了许多打印语句来尝试调试它,但说实话,我甚至不太确定Stack应该如何工作。这是我的两个班级:
package fibNumbers.fibStack;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
/**
* A utility class containing methods to compute Fibonacci numbers.
*
* @author EECS2030 Fall 2016-17
*
*/
public class Fibonacci {
private Fibonacci() {
// empty by design
}
private static Map<Integer, BigInteger> cache = new HashMap<Integer, BigInteger>();
/**
* Memoized recursive implementation for computing Fibonacci numbers.
* This method will fail for modest values of n because of limits
* on the size of the call stack memory.
*
* @param n which Fibonacci number to compute
* @return the value of F(n)
*/
public static BigInteger fib(int n) {
BigInteger sum = null;
if (n == 0) {
sum = BigInteger.ZERO;
}
else if (n == 1) {
sum = BigInteger.ONE;
}
else if (cache.containsKey(n)) {
sum = cache.get(n);
}
else {
BigInteger fMinus1 = Fibonacci.fib(n - 1);
BigInteger fMinus2 = Fibonacci.fib(n - 2);
sum = fMinus1.add(fMinus2);
cache.put(n, sum);
}
return sum;
}
/**
* Memoized iterative implementation for computing Fibonacci numbers
* using an explicit call stack and simulated stack frames.
*
* @param n which Fibonacci number to compute
* @return the value of F(n)
*/
public static BigInteger fib2(int n) {
Stack<FibonacciStackFrame> callStack = new Stack<FibonacciStackFrame>();
FibonacciStackFrame f = new FibonacciStackFrame(n, null);
callStack.push(f);
while (!callStack.isEmpty()) {
f = callStack.peek();
f.execute(callStack);
System.out.println("Finished execution, starting loop again.");
}
return f.getReturnValue();
}
public static void main(String[] args) {
System.out.println("F(10000) = " + Fibonacci.fib2(10000));
}
}
我的堆栈框架类。我在迭代运行30次后放入异常,否则它永远不会停止。
package fibNumbers.fibStack;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
/**
* A stack frame for computing Fibonacci numbers.
*
* @author EECS2030 Fall 2016-17
*
*/
public class FibonacciStackFrame {
private static Map<Integer, BigInteger> cache = new HashMap<Integer, BigInteger>();
private int n;
private FibonacciStackFrame caller;
private boolean fMinus1Computed;
private boolean fMinus2Computed;
private BigInteger fMinus1;
private BigInteger fMinus2;
private BigInteger sum;
public static int timesExecuted = 0;
/**
* Creates a stack frame to compute F(n). <code>caller</code> is a reference
* to the <code>FibonacciStackFrame</code> that created this stack frame. If
* this stack frame is not being created by another
* <code>FibonacciStackFrame</code> instance, then <code>caller</code> is
* expected to be equal to <code>null</code>.
*
*
* @param n
* the Fibonccci number to compute
* @param caller
* the <code>FibonacciStackFrame</code> that created this stack
* frame
* @throws IllegalArgumentException
* if n is less than 0
*/
public FibonacciStackFrame(int n, FibonacciStackFrame caller) {
if (n < 0) {
throw new IllegalArgumentException("n must != ZERO");
}
this.n = n;
this.caller = caller;
this.fMinus1 = BigInteger.ZERO;
this.fMinus1Computed = false;
this.fMinus2 = BigInteger.ZERO;
this.fMinus2Computed = false;
this.sum = BigInteger.ZERO;
}
/**
* Receive a return value from another <code>FibonacciStackFrame</code>
* instance.
*
* <p>
* This method is used to simulate Lines 16 and 17 of the fib(int) method
* described in the lab web page. This method needs to figure out if it is
* simulating Line 16 or Line 17; continue reading for details.
* </p>
*
* <p>
* If this.fMinus1Computed is false, then this method should set
* this.fMinus1 to <code>retVal</code> (simulating Line 16), and set
* this.fMinus1Computed to true.
* </p>
*
* <p>
* If this.fMinus1Computed is true, and this.fMinus2Computed is false, then
* this method should set this.fMinus2 to <code>retVal</code> (simulating
* Line 17), and set this.fMinus2Computed to true..
* </p>
*
* <p>
* If both this.fMinus1Computed and this.fMinus2Computed are true then you
* have done something wrong elsewhere in the class, and you should throw an
* IllegalStateException.
* </p>
*
* @param retVal
* the value of F(n - 1) or F(n - 2) returned from another
* <code>FibonacciStackFrame</code> instance
* @throws IllegalStateException
* if this stack frame has already received values for both F(n
* - 1) and F(n - 2)
*/
public void receiveReturnValue(BigInteger retVal) {
System.out.println("...Now at top of this.recieveReturnValue");
if (this.fMinus1Computed == false) {
System.out.println("...Computing fMinus1, setting to true");
this.fMinus1 = retVal;
this.fMinus1Computed = true;
} else if ((this.fMinus1Computed == true) && (this.fMinus2Computed == false)) {
this.fMinus2 = retVal;
this.fMinus2Computed = true;
} else if ((this.fMinus1Computed == true) && (this.fMinus2Computed == true)) {
throw new IllegalStateException("BOTH both F(n - 1) and F(n - 2), stack frames have recieved values");
}
}
/**
* Returns the value of F(this.n) back to the caller. This simulates Line 21
* of the fib(int) method described in the lab web page.
*
* <p>
* If the caller is equal to null, then you should simply pop the call
* stack. Otherwise, you should have the caller invoke
* <code>receiveReturnValue</code> to accept the value of F(n), which can be
* obtained from the method <code>getReturnValue</code>, and then pop the
* call stack.
*
* @param callStack
* the call stack
*/
public void returnValueToCaller(Stack<FibonacciStackFrame> callStack) {
if (this.caller == null) {
System.out.println("current caller is null");
this.caller = callStack.pop();
} else {
System.out.println("Caller != null");
System.out.println("getReturnValue yields: "+this.getReturnValue());
this.receiveReturnValue(this.getReturnValue());
this.caller = callStack.pop();
System.out.println("The call stack has been popped");
}
}
/**
* Start or resume execution of this stack frame. This method is expected to
* be invoked when this stack frame is on top of the call stack. When this
* method is invoked, this stack frame can be in one of six possible states:
*
* <ol>
* <li>this stack frame is computing F(0), in which case the value 0 can be
* returned to the caller
* <li>this stack frame is computing F(1), in which case the value 1 can be
* returned to the caller
* <li>this stack frame is computing F(n) and F(n) is in the cache, in which
* case the value F(n) can be retrieved from the cache and returned to the
* caller
* <li>this stack frame is computing F(n - 1), in which case this stack
* frame should push a new stack frame onto the call stack to compute F(n -
* 1) passing <code>this</code> as the caller of the new stack frame, and
* then return from this method
* <li>this stack frame is computing F(n - 2), in which case this stack
* frame should push a new stack frame onto the call stack to compute F(n -
* 2) passing <code>this</code> as the caller of the new stack frame, and
* then return from this method
* <li>this stack frame is computing the sum F(n - 1) + F(n - 2), in which
* case this stack frame should compute the sum, put the sum in the cache,
* and return the value of the sum to the caller
* </ol>
*
* <p>
* You should write an if-else-if statement that covers the 6 cases
* described above.
* </p>
*
* @param callStack
* the call stack that this frame is on top of
*/
public void execute(Stack<FibonacciStackFrame> callStack) {
System.out.println("");
System.out.println("New Frame *** *** *** ***");
timesExecuted ++;
if(timesExecuted>20){
throw new IllegalArgumentException("***Stack overflow, infinite loop occuring***");
}
System.out.println("currentFrame.n ="+this.getN());
if (this.getN() == 0) {
timesExecuted++;
System.out.println("Execution #" + timesExecuted);
setSum(BigInteger.ZERO);
this.returnValueToCaller(callStack);
} else if (this.getN() == 1) {
System.out.println("running through case n==1");
setSum(BigInteger.ONE);
System.out.println(this.sum);
this.returnValueToCaller(callStack);
} else if (cache.containsKey(this.getN())) {
setSum(cache.get(n));
this.returnValueToCaller(callStack);
} else if (!getfMinus1Computed()) {
System.out.println("***Computing fMinus1");
System.out.println("n at this point: "+this.getN());
callStack.push(new FibonacciStackFrame(this.getN() - 1, this));
return;
} else if (!getfMinus2Computed()) {
System.out.println("*^*Computing fMinus2");
callStack.push(new FibonacciStackFrame(this.getN() - 2,this));
return;
} else {
this.setSum(fMinus1);
this.sum.add(fMinus2);
cache.put(this.getN(), this.getReturnValue());
System.out.println(cache.get(this.getN())+"End case");
this.returnValueToCaller(callStack);
}
}
// EVERYTHING BELOW THIS LINE HAS ALREADY BEEN IMPLEMENTED FOR YOU
/**
* Get the return value (F(n)) from this stack frame. The return value is
* just this.sum (which is equal to F(n-1) + F(n-2) when this frame has
* finished all of its work).
*
* <p>
* If you call this method before the frame has finished all of its work
* then you will get the wrong value for the sum.
*
* @return the value of F(n)
*/
public BigInteger getReturnValue() {
// ALREADY COMPLETED FOR YOU
return this.sum;
}
/**
* Get the cache of already computed Fibonacci numbers. This method is here
* for debugging and testing purposes.
*
* @return the cache of already computed Fibonacci numbers
*/
public static Map<Integer, BigInteger> getCache() {
return FibonacciStackFrame.cache;
}
/**
* Get the value of n. This method is here for debugging and testing
* purposes.
*
* @return the value of n
*/
public int getN() {
return this.n;
}
/**
* Get the creator of this call stack. This method is here for debugging and
* testing purposes.
*
* @return the creator of this call stack
*/
public FibonacciStackFrame getCaller() {
return this.caller;
}
/**
* Get the current value of fMinus1. This method is here for debugging and
* testing purposes.
*
* @return the current value of fMinus1
*/
public BigInteger getfMinus1() {
return this.fMinus1;
}
/**
* Get the current value of fMinus2. This method is here for debugging and
* testing purposes.
*
* @return the current value of fMinus2
*/
public BigInteger getfMinus2() {
return this.fMinus2;
}
/**
* Get the current value of fMinus1Computed. This method is here for
* debugging and testing purposes.
*
* @return the current value of fMinus1Computed
*/
public boolean getfMinus1Computed() {
return this.fMinus1Computed;
}
/**
* Get the current value of fMinus2Computed. This method is here for
* debugging and testing purposes.
*
* @return the current value of fMinus2Computed
*/
public boolean getfMinus2Computed() {
return this.fMinus2Computed;
}
/**
* Set the value of this.sum. This method is here for debugging and testing
* purposes.
*
* @param sum
* the new value for this.sum
*/
public void setSum(BigInteger sum) {
this.sum = sum;
}
}
当我运行我的测试时,我注意到无论我在fib2(int)中放入了什么数字,它都会卡在n值的循环中,在1和2之间交替并继续没有结束。我放了大量的print语句,所以我可以找出它在做什么等等,但我认为一个主要的问题是我只是不清楚堆栈是如何与缓存联合工作然后返回最终值的。我还要提一下,我正在用我教授给出的框架构建这些类。
答案 0 :(得分:0)
我想这一行是问题所在:
f = callStack.peek()
peek只是检索该值,但不会将其从堆栈中删除,因此堆栈永远不会变空。我认为您正在尝试这样做:
f = callStack.pop()
检查the documentation以获取更多信息