简体   繁体   中英

Fibonacci Stack program in Java. Infinite loop

I'm trying to make a fibonacci number calculator that does not use recursion in Java. However when I run the program it generates an infinite loop. I have put in many print statements to try and debug it but honestly i'm not even too sure how Stack is supposed to work. Here are my two classes:

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));
}

}

And my stack frame class. I put in an exception after the iteration runs for 30 times because otherwise it would never stop.

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;
}

}

When I ran my tests, I noticed no matter what number I put into the fib2(int), It would get stuck on a loop of the value of n alternating between 1 and 2 and continue without end. I put ALOT of print statements so I could trace out what it was doing and so on but I think a major problem is I just don't understand exactly how the stack is working in junction with the cache and then returning a final value. I should also mention that I am building these classes off of frames given by my professor.

I guess this line is the problem:

f = callStack.peek()

peek just retrieves the value, but doesn't remove it from stack, so the stack will never get empty.I think you are trying to do this:

f = callStack.pop()

Check the documentation for more information

Based on the documentation I believe you made a minor error on a call.

/**
 * 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
 */

Otherwise, you should have the caller invoke receiveReturnValue to accept the value of F(n), which can be obtained from the method getReturnValue ...

And to achieve that you do:

this.receiveReturnValue(this.getReturnValue());

But the documentation for that method says:

Receive a return value from another FibonacciStackFrame instance. (emphasis mine)

You're calling it on this frame though. I don't see where you actually return the value to any caller. I think it's supposed to be:

caller.receiveReturnValue(this.getReturnValue());

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