詳細實現思路可以看論文:
弗雷歇算法文獻
下面是java版本的實現
package momenta.hdmap.diffosm.Frechet;
import org.locationtech.jts.geom.Coordinate;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
public class DiscreteFrechetDistance {
/**
* Dynamic programming memory array
*/
private static double[][] mem;
private static List<Coordinate> timeSeriesP;
private static List<Coordinate> timeSeriesQ;
/**-
* 計算Frechet距離
* @param cp
* @param cq
* @return
*/
public static double getDiscreteFrechet(Coordinate[] cp, Coordinate[] cq) {
timeSeriesP = Arrays.asList(cp);
timeSeriesQ = Arrays.asList(cq);
double distance = computeDiscreteFrechet(timeSeriesP, timeSeriesQ);
// 四捨五入保留兩位小數輸出結果 (單位米)
BigDecimal b = new BigDecimal(distance);
return b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* Wrapper that makes a call to computeDFD. Initializes mem array with all
* -1 values.
*
* @param P - the first time series
* @param Q - the second time series
* @return The length of the shortest distance that can traverse both time
* series.
*/
private static double computeDiscreteFrechet(List<Coordinate> P, List<Coordinate> Q) {
mem = new double[P.size()][Q.size()];
// initialize all values to -1
for (int i = 0; i < mem.length; i++) {
for (int j = 0; j < mem[i].length; j++) {
mem[i][j] = -1.0;
}
}
return computeDFD(P.size() - 1, Q.size() - 1);
}
/**
* @param i - the row
* @param j - the column
* @return The length of the shortest distance that can traverse both time
* series.
*/
private static double computeDFD(int i, int j) {
// if the value has already been solved
if (mem[i][j] > -1)
return mem[i][j];
// if top left column, just compute the distance
else if (i == 0 && j == 0)
mem[i][j] = euclideanDistance(timeSeriesP.get(i), timeSeriesQ.get(j));
// can either be the actual distance or distance pulled from above
else if (i > 0 && j == 0)
mem[i][j] = max(computeDFD(i - 1, 0), euclideanDistance(timeSeriesP.get(i), timeSeriesQ.get(j)));
// can either be the distance pulled from the left or the actual
// distance
else if (i == 0 && j > 0)
mem[i][j] = max(computeDFD(0, j - 1), euclideanDistance(timeSeriesP.get(i), timeSeriesQ.get(j)));
// can be the actual distance, or distance from above or from the left
else if (i > 0 && j > 0) {
mem[i][j] = max(min(computeDFD(i - 1, j), computeDFD(i - 1, j - 1), computeDFD(i, j - 1)), euclideanDistance(timeSeriesP.get(i), timeSeriesQ.get(j)));
}
// infinite
else
mem[i][j] = Integer.MAX_VALUE;
// printMemory();
// return the DFD
return mem[i][j];
}
/**
* Get the max value of all the values.
*
* @param values - the values being compared
* @return The max value of all the values.
*/
private static double max(double... values) {
double max = Integer.MIN_VALUE;
for (double i : values) {
if (i >= max)
max = i;
}
return max;
}
/**
* Get the minimum value of all the values.
*
* @param values - the values being compared
* @return The minimum value of all the values.
*/
private static double min(double... values) {
double min = Integer.MAX_VALUE;
for (double i : values) {
if (i <= min)
min = i;
}
return min;
}
/**
* Given two points, calculate the Euclidean distance between them, where
* the Euclidean distance: sum from 1 to n dimensions of ((x - y)^2)^1/2
*
* @param i - the first point
* @param j - the second point
* @return The total Euclidean distance between two points.
*/
private static double euclideanDistance(Coordinate i, Coordinate j) {
double distance = 0;
distance = i.distance(j) * 100000;
// System.out.println(i.toString() + "_" + j.toString() + "_dis:" + distance);
return distance;
}
/**
* Test method that prints the 2D dynamic programming array.
*/
private static void printMemory() {
System.out.println("\n\n memory");
for (int row = 0; row < mem.length; row++) {
for (int col = 0; col < mem[row].length; col++) {
System.out.print(mem[row][col] + "\t");
}
System.out.println();
}
}
}