-
Notifications
You must be signed in to change notification settings - Fork 0
/
MedianCut4BoatSpeed.java
285 lines (251 loc) · 7.51 KB
/
MedianCut4BoatSpeed.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
// $Header$
// Copyright © 2008 Martin Weber
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.PriorityQueue;
/**
* Median cut algorithm, based on <a
* href="http://en.literateprograms.org/Median_cut_algorithm_(C_Plus_Plus)"
* >Median cut algorithm (C Plus Plus)</a>.<br>
* Specialized for boat speed values.
*
* @author Martin Weber
*/
public class MedianCut4BoatSpeed
{
/**
*/
// @SuppressWarnings("unchecked")
public MedianCut4BoatSpeed()
{}
/**
* Determines the most representative values on the range of the specifed
* input data for the desired number of quantization level.
*
* @return a list of clusters containing the input points, one for each
* desired quantization level.
*/
public List<MedianCut4BoatSpeed.Cluster> medianCut( List<Float> inputData,
int desiredQuantizationLevels)
{
/**
* a queue with the ClusterImpl having the longest side to have maximum
* priority
*/
PriorityQueue<ClusterImpl> blockQueue= new PriorityQueue<ClusterImpl>();
// create initial block
ClusterImpl longestBlock=
new ClusterImpl( inputData.toArray( new Float[inputData.size()]));
longestBlock.shrink();
blockQueue.offer( longestBlock);
// While the number of clusters is less than desired number...
while (blockQueue.size() < desiredQuantizationLevels
&& blockQueue.peek().getPointCnt() > 1) {
// Find the largest side length of any side of any cluster..
longestBlock= blockQueue.poll();
// split off block2 from longestBlock
ClusterImpl block2= longestBlock.split();
// Shrink the two new clusters so that they are just large enough to
// contain their points.
longestBlock.shrink();
block2.shrink();
blockQueue.offer( longestBlock);
blockQueue.offer( block2);
}
// for each block add it to the result...
ArrayList<Cluster> result= new ArrayList<Cluster>(blockQueue.size());
while ( !blockQueue.isEmpty()) {
ClusterImpl block= blockQueue.poll();
result.add( block);
}
return result;
}
// //////////////////////////////////////////////////////////////////
// inner classes
// //////////////////////////////////////////////////////////////////
/**
* A cluster containing a number of points.
*
* @author Martin Weber
*/
public interface Cluster
{
/**
* Gets the point with thre minimum value in this cluster.
*/
public Float getMinimumPoint();
/**
* Gets the point with thre maximum value in this cluster.
*/
public Float getMaximumPoint();
/**
* Finds a representative point for this block. Implemented to compute the
* arithmetic mean (average) of all points in the cluster.
*
* @return a representative point for this block
*/
public abstract Float getRepresentativePoint();
}
/**
* A cluster containing a number of points in n-dimensional space. For
* efficiency reasons, the cluster is implemented as a rectangular block (a
* cuboidal).
*
* @author Martin Weber
*/
private class ClusterImpl implements Comparable<ClusterImpl>, Cluster
{
/** value storage. necessary that we have random access to the points */
private final Float[] points;
/** number of dimensions in DataPoint3Byte */
// private final int numDimensions;
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of points in the ClusterImpl. */
private int count;
/** the points corresponding to two opposite corners of the block. */
private float minCorner;
private float maxCorner;
/**
* @param points
*/
public ClusterImpl( Float[] points)
{
this.points= points;
// numDimensions= points[0].getDimensions();
offset= 0;
count= points.length;
initCorners();
// sort points for split()..
Arrays.sort( points, offset, offset + count);
}
/**
* Private constructor which shares value array for speed.
*
* @param offset
* @param count
* @param points
*/
private ClusterImpl( int offset, int count, Float[] points)
{
this.points= points;
// numDimensions= points[0].getDimensions();
this.offset= offset;
this.count= count;
initCorners();
}
/**
*/
private void initCorners()
{
minCorner= Float.MIN_VALUE;
maxCorner= Float.MAX_VALUE;
}
/**
* Gets the number of points in this block.
*/
public final int getPointCnt()
{
return this.count;
}
/**
* figures out which side (dimension) of the block is longest.
*
* @return the number of dimension with the longest side.
*/
private short longestSideIndex()
{
return 0;
}
/**
* Gets the length of the longest side of the block.
*/
private float longestSideLength()
{
return maxCorner - minCorner;
}
/**
* Shrinks a block so that it just barely contains its points; that is, its
* minimum and maximum coordinates are chosen according to the minimum and
* maximum coordinates of its points.
*/
public void shrink()
{
float value= points[offset].floatValue();
minCorner= value;
maxCorner= value;
for (int i= 0; i < count; i++) {
value= points[offset + i].floatValue();
minCorner= Math.min( minCorner, value);
maxCorner= Math.max( maxCorner, value);
}
}
/**
* Partitions the points in this block into two sublists. Splitting is done
* along the largest side in such a way that half the contained points fall
* into a new cluster.
*
* @return a newly created block with the splitted off points
*/
public ClusterImpl split()
{
// partition the points into two sublists
// Arrays.sort( points, offset, offset + count);
int median= (count + 1) / 2;
ClusterImpl block2=
new ClusterImpl( offset + median, count - median, points);
this.count= median;
this.initCorners();
return block2;
}
/**
* Finds a representative point for this block. Implemented to compute the
* arithmetic mean (average) of all points in the cluster.
*
* @return a representative point for this block
*/
public Float getRepresentativePoint()
{
// To find a representative point for each block, we merely compute the
// arithmetic mean (average) of all points in the cluster:
double sum= 0.0;
for (int i= 0; i < count; i++) {
sum+= points[offset + i].floatValue();
}
Float averagePoint= new Float( sum / count);
return averagePoint;
}
/**
* {@inheritDoc} Compares two blocks by the length of their longest side.
*/
public int compareTo( ClusterImpl rhs)
{
return Float.compare( rhs.longestSideLength(), this.longestSideLength());
}
/*-
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return getClass().getSimpleName() + ", min=" + minCorner + ", max="
+ maxCorner + ", len=" + longestSideLength();
}
/*-
* @see mediancut.MedianCut4BoatSpeed.Cluster#getMaximumPoint()
*/
public Float getMaximumPoint()
{
return minCorner;
}
/*-
* @see mediancut.MedianCut4BoatSpeed.Cluster#getMinimumPoint()
*/
public Float getMinimumPoint()
{
return maxCorner;
}
}
}