source: osm/applications/editors/josm/plugins/utilsplugin2/src/edu/princeton/cs/algs4/AssignmentProblem.java

Last change on this file was 28116, checked in by joshdoe, 14 years ago

utilsplugin2: use improved assignment method for Replace Geometry (see #josm7295)

File size: 6.0 KB
Line 
1// License: GPL v3 or later courtesy of author Kevin Wayne
2package edu.princeton.cs.algs4;
3
4/*************************************************************************
5 * Compilation: javac AssignmentProblem.java
6 * Execution: java AssignmentProblem N
7 * Dependencies: DijkstraSP.java DirectedEdge.java
8 *
9 * Solve an N-by-N assignment problem in N^3 log N time using the
10 * successive shortest path algorithm.
11 *
12 * Remark: could use dense version of Dijsktra's algorithm for
13 * improved theoretical efficiency of N^3, but it doesn't seem to
14 * help in practice.
15 *
16 * Assumes N-by-N cost matrix is nonnegative.
17 *
18 *
19 *********************************************************************/
20
21public class AssignmentProblem {
22 private static final int UNMATCHED = -1;
23
24 private int N; // number of rows and columns
25 private double[][] weight; // the N-by-N cost matrix
26 private double[] px; // px[i] = dual variable for row i
27 private double[] py; // py[j] = dual variable for col j
28 private int[] xy; // xy[i] = j means i-j is a match
29 private int[] yx; // yx[j] = i means i-j is a match
30
31
32 public AssignmentProblem(double[][] weight) {
33 N = weight.length;
34 this.weight = new double[N][N];
35 for (int i = 0; i < N; i++)
36 for (int j = 0; j < N; j++)
37 this.weight[i][j] = weight[i][j];
38
39 // dual variables
40 px = new double[N];
41 py = new double[N];
42
43 // initial matching is empty
44 xy = new int[N];
45 yx = new int[N];
46 for (int i = 0; i < N; i++) xy[i] = UNMATCHED;
47 for (int j = 0; j < N; j++) yx[j] = UNMATCHED;
48
49 // add N edges to matching
50 for (int k = 0; k < N; k++) {
51 assert isDualFeasible();
52 assert isComplementarySlack();
53 augment();
54 }
55 assert check();
56 }
57
58 // find shortest augmenting path and upate
59 private void augment() {
60
61 // build residual graph
62 EdgeWeightedDigraph G = new EdgeWeightedDigraph(2*N+2);
63 int s = 2*N, t = 2*N+1;
64 for (int i = 0; i < N; i++) {
65 if (xy[i] == UNMATCHED) G.addEdge(new DirectedEdge(s, i, 0.0));
66 }
67 for (int j = 0; j < N; j++) {
68 if (yx[j] == UNMATCHED) G.addEdge(new DirectedEdge(N+j, t, py[j]));
69 }
70 for (int i = 0; i < N; i++) {
71 for (int j = 0; j < N; j++) {
72 if (xy[i] == j) G.addEdge(new DirectedEdge(N+j, i, 0.0));
73 else G.addEdge(new DirectedEdge(i, N+j, reduced(i, j)));
74 }
75 }
76
77 // compute shortest path from s to every other vertex
78 DijkstraSP spt = new DijkstraSP(G, s);
79
80 // augment along alternating path
81 for (DirectedEdge e : spt.pathTo(t)) {
82 int i = e.from(), j = e.to() - N;
83 if (i < N) {
84 xy[i] = j;
85 yx[j] = i;
86 }
87 }
88
89 // update dual variables
90 for (int i = 0; i < N; i++) px[i] += spt.distTo(i);
91 for (int j = 0; j < N; j++) py[j] += spt.distTo(N+j);
92 }
93
94 // reduced cost of i-j
95 private double reduced(int i, int j) {
96 return weight[i][j] + px[i] - py[j];
97 }
98
99 // total weight of min weight perfect matching
100 public double weight() {
101 double total = 0.0;
102 for (int i = 0; i < N; i++) {
103 if (xy[i] != UNMATCHED)
104 total += weight[i][xy[i]];
105 }
106 return total;
107 }
108
109 public int sol(int i) {
110 return xy[i];
111 }
112
113 // check that dual variables are feasible
114 private boolean isDualFeasible() {
115 // check that all edges have >= 0 reduced cost
116 for (int i = 0; i < N; i++) {
117 for (int j = 0; j < N; j++) {
118 if (reduced(i, j) < 0) {
119// StdOut.println("Dual variables are not feasible");
120 return false;
121 }
122 }
123 }
124 return true;
125 }
126
127 // check that primal and dual variables are complementary slack
128 private boolean isComplementarySlack() {
129
130 // check that all matched edges have 0-reduced cost
131 for (int i = 0; i < N; i++) {
132 if ((xy[i] != UNMATCHED) && (reduced(i, xy[i]) != 0)) {
133// StdOut.println("Primal and dual variables are not complementary slack");
134 return false;
135 }
136 }
137 return true;
138 }
139
140 // check that primal variables are a perfect matching
141 private boolean isPerfectMatching() {
142
143 // check that xy[] is a perfect matching
144 boolean[] perm = new boolean[N];
145 for (int i = 0; i < N; i++) {
146 if (perm[xy[i]]) {
147// StdOut.println("Not a perfect matching");
148 return false;
149 }
150 perm[xy[i]] = true;
151 }
152
153 // check that xy[] and yx[] are inverses
154 for (int j = 0; j < N; j++) {
155 if (xy[yx[j]] != j) {
156// StdOut.println("xy[] and yx[] are not inverses");
157 return false;
158 }
159 }
160 for (int i = 0; i < N; i++) {
161 if (yx[xy[i]] != i) {
162// StdOut.println("xy[] and yx[] are not inverses");
163 return false;
164 }
165 }
166
167 return true;
168 }
169
170
171 // check optimality conditions
172 private boolean check() {
173 return isPerfectMatching() && isDualFeasible() && isComplementarySlack();
174 }
175
176// public static void main(String[] args) {
177// In in = new In(args[0]);
178// int N = in.readInt();
179// double[][] weight = new double[N][N];
180// for (int i = 0; i < N; i++) {
181// for (int j = 0; j < N; j++) {
182// weight[i][j] = in.readDouble();
183// }
184// }
185//
186// AssignmentProblem assignment = new AssignmentProblem(weight);
187// StdOut.println("weight = " + assignment.weight());
188// for (int i = 0; i < N; i++)
189// StdOut.println(i + "-" + assignment.sol(i) + " " + weight[i][assignment.sol(i)]);
190// }
191
192}
Note: See TracBrowser for help on using the repository browser.