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