/**
* edu.utexas.GeDBIT.dist.LMetric 2006.06.16
*
* Copyright Information:
*
* Change Log: 2006.06.16: Modified from jdb 1.0, by Rui Mao
*/
package GeDBIT.dist;
import GeDBIT.type.DoubleVector;
import GeDBIT.type.IndexObject;
/**
* This class computes the L family distance function on two vectors. Given two
* vectors x(x1,x2,...,xk), y(y1,y2,...,yk), the distance between x,y: Ls(x,y) =
* sum( |xi-yi|^s )^(1/s), i= 1,..,k where s=1,2,...,infinity. For infinite
* distance, L(x,y) = max( |xi-yi| ), i = 1,...,k. L1 is called Manhattan
* distance, L2 is called Euclidean distance. In this implementation, L0 is used
* to represent L infinity.
*
* @author Rui Mao, Wenguo Liu, Willard
* @version 2005.10.31
*/
public class LMetric implements Metric {
/**
*
*/
private static final long serialVersionUID = -4658067077491099474L;
/**
* L1 distance (Manhattan distance) metric for two non-null double arrays of
* the same length. Given two vectors x(x1,x2,...,xk), y(y1,y2,...,yk), the
* distance between x,y: L1(x,y) = sum(|xi-yi| ), i= 1,..,k
*/
public final static LMetric ManhattanDistanceMetric = new LMetric(1);
/**
* L2 distance (Euclidean distance) metric for two non-null double arrays of
* the same length. Given two vectors x(x1,x2,...,xk), y(y1,y2,...,yk), the
* distance between x,y: L2(x,y) = sum(|xi-yi|^2 )^(1/2), i= 1,..,k
*/
public final static LMetric EuclideanDistanceMetric = new LMetric(2);
/**
* L infinity distance metric for two non-null double arrays of the same
* length. Given two vectors x(x1,x2,...,xk), y(y1,y2,...,yk), the distance
* between x,y: L infinity (x,y) = max( |xi-yi| ), i= 1,..,k
*/
public final static LMetric LInfinityDistanceMetric = new LMetric(0);
/** Dimension of the metric */
private int dim;
/**
* Constructor. Takes the dimension of the distance function as an argument.
* (the s, but not the dimension of the vectors to compute distance) Use 0
* to represent infinity.
*
* @param dim
* dimension of the distance function
*/
public LMetric(int dim) {
if (dim < 0)
throw new IllegalArgumentException(
"dimension of LMetric is negative:" + dim);
this.dim = dim;
}
/**
* Computes the distance between two objects. The two objects should be two
* double arrays or {@link DoubleVector}s of the same length.
*
* @param o1
* the first {@link IndexObject} to compute distance on
* @param o2
* the second {@link IndexObject} to compute distance on
* @return the distance between the two objects
*/
public double getDistance(IndexObject o1, IndexObject o2) {
if (o1 instanceof DoubleVector && o2 instanceof DoubleVector)
return getDistance((DoubleVector) o1, (DoubleVector) o2);
else
throw new IllegalArgumentException(
"LMetric cannot compute distance on " + o1.getClass()
+ " and " + o2.getClass());
}
public double getDistance(DoubleVector dv1, DoubleVector dv2) {
return getDistance(dv1.getData(), dv2.getData());
}
/**
* Computes the distance between two double arrays with the same dimension.
*
* @param a1
* the first double array
* @param a2
* the second double array
* @return the distance between the two arrays
*/
public double getDistance(double[] a1, double[] a2) {
// check arguments
if (a1 == null)
throw new IllegalArgumentException(
"the first argument is null calling getDistance() of LMetric");
if (a2 == null)
throw new IllegalArgumentException(
"the second argument is null calling getDistance() of LMetric");
if (a1.length != a2.length)
throw new IllegalArgumentException(
"the two arraies are of different lengths (" + a1.length
+ ", " + a2.length
+ ") calling getDistance() of LMetric");
final int length = a1.length;
double distance = 0;
// infinite distance
if (dim == 0) {
for (int i = 0; i < length; i++)
distance = Math.max(distance, Math.abs(a1[i] - a2[i]));
}
// else finite distance
else {
for (int i = 0; i < length; i++)
distance += Math.pow(Math.abs(a1[i] - a2[i]), dim);
distance = Math.pow(distance, 1 / (double) dim);
}
return distance;
}
/**
* @return the dimension of the metric
*/
public int getDimension() {
return dim;
}
/**
* main method, for test, 3 arguments argument 1: dimension of the metric
* argument 2,3: the two vectors, dimension by dimension, seperated by
* comma, no space.
*/
public static void main(String args[]) {
if (args.length != 3) {
System.out
.println("3 arguments: argument 1: dimension of the metric, argument 2,3: "
+ "the two vectors, dimension by dimension, seperated by comma, no space.");
return;
}
final int dim = Integer.parseInt(args[0]);
String[] a1String = args[1].split(",");
String[] a2String = args[2].split(",");
double[] a1 = new double[a1String.length];
for (int i = 0; i < a1.length; i++)
a1[i] = Double.parseDouble(a1String[i]);
double[] a2 = new double[a2String.length];
for (int i = 0; i < a2.length; i++)
a2[i] = Double.parseDouble(a2String[i]);
System.out.println("distance = "
+ (new LMetric(dim)).getDistance(a1, a2));
}
}