(6.0, false, 9.0, false);
140 |
141 | Assert.assertTrue(i1.overlaps(i1));
142 |
143 | Assert.assertTrue(i1.overlaps(i2));
144 | Assert.assertFalse(i1.overlaps(i3));
145 | Assert.assertTrue(i1.overlaps(i4));
146 | Assert.assertFalse(i1.overlaps(i5));
147 | Assert.assertTrue(i1.overlaps(i6));
148 |
149 | Assert.assertTrue(i2.overlaps(i1));
150 | Assert.assertFalse(i3.overlaps(i1));
151 | Assert.assertTrue(i4.overlaps(i1));
152 | Assert.assertFalse(i5.overlaps(i1));
153 | Assert.assertTrue(i6.overlaps(i1));
154 | }
155 |
156 | @Test
157 | public void testToString() {
158 | Assert.assertEquals("(0, 1)", new Interval(0, false, 1, false).toString());
159 | Assert.assertEquals("(0, 1]", new Interval(0, false, 1, true).toString());
160 | Assert.assertEquals("[0, 1)", new Interval(0, true, 1, false).toString());
161 | Assert.assertEquals("[0, 1]", new Interval(0, true, 1, true).toString());
162 | }
163 | }
164 |
--------------------------------------------------------------------------------
/src/main/java/blogspot/software_and_algorithms/stern_library/string/DamerauLevenshteinAlgorithm.java:
--------------------------------------------------------------------------------
1 | package blogspot.software_and_algorithms.stern_library.string;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /* Copyright (c) 2012 Kevin L. Stern
7 | *
8 | * Permission is hereby granted, free of charge, to any person obtaining a copy
9 | * of this software and associated documentation files (the "Software"), to deal
10 | * in the Software without restriction, including without limitation the rights
11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 | * copies of the Software, and to permit persons to whom the Software is
13 | * furnished to do so, subject to the following conditions:
14 | *
15 | * The above copyright notice and this permission notice shall be included in
16 | * all copies or substantial portions of the Software.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | * SOFTWARE.
25 | */
26 |
27 | /**
28 | * The Damerau-Levenshtein Algorithm is an extension to the Levenshtein
29 | * Algorithm which solves the edit distance problem between a source string and
30 | * a target string with the following operations:
31 | *
32 | *
33 | * - Character Insertion
34 | * - Character Deletion
35 | * - Character Replacement
36 | * - Adjacent Character Swap
37 | *
38 | *
39 | * Note that the adjacent character swap operation is an edit that may be
40 | * applied when two adjacent characters in the source string match two adjacent
41 | * characters in the target string, but in reverse order, rather than a general
42 | * allowance for adjacent character swaps.
43 | *
44 | *
45 | * This implementation allows the client to specify the costs of the various
46 | * edit operations with the restriction that the cost of two swap operations
47 | * must not be less than the cost of a delete operation followed by an insert
48 | * operation. This restriction is required to preclude two swaps involving the
49 | * same character being required for optimality which, in turn, enables a fast
50 | * dynamic programming solution.
51 | *
52 | *
53 | * The running time of the Damerau-Levenshtein algorithm is O(n*m) where n is
54 | * the length of the source string and m is the length of the target string.
55 | * This implementation consumes O(n*m) space.
56 | *
57 | * @author Kevin L. Stern
58 | */
59 | public class DamerauLevenshteinAlgorithm {
60 | private final int deleteCost, insertCost, replaceCost, swapCost;
61 |
62 | /**
63 | * Constructor.
64 | *
65 | * @param deleteCost
66 | * the cost of deleting a character.
67 | * @param insertCost
68 | * the cost of inserting a character.
69 | * @param replaceCost
70 | * the cost of replacing a character.
71 | * @param swapCost
72 | * the cost of swapping two adjacent characters.
73 | */
74 | public DamerauLevenshteinAlgorithm(int deleteCost, int insertCost,
75 | int replaceCost, int swapCost) {
76 | /*
77 | * Required to facilitate the premise to the algorithm that two swaps of the
78 | * same character are never required for optimality.
79 | */
80 | if (2 * swapCost < insertCost + deleteCost) {
81 | throw new IllegalArgumentException("Unsupported cost assignment");
82 | }
83 | this.deleteCost = deleteCost;
84 | this.insertCost = insertCost;
85 | this.replaceCost = replaceCost;
86 | this.swapCost = swapCost;
87 | }
88 |
89 | /**
90 | * Compute the Damerau-Levenshtein distance between the specified source
91 | * string and the specified target string.
92 | */
93 | public int execute(String source, String target) {
94 | if (source.length() == 0) {
95 | return target.length() * insertCost;
96 | }
97 | if (target.length() == 0) {
98 | return source.length() * deleteCost;
99 | }
100 | int[][] table = new int[source.length()][target.length()];
101 | Map sourceIndexByCharacter = new HashMap();
102 | if (source.charAt(0) != target.charAt(0)) {
103 | table[0][0] = Math.min(replaceCost, deleteCost + insertCost);
104 | }
105 | sourceIndexByCharacter.put(source.charAt(0), 0);
106 | for (int i = 1; i < source.length(); i++) {
107 | int deleteDistance = table[i - 1][0] + deleteCost;
108 | int insertDistance = (i + 1) * deleteCost + insertCost;
109 | int matchDistance = i * deleteCost
110 | + (source.charAt(i) == target.charAt(0) ? 0 : replaceCost);
111 | table[i][0] = Math.min(Math.min(deleteDistance, insertDistance),
112 | matchDistance);
113 | }
114 | for (int j = 1; j < target.length(); j++) {
115 | int deleteDistance = (j + 1) * insertCost + deleteCost;
116 | int insertDistance = table[0][j - 1] + insertCost;
117 | int matchDistance = j * insertCost
118 | + (source.charAt(0) == target.charAt(j) ? 0 : replaceCost);
119 | table[0][j] = Math.min(Math.min(deleteDistance, insertDistance),
120 | matchDistance);
121 | }
122 | for (int i = 1; i < source.length(); i++) {
123 | int maxSourceLetterMatchIndex = source.charAt(i) == target.charAt(0) ? 0
124 | : -1;
125 | for (int j = 1; j < target.length(); j++) {
126 | Integer candidateSwapIndex = sourceIndexByCharacter.get(target
127 | .charAt(j));
128 | int jSwap = maxSourceLetterMatchIndex;
129 | int deleteDistance = table[i - 1][j] + deleteCost;
130 | int insertDistance = table[i][j - 1] + insertCost;
131 | int matchDistance = table[i - 1][j - 1];
132 | if (source.charAt(i) != target.charAt(j)) {
133 | matchDistance += replaceCost;
134 | } else {
135 | maxSourceLetterMatchIndex = j;
136 | }
137 | int swapDistance;
138 | if (candidateSwapIndex != null && jSwap != -1) {
139 | int iSwap = candidateSwapIndex;
140 | int preSwapCost;
141 | if (iSwap == 0 && jSwap == 0) {
142 | preSwapCost = 0;
143 | } else {
144 | preSwapCost = table[Math.max(0, iSwap - 1)][Math.max(0, jSwap - 1)];
145 | }
146 | swapDistance = preSwapCost + (i - iSwap - 1) * deleteCost
147 | + (j - jSwap - 1) * insertCost + swapCost;
148 | } else {
149 | swapDistance = Integer.MAX_VALUE;
150 | }
151 | table[i][j] = Math.min(Math.min(Math
152 | .min(deleteDistance, insertDistance), matchDistance), swapDistance);
153 | }
154 | sourceIndexByCharacter.put(source.charAt(i), i);
155 | }
156 | return table[source.length() - 1][target.length() - 1];
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/main/java/blogspot/software_and_algorithms/stern_library/data_structure/Interval.java:
--------------------------------------------------------------------------------
1 | package blogspot.software_and_algorithms.stern_library.data_structure;
2 |
3 | /* Copyright (c) 2012 Kevin L. Stern
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in
13 | * all copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 |
24 | /**
25 | * An interval is the subset of elements which fall between (with
26 | * respect to a total order) two endpoint elements of a set. An interval that
27 | * contains its endpoints is closed, an interval that contains one of
28 | * its endpoints but not the other is half open and an interval that
29 | * does not contain either of its endpoints is open. This class
30 | * encapsulates the concept of an interval and uses a class's natural order.
31 | *
32 | * @author Kevin L. Stern
33 | */
34 | public class Interval> implements
35 | Comparable> {
36 | private T low, high;
37 | private boolean isClosedOnLow, isClosedOnHigh;
38 | private int hashCode = 0;
39 |
40 | /**
41 | * Construct a new instance with the specified low and high endpoints.
42 | *
43 | * @param low
44 | * the low endpoint.
45 | * @param isClosedOnLow
46 | * true if this interval contains its low endpoint, false otherwise.
47 | * @param high
48 | * the high endpoint.
49 | * @param isClosedOnHigh
50 | * true if this interval contains its high endpoint, false otherwise.
51 | */
52 | public Interval(T low, boolean isClosedOnLow, T high, boolean isClosedOnHigh) {
53 | if (low == null) {
54 | throw new NullPointerException("low endpoint is null");
55 | } else if (high == null) {
56 | throw new NullPointerException("high endpoint is null");
57 | }
58 | this.low = low;
59 | this.isClosedOnLow = isClosedOnLow;
60 | this.high = high;
61 | this.isClosedOnHigh = isClosedOnHigh;
62 | }
63 |
64 | /**
65 | * {@inheritDoc}
66 | */
67 | @Override
68 | public int compareTo(Interval o) {
69 | int result = low.compareTo(o.low);
70 | if (result == 0) {
71 | if (isClosedOnLow != o.isClosedOnLow) {
72 | result = isClosedOnLow ? -1 : 1;
73 | } else {
74 | result = high.compareTo(o.high);
75 | if (result == 0) {
76 | if (isClosedOnHigh != o.isClosedOnHigh) {
77 | result = isClosedOnHigh ? -1 : 1;
78 | }
79 | }
80 | }
81 | }
82 | return result;
83 | }
84 |
85 | /**
86 | * Test whether or not this interval contains the specified interval. An
87 | * interval is contained by another precisely when all of its values are
88 | * contained by the other.
89 | *
90 | * @param interval
91 | * the query interval, non-null.
92 | * @return true if this interval contains the specified interval, false
93 | * otherwise.
94 | */
95 | public boolean contains(Interval interval) {
96 | boolean lowIsLowerBound = low.equals(interval.low)
97 | && (isClosedOnLow || !interval.isClosedOnLow)
98 | || low.compareTo(interval.low) < 0;
99 | boolean highIsUpperBound = high.equals(interval.high)
100 | && (isClosedOnHigh || !interval.isClosedOnHigh)
101 | || high.compareTo(interval.high) > 0;
102 | return lowIsLowerBound && highIsUpperBound;
103 | }
104 |
105 | /**
106 | * Test whether or not this interval contains the specified value.
107 | *
108 | * @param value
109 | * the query value, non-null.
110 | * @return true if this interval contains the specified value, false
111 | * otherwise.
112 | */
113 | public boolean contains(T value) {
114 | return value.equals(low) && isClosedOnLow || value.equals(high)
115 | && isClosedOnHigh || low.compareTo(value) < 0
116 | && value.compareTo(high) < 0;
117 | }
118 |
119 | /**
120 | * {@inheritDoc}
121 | */
122 | @Override
123 | public boolean equals(Object obj) {
124 | if (this == obj)
125 | return true;
126 | if (obj == null)
127 | return false;
128 | if (getClass() != obj.getClass())
129 | return false;
130 | Interval other = (Interval) obj;
131 | if (high == null) {
132 | if (other.high != null)
133 | return false;
134 | } else if (!high.equals(other.high))
135 | return false;
136 | if (isClosedOnHigh != other.isClosedOnHigh)
137 | return false;
138 | if (low == null) {
139 | if (other.low != null)
140 | return false;
141 | } else if (!low.equals(other.low))
142 | return false;
143 | if (isClosedOnLow != other.isClosedOnLow)
144 | return false;
145 | return true;
146 | }
147 |
148 | /**
149 | * Get the high endpoint.
150 | *
151 | * @return the high endpoint.
152 | */
153 | public T getHigh() {
154 | return high;
155 | }
156 |
157 | /**
158 | * Get the low endpoint.
159 | *
160 | * @return the low endpoint.
161 | */
162 | public T getLow() {
163 | return low;
164 | }
165 |
166 | /**
167 | * {@inheritDoc}
168 | */
169 | @Override
170 | public int hashCode() {
171 | if (hashCode == 0) {
172 | final int prime = 31;
173 | int result = 1;
174 | result = prime * result + ((high == null) ? 0 : high.hashCode());
175 | result = prime * result + (isClosedOnHigh ? 1231 : 1237);
176 | result = prime * result + ((low == null) ? 0 : low.hashCode());
177 | result = prime * result + (isClosedOnLow ? 1231 : 1237);
178 | hashCode = result;
179 | }
180 | return hashCode;
181 | }
182 |
183 | /**
184 | *
185 | * @return true if this interval is closed at its high endpoint, false
186 | * otherwise.
187 | */
188 | public boolean isClosedOnHigh() {
189 | return isClosedOnHigh;
190 | }
191 |
192 | /**
193 | *
194 | * @return true if the interval is closed at its low endpoint, false
195 | * otherwise.
196 | */
197 | public boolean isClosedOnLow() {
198 | return isClosedOnLow;
199 | }
200 |
201 | /**
202 | * Test whether or not this interval and the specified interval overlap. Two
203 | * intervals overlap precisely when their intersection is non-empty.
204 | *
205 | * @param interval
206 | * the query interval.
207 | * @return true if this interval and the specified interval overlap, false
208 | * otherwise.
209 | */
210 | public boolean overlaps(Interval interval) {
211 | if (interval.isClosedOnLow && contains(interval.low) || isClosedOnLow
212 | && interval.contains(low)) {
213 | return true;
214 | }
215 | if (!interval.isClosedOnLow && low.compareTo(interval.low) <= 0
216 | && interval.low.compareTo(high) < 0 || !isClosedOnLow
217 | && interval.low.compareTo(low) <= 0 && low.compareTo(interval.high) < 0) {
218 | return true;
219 | }
220 | return false;
221 | }
222 |
223 | /**
224 | * {@inheritDoc}
225 | */
226 | @Override
227 | public String toString() {
228 | String format;
229 | if (isClosedOnLow) {
230 | if (isClosedOnHigh) {
231 | format = "[%s, %s]";
232 | } else {
233 | format = "[%s, %s)";
234 | }
235 | } else {
236 | if (isClosedOnHigh) {
237 | format = "(%s, %s]";
238 | } else {
239 | format = "(%s, %s)";
240 | }
241 | }
242 | return String.format(format, low.toString(), high.toString());
243 | }
244 | }
--------------------------------------------------------------------------------
/src/main/java/blogspot/software_and_algorithms/stern_library/geometry/ClosestPointPairAlgorithm.java:
--------------------------------------------------------------------------------
1 | package blogspot.software_and_algorithms.stern_library.geometry;
2 |
3 | import java.awt.geom.Point2D;
4 | import java.util.ArrayList;
5 | import java.util.Collection;
6 | import java.util.Collections;
7 | import java.util.HashSet;
8 | import java.util.List;
9 | import java.util.Set;
10 |
11 | /* Copyright (c) 2012 Kevin L. Stern
12 | *
13 | * Permission is hereby granted, free of charge, to any person obtaining a copy
14 | * of this software and associated documentation files (the "Software"), to deal
15 | * in the Software without restriction, including without limitation the rights
16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 | * copies of the Software, and to permit persons to whom the Software is
18 | * furnished to do so, subject to the following conditions:
19 | *
20 | * The above copyright notice and this permission notice shall be included in
21 | * all copies or substantial portions of the Software.
22 | *
23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 | * SOFTWARE.
30 | */
31 |
32 | /**
33 | * An implementation of a divide-and-conquer algorithm for computing the closest
34 | * pair of elements of a set of points. The algorithm consists of constructing
35 | * an ordered list of points, then recursively dividing the list into a left and
36 | * right sublist towards finding the closest point pairs for each sublist. The
37 | * two sub-results are merged by selecting the optimal among them and all closer
38 | * point pairs that cross the boundary of separation. Happily, only a linear
39 | * amount of work is required to find all closer point pairs that cross the
40 | * boundary, giving a total runtime of O(n*log(n)) for the algorithm.
41 | *
42 | * @author Kevin L. Stern
43 | */
44 | public class ClosestPointPairAlgorithm {
45 | /**
46 | * Find the closest pair of points among p1, p2 and p3.
47 | */
48 | private static PairStructure closestPair(Point2D p1, Point2D p2, Point2D p3) {
49 | double d1 = p1.distanceSq(p2);
50 | double d2 = p2.distanceSq(p3);
51 | double d3 = p1.distanceSq(p3);
52 | if (d1 < d2) {
53 | if (d1 < d3) {
54 | return new PairStructure(p1, p2, d1);
55 | } else {
56 | return new PairStructure(p1, p3, d3);
57 | }
58 | } else {
59 | if (d2 < d3) {
60 | return new PairStructure(p2, p3, d2);
61 | } else {
62 | return new PairStructure(p1, p3, d3);
63 | }
64 | }
65 | }
66 |
67 | private List pointsOrderedByXCoordinate, pointsOrderedByYCoordinate;
68 |
69 | /**
70 | * Construct an instance of the algorithm for the specified point Collection.
71 | *
72 | * @param points
73 | * the Collection of points through which to search for the closest
74 | * pair.
75 | */
76 | public ClosestPointPairAlgorithm(Collection points) {
77 | if (points == null) {
78 | throw new NullPointerException("points is null");
79 | }
80 | if (points.size() < 2) {
81 | throw new IllegalArgumentException("points is too small");
82 | }
83 | pointsOrderedByXCoordinate = new ArrayList(points);
84 | Collections.sort(pointsOrderedByXCoordinate, (o1, o2) -> {
85 | double delta = o1.getX() - o2.getX();
86 | if (delta == 0.0) {
87 | delta = o1.getY() - o2.getY();
88 | }
89 | return delta < 0 ? -1 : delta > 0 ? 1 : 0;
90 | });
91 | pointsOrderedByYCoordinate = new ArrayList(points);
92 | Collections.sort(pointsOrderedByYCoordinate, (o1, o2) -> {
93 | double delta = o1.getY() - o2.getY();
94 | if (delta == 0.0) {
95 | delta = o1.getX() - o2.getX();
96 | }
97 | return delta < 0 ? -1 : delta > 0 ? 1 : 0;
98 | });
99 | }
100 |
101 | /**
102 | * Internal helper method which implements the closest point pair algorithm.
103 | *
104 | * @param low
105 | * the starting index, inclusive, of the sublist in which to search
106 | * for the closest point pair.
107 | * @param high
108 | * the ending index, exclusive, of the sublist in which to search for
109 | * the closest point pair.
110 | * @param localPointsOrderedByYCoordinate
111 | * the points from the target sublist, ordered by y coordinate.
112 | * @return a PairStructure containing the closest point pair among elements of
113 | * the target sublist.
114 | */
115 | protected PairStructure closestPair(int low, int high,
116 | List localPointsOrderedByYCoordinate) {
117 | int size = high - low;
118 | if (size == 3) {
119 | return closestPair(
120 | pointsOrderedByXCoordinate.get(low),
121 | pointsOrderedByXCoordinate.get(low + 1),
122 | pointsOrderedByXCoordinate.get(low + 2));
123 | } else if (size == 2) {
124 | Point2D p1 = pointsOrderedByXCoordinate.get(low);
125 | Point2D p2 = pointsOrderedByXCoordinate.get(low + 1);
126 | return new PairStructure(p1, p2, p1.distanceSq(p2));
127 | }
128 |
129 | int mid = (low + high) >> 1; // (low + high) / 2
130 | Set leftSubtreeMemberSet = new HashSet(mid - low);
131 | for (int j = low; j < mid; j++) {
132 | leftSubtreeMemberSet.add(pointsOrderedByXCoordinate.get(j));
133 | }
134 |
135 | /*
136 | * Construct the lists of points ordered by y coordinate for the left and
137 | * right subtrees in linear time by drawing upon the master list of points
138 | * ordered by y coordinate.
139 | */
140 | List leftPointsOrderedByYCoordinate = new ArrayList(mid
141 | - low);
142 | List rightPointsOrderedByYCoordinate = new ArrayList(high
143 | - mid);
144 | for (Point2D next : localPointsOrderedByYCoordinate) {
145 | if (leftSubtreeMemberSet.contains(next)) {
146 | leftPointsOrderedByYCoordinate.add(next);
147 | } else {
148 | rightPointsOrderedByYCoordinate.add(next);
149 | }
150 | }
151 |
152 | PairStructure leftSubtreeResult = closestPair(
153 | low,
154 | mid,
155 | leftPointsOrderedByYCoordinate);
156 | PairStructure rightSubtreeResult = closestPair(
157 | mid,
158 | high,
159 | rightPointsOrderedByYCoordinate);
160 | PairStructure result = leftSubtreeResult.distanceSq < rightSubtreeResult.distanceSq
161 | ? leftSubtreeResult
162 | : rightSubtreeResult;
163 |
164 | List boundaryPointsOrderedByYCoordinate = new ArrayList();
165 | double midXCoordinate = pointsOrderedByXCoordinate.get(mid).getX();
166 | for (Point2D next : localPointsOrderedByYCoordinate) {
167 | double v = next.getX() - midXCoordinate;
168 | if (v * v < result.distanceSq) {
169 | boundaryPointsOrderedByYCoordinate.add(next);
170 | }
171 | }
172 | for (int i = 0; i < boundaryPointsOrderedByYCoordinate.size(); ++i) {
173 | Point2D currentPoint = boundaryPointsOrderedByYCoordinate.get(i);
174 | int index;
175 | for (int j = 1; (index = i + j) < boundaryPointsOrderedByYCoordinate
176 | .size(); ++j) {
177 | Point2D testPoint = boundaryPointsOrderedByYCoordinate.get(index);
178 | /*
179 | * The number of points that can be situated within the boundary so that
180 | * their y coordinate is within the minimum of the result distances for
181 | * the left and right subtrees from currentPoint.getY() is bounded by a
182 | * constant, since that distance value spatially limits the number of
183 | * points that can be packed near one another on each side of the
184 | * boundary.
185 | */
186 | double v = testPoint.getY() - currentPoint.getY();
187 | if (v * v >= result.distanceSq) {
188 | break;
189 | }
190 | double testDistance = currentPoint.distanceSq(testPoint);
191 | if (testDistance < result.distanceSq) {
192 | result = new PairStructure(currentPoint, testPoint, testDistance);
193 | }
194 | }
195 | }
196 |
197 | return result;
198 | }
199 |
200 | /**
201 | * Execute the algorithm.
202 | *
203 | * @return a Point2D[] containing exactly two elements which are the closest
204 | * pair of points among those in the collection used to construct this
205 | * instance.
206 | */
207 | public Point2D[] execute() {
208 | PairStructure result = closestPair(
209 | 0,
210 | pointsOrderedByXCoordinate.size(),
211 | pointsOrderedByYCoordinate);
212 | return new Point2D[] { result.p1, result.p2 };
213 | }
214 |
215 | /**
216 | * Convenience data structure to hold a pair of points along with their
217 | * distance from one another.
218 | */
219 | protected static class PairStructure {
220 | private Point2D p1, p2;
221 | private double distanceSq;
222 |
223 | /**
224 | * Constructor.
225 | *
226 | * @param p1
227 | * the first point.
228 | * @param p2
229 | * the second point.
230 | * @param distanceSq
231 | * the distance between p1 and p2, squared.
232 | */
233 | public PairStructure(Point2D p1, Point2D p2, double distanceSq) {
234 | this.p1 = p1;
235 | this.p2 = p2;
236 | this.distanceSq = distanceSq;
237 | }
238 | }
239 | }
240 |
--------------------------------------------------------------------------------
/src/test/java/blogspot/software_and_algorithms/stern_library/data_structure/DynamicIntervalTreeTest.java:
--------------------------------------------------------------------------------
1 | package blogspot.software_and_algorithms.stern_library.data_structure;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.HashSet;
6 | import java.util.List;
7 | import java.util.Set;
8 |
9 | import org.junit.AfterClass;
10 | import org.junit.Assert;
11 | import org.junit.BeforeClass;
12 | import org.junit.Test;
13 |
14 | /* Copyright (c) 2012 Kevin L. Stern
15 | *
16 | * Permission is hereby granted, free of charge, to any person obtaining a copy
17 | * of this software and associated documentation files (the "Software"), to deal
18 | * in the Software without restriction, including without limitation the rights
19 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20 | * copies of the Software, and to permit persons to whom the Software is
21 | * furnished to do so, subject to the following conditions:
22 | *
23 | * The above copyright notice and this permission notice shall be included in
24 | * all copies or substantial portions of the Software.
25 | *
26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 | * SOFTWARE.
33 | */
34 |
35 | /**
36 | * Test class for DynamicIntervalTree.
37 | *
38 | * @author Kevin L. Stern
39 | */
40 | public class DynamicIntervalTreeTest {
41 | private static List> masterClosed;
42 | private static List> masterHalfOpenLow;
43 | private static List> masterHalfOpenHigh;
44 | private static List> masterOpen;
45 | private static DynamicIntervalTree> masterClosedTree;
46 | private static DynamicIntervalTree> masterHalfOpenLowTree;
47 | private static DynamicIntervalTree> masterHalfOpenHighTree;
48 | private static DynamicIntervalTree> masterOpenTree;
49 |
50 | private static void executeContainingIntervalsTest(List queryPoints,
51 | List> master,
52 | DynamicIntervalTree> tree) {
53 | Set> result = new HashSet>();
54 | for (Double queryPoint : queryPoints) {
55 | result.addAll(tree.fetchContainingIntervals(queryPoint));
56 | Assert.assertEquals(fetchContainingIntervals(master, queryPoint), result);
57 | result.clear();
58 | }
59 | }
60 |
61 | private static void executeOverlappingIntervalsTest(List> queryIntervals,
62 | List> master,
63 | DynamicIntervalTree> tree) {
64 | Set> result = new HashSet>();
65 | for (Interval next : queryIntervals) {
66 | result.addAll(tree.fetchOverlappingIntervals(next));
67 | Assert.assertEquals(fetchOverlappingIntervals(master, next), result);
68 | result.clear();
69 | }
70 | }
71 |
72 | /**
73 | * Helper method to fetch the set of intervals from the master list that
74 | * contain the query point using the brute force method. This gives a correct
75 | * answer against which to compare the result from the interval tree.
76 | */
77 | private static Set> fetchContainingIntervals(List> master,
78 | double queryPoint) {
79 | Set> result = new HashSet>();
80 | for (Interval next : master) {
81 | if (next.contains(queryPoint)) {
82 | result.add(next);
83 | }
84 | }
85 | return result;
86 | }
87 |
88 | /**
89 | * Helper method to fetch the set of intervals from the master list that
90 | * overlap the query interval using the brute force method. This gives a
91 | * correct answer against which to compare the result from the interval tree.
92 | */
93 | private static Set> fetchOverlappingIntervals(List> master,
94 | Interval queryInterval) {
95 | Set> result = new HashSet>();
96 | for (Interval next : master) {
97 | if (next.overlaps(queryInterval)) {
98 | result.add(next);
99 | }
100 | }
101 | return result;
102 | }
103 |
104 | @BeforeClass
105 | public static void globalSetup() {
106 | masterClosed = new ArrayList>();
107 | masterHalfOpenLow = new ArrayList>();
108 | masterHalfOpenHigh = new ArrayList>();
109 | masterOpen = new ArrayList>();
110 |
111 | final double intervalWidth = 0.1;
112 | final double halfIntervalWidth = intervalWidth / 2;
113 |
114 | for (int j = 0; j < 10; j++) {
115 | Interval next;
116 | next = new Interval(intervalWidth * j, true, intervalWidth
117 | * (j + 1), true);
118 | masterClosed.add(next);
119 | masterClosed.add(new Interval(next.getLow() + halfIntervalWidth,
120 | true, next.getHigh() + halfIntervalWidth, true));
121 |
122 | next = new Interval(intervalWidth * j, false, intervalWidth
123 | * (j + 1), true);
124 | masterHalfOpenLow.add(next);
125 | masterHalfOpenLow
126 | .add(new Interval(next.getLow() + halfIntervalWidth, false,
127 | next.getHigh() + halfIntervalWidth, true));
128 |
129 | next = new Interval(intervalWidth * j, true, intervalWidth
130 | * (j + 1), false);
131 | masterHalfOpenHigh.add(next);
132 | masterHalfOpenHigh
133 | .add(new Interval(next.getLow() + halfIntervalWidth, true,
134 | next.getHigh() + halfIntervalWidth, false));
135 |
136 | next = new Interval(intervalWidth * j, false, intervalWidth
137 | * (j + 1), false);
138 | masterOpen.add(next);
139 | masterOpen.add(new Interval(next.getLow() + halfIntervalWidth,
140 | false, next.getHigh() + halfIntervalWidth, false));
141 | }
142 | masterClosedTree = makeTree(masterClosed);
143 | masterHalfOpenLowTree = makeTree(masterHalfOpenLow);
144 | masterHalfOpenHighTree = makeTree(masterHalfOpenHigh);
145 | masterOpenTree = makeTree(masterOpen);
146 | }
147 |
148 | @AfterClass
149 | public static void globalTeardown() {
150 | masterClosed = null;
151 | masterHalfOpenLow = null;
152 | masterHalfOpenHigh = null;
153 | masterOpen = null;
154 | masterClosedTree = null;
155 | masterHalfOpenLowTree = null;
156 | masterHalfOpenHighTree = null;
157 | masterOpenTree = null;
158 | }
159 |
160 | /**
161 | * Helper method to make and populate an IntervalTree based upon the specified
162 | * list of intervals.
163 | */
164 | private static DynamicIntervalTree> makeTree(List> list) {
165 | DynamicIntervalTree> tree = new DynamicIntervalTree>();
166 | for (Interval next : list)
167 | tree.insert(next);
168 | return tree;
169 | }
170 |
171 | @Test
172 | public void testClear() {
173 | DynamicIntervalTree> tree = makeTree(masterClosed);
174 | Assert.assertEquals(masterClosed.size(), tree.getSize());
175 | tree.clear();
176 | Assert.assertTrue(tree.fetchContainingIntervals(masterClosed.get(0)
177 | .getLow()).isEmpty());
178 | Assert.assertEquals(0, tree.getSize());
179 | }
180 |
181 | @Test
182 | public void testConstructionPointerEdgeCases() {
183 | List> master = new ArrayList>();
184 | master.add(new Interval(0.0, true, 0.1, true));
185 | master.add(new Interval(0.2, true, 0.3, true));
186 | master.add(new Interval(0.4, true, 0.5, true));
187 | master.add(new Interval(0.6, true, 0.7, true));
188 | DynamicIntervalTree> tree = makeTree(master);
189 | Collection> result = tree.fetchContainingIntervals(0.25);
190 | Assert.assertEquals(1, result.size());
191 | Assert.assertEquals(master.get(1), result.iterator().next());
192 | }
193 |
194 | @Test
195 | public void testContainingIntervals() {
196 | List queryPointList = new ArrayList();
197 | for (Interval next : masterClosed) {
198 | queryPointList.add(next.getLow());
199 | }
200 | executeContainingIntervalsTest(queryPointList, masterClosed,
201 | masterClosedTree);
202 | executeContainingIntervalsTest(queryPointList, masterHalfOpenLow,
203 | masterHalfOpenLowTree);
204 | executeContainingIntervalsTest(queryPointList, masterHalfOpenHigh,
205 | masterHalfOpenHighTree);
206 | executeContainingIntervalsTest(queryPointList, masterOpen, masterOpenTree);
207 | }
208 |
209 | @Test
210 | public void testNull() {
211 | DynamicIntervalTree> tree = new DynamicIntervalTree>();
212 | try {
213 | tree.fetchContainingIntervals((Double) null);
214 | Assert.fail();
215 | } catch (NullPointerException e) {
216 |
217 | }
218 | try {
219 | tree.fetchOverlappingIntervals((Interval) null);
220 | Assert.fail();
221 | } catch (NullPointerException e) {
222 |
223 | }
224 | Assert.assertFalse(tree.delete(null));
225 | try {
226 | tree.insert(null);
227 | Assert.fail();
228 | } catch (NullPointerException e) {
229 |
230 | }
231 | }
232 |
233 | @Test
234 | public void testOverlappingIntervals() {
235 | executeOverlappingIntervalsTest(masterClosed, masterClosed,
236 | masterClosedTree);
237 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterClosed,
238 | masterClosedTree);
239 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterClosed,
240 | masterClosedTree);
241 | executeOverlappingIntervalsTest(masterOpen, masterClosed, masterClosedTree);
242 |
243 | executeOverlappingIntervalsTest(masterClosed, masterHalfOpenLow,
244 | masterHalfOpenLowTree);
245 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenLow,
246 | masterHalfOpenLowTree);
247 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenLow,
248 | masterHalfOpenLowTree);
249 | executeOverlappingIntervalsTest(masterOpen, masterHalfOpenLow,
250 | masterHalfOpenLowTree);
251 |
252 | executeOverlappingIntervalsTest(masterClosed, masterHalfOpenHigh,
253 | masterHalfOpenHighTree);
254 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterHalfOpenHigh,
255 | masterHalfOpenHighTree);
256 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterHalfOpenHigh,
257 | masterHalfOpenHighTree);
258 | executeOverlappingIntervalsTest(masterOpen, masterHalfOpenHigh,
259 | masterHalfOpenHighTree);
260 |
261 | executeOverlappingIntervalsTest(masterClosed, masterOpen, masterOpenTree);
262 | executeOverlappingIntervalsTest(masterHalfOpenLow, masterOpen,
263 | masterOpenTree);
264 | executeOverlappingIntervalsTest(masterHalfOpenHigh, masterOpen,
265 | masterOpenTree);
266 | executeOverlappingIntervalsTest(masterOpen, masterOpen, masterOpenTree);
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/src/main/java/blogspot/software_and_algorithms/stern_library/optimization/HungarianAlgorithm.java:
--------------------------------------------------------------------------------
1 | package blogspot.software_and_algorithms.stern_library.optimization;
2 |
3 | import java.util.Arrays;
4 |
5 | /* Copyright (c) 2012 Kevin L. Stern
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining a copy
8 | * of this software and associated documentation files (the "Software"), to deal
9 | * in the Software without restriction, including without limitation the rights
10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | * copies of the Software, and to permit persons to whom the Software is
12 | * furnished to do so, subject to the following conditions:
13 | *
14 | * The above copyright notice and this permission notice shall be included in
15 | * all copies or substantial portions of the Software.
16 | *
17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | * SOFTWARE.
24 | */
25 |
26 | /**
27 | * An implementation of the Hungarian algorithm for solving the assignment
28 | * problem. An instance of the assignment problem consists of a number of
29 | * workers along with a number of jobs and a cost matrix which gives the cost of
30 | * assigning the i'th worker to the j'th job at position (i, j). The goal is to
31 | * find an assignment of workers to jobs so that no job is assigned more than
32 | * one worker and so that no worker is assigned to more than one job in such a
33 | * manner so as to minimize the total cost of completing the jobs.
34 | *
35 | *
36 | * An assignment for a cost matrix that has more workers than jobs will
37 | * necessarily include unassigned workers, indicated by an assignment value of
38 | * -1; in no other circumstance will there be unassigned workers. Similarly, an
39 | * assignment for a cost matrix that has more jobs than workers will necessarily
40 | * include unassigned jobs; in no other circumstance will there be unassigned
41 | * jobs. For completeness, an assignment for a square cost matrix will give
42 | * exactly one unique worker to each job.
43 | *
44 | *
45 | * This version of the Hungarian algorithm runs in time O(n^3), where n is the
46 | * maximum among the number of workers and the number of jobs.
47 | *
48 | * @author Kevin L. Stern
49 | */
50 | public class HungarianAlgorithm {
51 | private final double[][] costMatrix;
52 | private final int rows, cols, dim;
53 | private final double[] labelByWorker, labelByJob;
54 | private final int[] minSlackWorkerByJob;
55 | private final double[] minSlackValueByJob;
56 | private final int[] matchJobByWorker, matchWorkerByJob;
57 | private final int[] parentWorkerByCommittedJob;
58 | private final boolean[] committedWorkers;
59 |
60 | /**
61 | * Construct an instance of the algorithm.
62 | *
63 | * @param costMatrix
64 | * the cost matrix, where matrix[i][j] holds the cost of assigning
65 | * worker i to job j, for all i, j. The cost matrix must not be
66 | * irregular in the sense that all rows must be the same length; in
67 | * addition, all entries must be non-infinite numbers.
68 | */
69 | public HungarianAlgorithm(double[][] costMatrix) {
70 | this.dim = Math.max(costMatrix.length, costMatrix[0].length);
71 | this.rows = costMatrix.length;
72 | this.cols = costMatrix[0].length;
73 | this.costMatrix = new double[this.dim][this.dim];
74 | for (int w = 0; w < this.dim; w++) {
75 | if (w < costMatrix.length) {
76 | if (costMatrix[w].length != this.cols) {
77 | throw new IllegalArgumentException("Irregular cost matrix");
78 | }
79 | for (int j = 0; j < this.cols; j++) {
80 | if (Double.isInfinite(costMatrix[w][j])) {
81 | throw new IllegalArgumentException("Infinite cost");
82 | }
83 | if (Double.isNaN(costMatrix[w][j])) {
84 | throw new IllegalArgumentException("NaN cost");
85 | }
86 | }
87 | this.costMatrix[w] = Arrays.copyOf(costMatrix[w], this.dim);
88 | } else {
89 | this.costMatrix[w] = new double[this.dim];
90 | }
91 | }
92 | labelByWorker = new double[this.dim];
93 | labelByJob = new double[this.dim];
94 | minSlackWorkerByJob = new int[this.dim];
95 | minSlackValueByJob = new double[this.dim];
96 | committedWorkers = new boolean[this.dim];
97 | parentWorkerByCommittedJob = new int[this.dim];
98 | matchJobByWorker = new int[this.dim];
99 | Arrays.fill(matchJobByWorker, -1);
100 | matchWorkerByJob = new int[this.dim];
101 | Arrays.fill(matchWorkerByJob, -1);
102 | }
103 |
104 | /**
105 | * Compute an initial feasible solution by assigning zero labels to the
106 | * workers and by assigning to each job a label equal to the minimum cost
107 | * among its incident edges.
108 | */
109 | protected void computeInitialFeasibleSolution() {
110 | for (int j = 0; j < dim; j++) {
111 | labelByJob[j] = Double.POSITIVE_INFINITY;
112 | }
113 | for (int w = 0; w < dim; w++) {
114 | for (int j = 0; j < dim; j++) {
115 | if (costMatrix[w][j] < labelByJob[j]) {
116 | labelByJob[j] = costMatrix[w][j];
117 | }
118 | }
119 | }
120 | }
121 |
122 | /**
123 | * Execute the algorithm.
124 | *
125 | * @return the minimum cost matching of workers to jobs based upon the
126 | * provided cost matrix. A matching value of -1 indicates that the
127 | * corresponding worker is unassigned.
128 | */
129 | public int[] execute() {
130 | /*
131 | * Heuristics to improve performance: Reduce rows and columns by their
132 | * smallest element, compute an initial non-zero dual feasible solution and
133 | * create a greedy matching from workers to jobs of the cost matrix.
134 | */
135 | reduce();
136 | computeInitialFeasibleSolution();
137 | greedyMatch();
138 |
139 | int w = fetchUnmatchedWorker();
140 | while (w < dim) {
141 | initializePhase(w);
142 | executePhase();
143 | w = fetchUnmatchedWorker();
144 | }
145 | int[] result = Arrays.copyOf(matchJobByWorker, rows);
146 | for (w = 0; w < result.length; w++) {
147 | if (result[w] >= cols) {
148 | result[w] = -1;
149 | }
150 | }
151 | return result;
152 | }
153 |
154 | /**
155 | * Execute a single phase of the algorithm. A phase of the Hungarian algorithm
156 | * consists of building a set of committed workers and a set of committed jobs
157 | * from a root unmatched worker by following alternating unmatched/matched
158 | * zero-slack edges. If an unmatched job is encountered, then an augmenting
159 | * path has been found and the matching is grown. If the connected zero-slack
160 | * edges have been exhausted, the labels of committed workers are increased by
161 | * the minimum slack among committed workers and non-committed jobs to create
162 | * more zero-slack edges (the labels of committed jobs are simultaneously
163 | * decreased by the same amount in order to maintain a feasible labeling).
164 | *
165 | *
166 | * The runtime of a single phase of the algorithm is O(n^2), where n is the
167 | * dimension of the internal square cost matrix, since each edge is visited at
168 | * most once and since increasing the labeling is accomplished in time O(n) by
169 | * maintaining the minimum slack values among non-committed jobs. When a phase
170 | * completes, the matching will have increased in size.
171 | */
172 | protected void executePhase() {
173 | while (true) {
174 | int minSlackWorker = -1, minSlackJob = -1;
175 | double minSlackValue = Double.POSITIVE_INFINITY;
176 | for (int j = 0; j < dim; j++) {
177 | if (parentWorkerByCommittedJob[j] == -1) {
178 | if (minSlackValueByJob[j] < minSlackValue) {
179 | minSlackValue = minSlackValueByJob[j];
180 | minSlackWorker = minSlackWorkerByJob[j];
181 | minSlackJob = j;
182 | }
183 | }
184 | }
185 | if (minSlackValue > 0) {
186 | updateLabeling(minSlackValue);
187 | }
188 | parentWorkerByCommittedJob[minSlackJob] = minSlackWorker;
189 | if (matchWorkerByJob[minSlackJob] == -1) {
190 | /*
191 | * An augmenting path has been found.
192 | */
193 | int committedJob = minSlackJob;
194 | int parentWorker = parentWorkerByCommittedJob[committedJob];
195 | while (true) {
196 | int temp = matchJobByWorker[parentWorker];
197 | match(parentWorker, committedJob);
198 | committedJob = temp;
199 | if (committedJob == -1) {
200 | break;
201 | }
202 | parentWorker = parentWorkerByCommittedJob[committedJob];
203 | }
204 | return;
205 | } else {
206 | /*
207 | * Update slack values since we increased the size of the committed
208 | * workers set.
209 | */
210 | int worker = matchWorkerByJob[minSlackJob];
211 | committedWorkers[worker] = true;
212 | for (int j = 0; j < dim; j++) {
213 | if (parentWorkerByCommittedJob[j] == -1) {
214 | double slack = costMatrix[worker][j] - labelByWorker[worker]
215 | - labelByJob[j];
216 | if (minSlackValueByJob[j] > slack) {
217 | minSlackValueByJob[j] = slack;
218 | minSlackWorkerByJob[j] = worker;
219 | }
220 | }
221 | }
222 | }
223 | }
224 | }
225 |
226 | /**
227 | *
228 | * @return the first unmatched worker or {@link #dim} if none.
229 | */
230 | protected int fetchUnmatchedWorker() {
231 | int w;
232 | for (w = 0; w < dim; w++) {
233 | if (matchJobByWorker[w] == -1) {
234 | break;
235 | }
236 | }
237 | return w;
238 | }
239 |
240 | /**
241 | * Find a valid matching by greedily selecting among zero-cost matchings. This
242 | * is a heuristic to jump-start the augmentation algorithm.
243 | */
244 | protected void greedyMatch() {
245 | for (int w = 0; w < dim; w++) {
246 | for (int j = 0; j < dim; j++) {
247 | if (matchJobByWorker[w] == -1 && matchWorkerByJob[j] == -1
248 | && costMatrix[w][j] - labelByWorker[w] - labelByJob[j] == 0) {
249 | match(w, j);
250 | }
251 | }
252 | }
253 | }
254 |
255 | /**
256 | * Initialize the next phase of the algorithm by clearing the committed
257 | * workers and jobs sets and by initializing the slack arrays to the values
258 | * corresponding to the specified root worker.
259 | *
260 | * @param w
261 | * the worker at which to root the next phase.
262 | */
263 | protected void initializePhase(int w) {
264 | Arrays.fill(committedWorkers, false);
265 | Arrays.fill(parentWorkerByCommittedJob, -1);
266 | committedWorkers[w] = true;
267 | for (int j = 0; j < dim; j++) {
268 | minSlackValueByJob[j] = costMatrix[w][j] - labelByWorker[w]
269 | - labelByJob[j];
270 | minSlackWorkerByJob[j] = w;
271 | }
272 | }
273 |
274 | /**
275 | * Helper method to record a matching between worker w and job j.
276 | */
277 | protected void match(int w, int j) {
278 | matchJobByWorker[w] = j;
279 | matchWorkerByJob[j] = w;
280 | }
281 |
282 | /**
283 | * Reduce the cost matrix by subtracting the smallest element of each row from
284 | * all elements of the row as well as the smallest element of each column from
285 | * all elements of the column. Note that an optimal assignment for a reduced
286 | * cost matrix is optimal for the original cost matrix.
287 | */
288 | protected void reduce() {
289 | for (int w = 0; w < dim; w++) {
290 | double min = Double.POSITIVE_INFINITY;
291 | for (int j = 0; j < dim; j++) {
292 | if (costMatrix[w][j] < min) {
293 | min = costMatrix[w][j];
294 | }
295 | }
296 | for (int j = 0; j < dim; j++) {
297 | costMatrix[w][j] -= min;
298 | }
299 | }
300 | double[] min = new double[dim];
301 | for (int j = 0; j < dim; j++) {
302 | min[j] = Double.POSITIVE_INFINITY;
303 | }
304 | for (int w = 0; w < dim; w++) {
305 | for (int j = 0; j < dim; j++) {
306 | if (costMatrix[w][j] < min[j]) {
307 | min[j] = costMatrix[w][j];
308 | }
309 | }
310 | }
311 | for (int w = 0; w < dim; w++) {
312 | for (int j = 0; j < dim; j++) {
313 | costMatrix[w][j] -= min[j];
314 | }
315 | }
316 | }
317 |
318 | /**
319 | * Update labels with the specified slack by adding the slack value for
320 | * committed workers and by subtracting the slack value for committed jobs. In
321 | * addition, update the minimum slack values appropriately.
322 | */
323 | protected void updateLabeling(double slack) {
324 | for (int w = 0; w < dim; w++) {
325 | if (committedWorkers[w]) {
326 | labelByWorker[w] += slack;
327 | }
328 | }
329 | for (int j = 0; j < dim; j++) {
330 | if (parentWorkerByCommittedJob[j] != -1) {
331 | labelByJob[j] -= slack;
332 | } else {
333 | minSlackValueByJob[j] -= slack;
334 | }
335 | }
336 | }
337 | }
338 |
--------------------------------------------------------------------------------
/src/test/java/blogspot/software_and_algorithms/stern_library/data_structure/StaticIntervalTreeTest.java:
--------------------------------------------------------------------------------
1 | package blogspot.software_and_algorithms.stern_library.data_structure;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collection;
5 | import java.util.HashSet;
6 | import java.util.List;
7 | import java.util.Set;
8 |
9 | import org.junit.AfterClass;
10 | import org.junit.Assert;
11 | import org.junit.BeforeClass;
12 | import org.junit.Test;
13 |
14 | /* Copyright (c) 2012 Kevin L. Stern
15 | *
16 | * Permission is hereby granted, free of charge, to any person obtaining a copy
17 | * of this software and associated documentation files (the "Software"), to deal
18 | * in the Software without restriction, including without limitation the rights
19 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20 | * copies of the Software, and to permit persons to whom the Software is
21 | * furnished to do so, subject to the following conditions:
22 | *
23 | * The above copyright notice and this permission notice shall be included in
24 | * all copies or substantial portions of the Software.
25 | *
26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32 | * SOFTWARE.
33 | */
34 |
35 | /**
36 | * Test class for StaticIntervalTree.
37 | *
38 | * @author Kevin L. Stern
39 | */
40 | public class StaticIntervalTreeTest {
41 | private static List> masterClosed;
42 | private static List> masterHalfOpenLow;
43 | private static List> masterHalfOpenHigh;
44 | private static List> masterOpen;
45 | private static StaticIntervalTree> masterClosedTree;
46 | private static StaticIntervalTree> masterHalfOpenLowTree;
47 | private static StaticIntervalTree> masterHalfOpenHighTree;
48 | private static StaticIntervalTree> masterOpenTree;
49 |
50 | private static void executeContainingIntervalsTest(List queryPoints,
51 | List> master,
52 | StaticIntervalTree> tree) {
53 | Set> result = new HashSet>();
54 | for (Double queryPoint : queryPoints) {
55 | Assert.assertEquals(fetchContainingIntervals(master, queryPoint),
56 | tree.fetchContainingIntervals(result, queryPoint));
57 | result.clear();
58 | }
59 | }
60 |
61 | private static void executeOverlappingIntervalsTest(List> queryIntervals,
62 | List> master,
63 | StaticIntervalTree> tree) {
64 | Set> result = new HashSet>();
65 | for (Interval next : queryIntervals) {
66 | Assert.assertEquals(fetchOverlappingIntervals(master, next),
67 | tree.fetchOverlappingIntervals(result, next));
68 | result.clear();
69 | }
70 | }
71 |
72 | /**
73 | * Helper method to fetch the set of intervals from the master list that
74 | * contain the query point using the brute force method. This gives a correct
75 | * answer against which to compare the result from the interval tree.
76 | */
77 | private static Set> fetchContainingIntervals(List> master,
78 | double queryPoint) {
79 | Set> result = new HashSet>();
80 | for (Interval next : master) {
81 | if (next.contains(queryPoint)) {
82 | result.add(next);
83 | }
84 | }
85 | return result;
86 | }
87 |
88 | /**
89 | * Helper method to fetch the set of intervals from the master list that
90 | * overlap the query interval using the brute force method. This gives a
91 | * correct answer against which to compare the result from the interval tree.
92 | */
93 | private static Set> fetchOverlappingIntervals(List> master,
94 | Interval queryInterval) {
95 | Set> result = new HashSet>();
96 | for (Interval