CMSI 282
Homework #3
Partial Answers

All questions from this assignment, save one, were from the textbook, so please collect the answers in class. I don't post solutions to textbook problems on the public web.

Here is my solution to the currency trade analyzer.

package edu.lmu.cs.currency;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import org.apache.commons.lang.Validate;

/**
 * A utility class with the popular "find the best sequence of currency
 * trades" and "find me an arbitrage opportunity" methods.
 */
public class TradeAnalyzer {
    private TradeAnalyzer() {
    }

    /**
     * Returns the adjusted rate matrix containing the negative logs
     * of the exchange rates.  Throws IllegalArgumentException if the
     * matrix is not square or if there are any zeros.
     */
    private static double[][] adjustedMatrix(double[][] rates) {
        double[][] result = new double[rates.length][rates.length];
        for (int i = 0, n = rates.length; i < n; i++) {
            Validate.isTrue(rates[i].length == rates.length, "Matrix not square");
            for (int j = 0; j < n; j++) {
                Validate.isTrue(rates[i][j] != 0, "No zeros allowed");
                result[i][j] = -Math.log(rates[i][j]);
            }
        }
        return result;
    }

    /**
     * Returns the sequence of exchanges that optimizes the amount
     * currency "to" that can be obtained from currency "from".
     * For example, if the most favorable way to turn currency
     * 4 into currency 10 is a direct exchange, this method returns
     * [4, 10].  If one can get a greater amount of currency 10
     * by first buying currency 7 then trading those for units of
     * currency 6, and then buying units of 10, then
     * the method wll return [4, 7, 6, 10].
     *
     * Returns null if there is an arbitrage.
     */
    public static List<Integer> optimalTradeSequence(int from, int to,
            double[][] exchangeRates) {
        double r[][] = adjustedMatrix(exchangeRates);

        Validate.isTrue(from >= 0 && from < r.length, "Index out of range: " + from);
        Validate.isTrue(to >= 0 && to < r.length, "Index out of range: " + to);

        // Run Bellman-Ford on the adjusted matrix.
        double[] distances = new double[r.length];
        Arrays.fill(distances, Double.POSITIVE_INFINITY);
        distances[from] = 0.0;
        int[] predecessors = new int[r.length];
        Arrays.fill(predecessors, -1);
        for (int i = 0; i < r.length; i++) {
            for (int j = 0; j < r.length; j++) {
                for (int k = 0; k < r.length; k++) {
                    if (distances[k] > distances[j] + r[j][k]) {
                        if (i == r.length - 1) return null;
                        distances[k] = distances[j] + r[j][k];
                        predecessors[k] = j;
                    }
                }
            }
        }

        LinkedList<Integer> result = new LinkedList<Integer>();
        for (int i = to; i != -1; i = predecessors[i]) {
            result.addFirst(i);
        }
        return result;
    }

    /**
     * Returns whether there exists a sequence of exchanges that
     * can turn a unit of one currency into more than one unit
     * of the same currency.
     */
    public static boolean freeMoneyAvailable(double[][] exchangeRates) {
        return exchangeRates.length > 0
                && optimalTradeSequence(0, 0, exchangeRates) == null;
    }
}