java/com.sap.sse.common.test/src/com/sap/sse/common/test/KadaneExtremeSubarraysFinderTest.java
... ...
@@ -23,8 +23,8 @@ public class KadaneExtremeSubarraysFinderTest {
23 23
finder.add(new ScalableDouble(2));
24 24
finder.add(new ScalableDouble(3));
25 25
assertEquals(6.0, finder.getMaxSum().divide(1.0), EPSILON);
26
- assertEquals(0, finder.getStartIndexInclusiveOfMaxSumSequence());
27
- assertEquals(3, finder.getEndIndexExclusiveOfMaxSumSequence());
26
+ assertEquals(0, finder.getStartIndexOfMaxSumSequence());
27
+ assertEquals(2, finder.getEndIndexOfMaxSumSequence());
28 28
}
29 29
30 30
@Test
... ...
@@ -33,8 +33,8 @@ public class KadaneExtremeSubarraysFinderTest {
33 33
finder.add(new ScalableDouble(3));
34 34
finder.add(1, new ScalableDouble(2));
35 35
assertEquals(6.0, finder.getMaxSum().divide(1.0), EPSILON);
36
- assertEquals(0, finder.getStartIndexInclusiveOfMaxSumSequence());
37
- assertEquals(3, finder.getEndIndexExclusiveOfMaxSumSequence());
36
+ assertEquals(0, finder.getStartIndexOfMaxSumSequence());
37
+ assertEquals(2, finder.getEndIndexOfMaxSumSequence());
38 38
}
39 39
40 40
@Test
... ...
@@ -47,8 +47,8 @@ public class KadaneExtremeSubarraysFinderTest {
47 47
finder.add(new ScalableDouble(6));
48 48
finder.add(new ScalableDouble(-5));
49 49
assertEquals(13.0, finder.getMaxSum().divide(1.0), EPSILON);
50
- assertEquals(0, finder.getStartIndexInclusiveOfMaxSumSequence());
51
- assertEquals(6, finder.getEndIndexExclusiveOfMaxSumSequence());
50
+ assertEquals(0, finder.getStartIndexOfMaxSumSequence());
51
+ assertEquals(5, finder.getEndIndexOfMaxSumSequence());
52 52
}
53 53
54 54
@Test
... ...
@@ -59,11 +59,11 @@ public class KadaneExtremeSubarraysFinderTest {
59 59
finder.add(new ScalableDouble(4));
60 60
finder.add(new ScalableDouble(5));
61 61
assertEquals(15.0, finder.getMaxSum().divide(1.0), EPSILON);
62
- assertEquals(0, finder.getStartIndexInclusiveOfMaxSumSequence());
63
- assertEquals(6, finder.getEndIndexExclusiveOfMaxSumSequence());
62
+ assertEquals(0, finder.getStartIndexOfMaxSumSequence());
63
+ assertEquals(4, finder.getEndIndexOfMaxSumSequence());
64 64
finder.add(3, new ScalableDouble(-7));
65 65
assertEquals(9.0, finder.getMaxSum().divide(1.0), EPSILON);
66
- assertEquals(4, finder.getStartIndexInclusiveOfMaxSumSequence());
67
- assertEquals(6, finder.getEndIndexExclusiveOfMaxSumSequence());
66
+ assertEquals(4, finder.getStartIndexOfMaxSumSequence());
67
+ assertEquals(5, finder.getEndIndexOfMaxSumSequence());
68 68
}
69 69
}
java/com.sap.sse.common/src/com/sap/sse/common/scalablevalue/KadaneExtremeSubarraysFinder.java
... ...
@@ -46,60 +46,101 @@ implements Serializable, Iterable<T> {
46 46
private final List<ScalableValueWithDistance<ValueType, AveragesTo>> maxSumEndingAt;
47 47
48 48
/**
49
- * The maximum of the sums of any contiguous sub-sequence
49
+ * Indices of the first element in {@link #sequence} of the contiguous sub-sequence having the maximum sum
50
+ * ending at the {@link #sequence} index that corresponds with the index of the element in this list. Example:
51
+ * if the contiguous sub-sequence in {@link #sequence} ending at the element before index 5 with the maximum
52
+ * sum starts at index 1, then {@link #startIndexOfMaxSumSequence}{@code .get(5)==1}.
50 53
*/
51
- private ScalableValueWithDistance<ValueType, AveragesTo> maxSum;
54
+ private List<Integer> startIndexOfMaxSumSequence;
52 55
53 56
/**
54
- * Index of the first element in {@link #sequence} of the contiguous sub-sequence having the maximum sum
57
+ * Index of the element in {@link #sequence} at which the contiguous sub-sequence ends that has the maximum sum
58
+ * of all such sub-sequences. This means that {@link #maxSumEndingAt}{@code .get(}{@link #endIndexOfMaxSumSequence})
59
+ * is minimal across all elements in {@link #maxSumEndingAt}.
55 60
*/
56
- private int startIndexInclusiveOfMaxSumSequence;
57
-
58
- /**
59
- * Index of the element after the last element in {@link #sequence} of the contiguous sub-sequence having the maximum sum
60
- */
61
- private int endIndexExclusiveOfMaxSumSequence;
61
+ private int endIndexOfMaxSumSequence;
62 62
63 63
/**
64 64
* See {@code #maxSumEndingAt}, only for the minimum.
65 65
*/
66 66
private final List<ScalableValueWithDistance<ValueType, AveragesTo>> minSumEndingAt;
67 67
68
- private ScalableValueWithDistance<ValueType, AveragesTo> minSum;
69
-
70 68
/**
71
- * Index of the first element in {@link #sequence} of the contiguous sub-sequence having the minium sum
69
+ * Indices of the first element in {@link #sequence} of the contiguous sub-sequence having the minimum sum
70
+ * ending at the {@link #sequence} index that corresponds with the index of the element in this list. Example:
71
+ * if the contiguous sub-sequence in {@link #sequence} ending at the element before index 5 with the minimum
72
+ * sum starts at index 1, then {@link #startIndexOfMaxSumSequence}{@code .get(5)==1}.
72 73
*/
73
- private int startIndexInclusiveOfMinSumSequence;
74
+ private List<Integer> startIndexOfMinSumSequence;
74 75
75 76
/**
76
- * Index of the element after the last element in {@link #sequence} of the contiguous sub-sequence having the minimum sum
77
+ * Index of the element in {@link #sequence} at which the contiguous sub-sequence ends that has the minimum sum
78
+ * of all such sub-sequences. This means that {@link #minSumEndingAt}{@code .get(}{@link #endIndexOfMinSumSequence})
79
+ * is minimal across all elements in {@link #minSumEndingAt}.
77 80
*/
78
- private int endIndexExclusiveOfMinSumSequence;
81
+ private int endIndexOfMinSumSequence;
79 82
80 83
public KadaneExtremeSubarraysFinder() {
81 84
sequence = new LinkedList<>();
82 85
maxSumEndingAt = new LinkedList<>();
83 86
minSumEndingAt = new LinkedList<>();
84
- maxSum = null;
85
- minSum = null;
86
- startIndexInclusiveOfMaxSumSequence = -1;
87
- endIndexExclusiveOfMaxSumSequence = -1;
88
- startIndexInclusiveOfMinSumSequence = -1;
89
- endIndexExclusiveOfMinSumSequence = -1;
87
+ startIndexOfMaxSumSequence = new LinkedList<>();
88
+ endIndexOfMaxSumSequence = -1;
89
+ startIndexOfMinSumSequence = new LinkedList<>();
90
+ endIndexOfMinSumSequence = -1;
90 91
}
91 92
92 93
public synchronized void add(int index, T t) {
94
+ final ScalableValueWithDistance<ValueType, AveragesTo> oldMaxSum = getMaxSum();
95
+ final ScalableValueWithDistance<ValueType, AveragesTo> oldMinSum = getMinSum();
96
+ final boolean insertingIntoMaxSumSequence = index <= endIndexOfMaxSumSequence && index > startIndexOfMaxSumSequence.get(endIndexOfMaxSumSequence);
97
+ final boolean insertingIntoMinSumSequence = index <= endIndexOfMinSumSequence && index > startIndexOfMinSumSequence.get(endIndexOfMinSumSequence);
93 98
sequence.add(index, t);
94 99
final ScalableValueWithDistance<ValueType, AveragesTo> newMaxSumEndingAtIndex;
95
- final ScalableValueWithDistance<ValueType, AveragesTo> sum = index == 0 ? null : t.add(maxSumEndingAt.get(index-1));
96
- if (index == 0 || compare(t, sum) >= 0) {
100
+ final ScalableValueWithDistance<ValueType, AveragesTo> sumWithMax = index == 0 ? null : t.add(maxSumEndingAt.get(index-1));
101
+ if (index == 0 || compare(t, sumWithMax) >= 0) {
97 102
newMaxSumEndingAtIndex = t; // one-element sum consisting of element at "index" is the maximum
103
+ startIndexOfMaxSumSequence.add(index, index);
98 104
} else {
99
- newMaxSumEndingAtIndex = sum;
105
+ newMaxSumEndingAtIndex = sumWithMax;
106
+ startIndexOfMaxSumSequence.add(index, startIndexOfMaxSumSequence.get(index-1));
100 107
}
101 108
maxSumEndingAt.add(index, newMaxSumEndingAtIndex);
102
- update(index+1, newMaxSumEndingAtIndex);
109
+ if (oldMaxSum == null || compare(newMaxSumEndingAtIndex, oldMaxSum) > 0) {
110
+ endIndexOfMaxSumSequence = index;
111
+ }
112
+ final ScalableValueWithDistance<ValueType, AveragesTo> newMinSumEndingAtIndex;
113
+ final ScalableValueWithDistance<ValueType, AveragesTo> sumWithMin = index == 0 ? null : t.add(minSumEndingAt.get(index-1));
114
+ if (index == 0 || compare(t, sumWithMin) <= 0) {
115
+ newMinSumEndingAtIndex = t; // one-element sum consisting of element at "index" is the minimum
116
+ startIndexOfMinSumSequence.add(index, index);
117
+ } else {
118
+ newMinSumEndingAtIndex = sumWithMin;
119
+ startIndexOfMinSumSequence.add(index, startIndexOfMinSumSequence.get(index-1));
120
+ }
121
+ minSumEndingAt.add(index, newMinSumEndingAtIndex);
122
+ if (oldMinSum == null || compare(newMinSumEndingAtIndex, oldMinSum) < 0) {
123
+ endIndexOfMinSumSequence = index;
124
+ }
125
+ if (index < sequence.size()) {
126
+ update(index+1, newMaxSumEndingAtIndex, newMinSumEndingAtIndex);
127
+ }
128
+ if (insertingIntoMaxSumSequence) { // TODO probably also check whether a "positive" value was inserted; in this case, max can only grow further, and sub-sequence indices will stay unchanged
129
+ updateMax();
130
+ }
131
+ if (insertingIntoMinSumSequence) { // TODO probably also check whether a "negative" value was inserted; in this case, min can only shrink further, and sub-sequence indices will stay unchanged
132
+ updateMin();
133
+ }
134
+ }
135
+
136
+ private void updateMin() {
137
+ // TODO Auto-generated method stub
138
+
139
+ }
140
+
141
+ private void updateMax() {
142
+ // TODO Auto-generated method stub
143
+
103 144
}
104 145
105 146
private int compare(final ScalableValueWithDistance<ValueType, AveragesTo> a, final ScalableValueWithDistance<ValueType, AveragesTo> b) {
... ...
@@ -111,32 +152,64 @@ implements Serializable, Iterable<T> {
111 152
* still is the maximum of {@link #maxSumEndingAt}{@code [i-1]+sequence[i]} and {@link #sequence}{@code [i]}. If yes, any change to
112 153
* elements with index less than {@code i} do not have to be carried forward any further. Otherwise, {@link #maxSumEndingAt}{@code [i]}
113 154
* is updated, and the process continues at {@code i+1} "recursively" (implemented iteratively, without recursion).
114
- * @param newMaxSumEndingAtIndex
115 155
*/
116
- private void update(int i, ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingAtPreviousIndex) {
156
+ private void update(int i, ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingAtPreviousIndex, ScalableValueWithDistance<ValueType, AveragesTo> newMinSumEndingAtIndex) {
117 157
final ListIterator<T> sequenceIter = sequence.listIterator(i);
118 158
final ListIterator<ScalableValueWithDistance<ValueType, AveragesTo>> maxSumEndingAtIter = maxSumEndingAt.listIterator(i);
119
- boolean finished = false;
120
- while (sequenceIter.hasNext() && !finished) {
159
+ final ListIterator<ScalableValueWithDistance<ValueType, AveragesTo>> minSumEndingAtIter = minSumEndingAt.listIterator(i);
160
+ final ListIterator<Integer> startIndexOfMaxSumSequenceIter = startIndexOfMaxSumSequence.listIterator(i);
161
+ final ListIterator<Integer> startIndexOfMinSumSequenceIter = startIndexOfMinSumSequence.listIterator(i);
162
+ boolean finishedMax = false;
163
+ boolean finishedMin = false;
164
+ while (sequenceIter.hasNext() && (!finishedMax || !finishedMin)) {
121 165
final T next = sequenceIter.next();
122
- final ScalableValueWithDistance<ValueType, AveragesTo> nextMaxSumEndingAt = maxSumEndingAtIter.next();
123
- final ScalableValueWithDistance<ValueType, AveragesTo> sum = next.add(maxSumEndingAtPreviousIndex);
124
- final ScalableValueWithDistance<ValueType, AveragesTo> newMaxSumEndingAt = compare(next, sum) >= 0 ?
125
- next : sum;
126
- if (compare(nextMaxSumEndingAt, newMaxSumEndingAt) != 0) {
127
- maxSumEndingAtIter.remove();
128
- maxSumEndingAtIter.add(newMaxSumEndingAt);
129
- maxSumEndingAtPreviousIndex = newMaxSumEndingAt;
130
- } else {
131
- finished = true; // no more changes to propagate
166
+ if (!finishedMax) {
167
+ final ScalableValueWithDistance<ValueType, AveragesTo> nextMaxSumEndingAt = maxSumEndingAtIter.next();
168
+ startIndexOfMaxSumSequenceIter.next();
169
+ final ScalableValueWithDistance<ValueType, AveragesTo> sum = next.add(maxSumEndingAtPreviousIndex);
170
+ final boolean nextGreaterOrEqualsThanSum = compare(next, sum) >= 0;
171
+ final ScalableValueWithDistance<ValueType, AveragesTo> newMaxSumEndingAt = nextGreaterOrEqualsThanSum ? next : sum;
172
+ if (compare(nextMaxSumEndingAt, newMaxSumEndingAt) != 0) {
173
+ maxSumEndingAtIter.remove();
174
+ maxSumEndingAtIter.add(newMaxSumEndingAt);
175
+ maxSumEndingAtPreviousIndex = newMaxSumEndingAt;
176
+ startIndexOfMaxSumSequenceIter.remove();
177
+ startIndexOfMaxSumSequenceIter.add(nextGreaterOrEqualsThanSum ? i : startIndexOfMaxSumSequence.get(i-1));
178
+ if (compare(newMaxSumEndingAt, getMaxSum()) > 0) {
179
+ endIndexOfMaxSumSequence = i;
180
+ }
181
+ } else {
182
+ finishedMax = true; // no more changes to propagate
183
+ }
184
+ }
185
+ if (!finishedMin) {
186
+ final ScalableValueWithDistance<ValueType, AveragesTo> nextMinSumEndingAt = minSumEndingAtIter.next();
187
+ startIndexOfMinSumSequenceIter.next();
188
+ final ScalableValueWithDistance<ValueType, AveragesTo> sum = next.add(maxSumEndingAtPreviousIndex);
189
+ final boolean nextLessOrEqualsThanSum = compare(next, sum) <= 0;
190
+ final ScalableValueWithDistance<ValueType, AveragesTo> newMinSumEndingAt = nextLessOrEqualsThanSum ? next : sum;
191
+ if (compare(nextMinSumEndingAt, newMinSumEndingAt) != 0) {
192
+ minSumEndingAtIter.remove();
193
+ minSumEndingAtIter.add(newMinSumEndingAt);
194
+ maxSumEndingAtPreviousIndex = newMinSumEndingAt;
195
+ startIndexOfMinSumSequenceIter.remove();
196
+ startIndexOfMinSumSequenceIter.add(nextLessOrEqualsThanSum ? i : startIndexOfMinSumSequence.get(i-1));
197
+ if (compare(newMinSumEndingAt, getMinSum()) < 0) {
198
+ endIndexOfMinSumSequence = i;
199
+ }
200
+ } else {
201
+ finishedMin = true; // no more changes to propagate
202
+ }
132 203
}
204
+ i++;
133 205
}
134 206
}
135 207
136 208
public synchronized void remove(int index) {
137 209
sequence.remove(index);
138 210
final ScalableValueWithDistance<ValueType, AveragesTo> maxSumEndingAtIndex = maxSumEndingAt.remove(index);
139
- update(index+1, maxSumEndingAtIndex);
211
+ final ScalableValueWithDistance<ValueType, AveragesTo> minSumEndingAtIndex = minSumEndingAt.remove(index);
212
+ update(index+1, maxSumEndingAtIndex, minSumEndingAtIndex);
140 213
}
141 214
142 215
public synchronized void add(T t) {
... ...
@@ -148,27 +221,27 @@ implements Serializable, Iterable<T> {
148 221
}
149 222
150 223
public ScalableValueWithDistance<ValueType, AveragesTo> getMaxSum() {
151
- return maxSum;
224
+ return endIndexOfMaxSumSequence == -1 ? null : maxSumEndingAt.get(endIndexOfMaxSumSequence);
152 225
}
153 226
154 227
public ScalableValueWithDistance<ValueType, AveragesTo> getMinSum() {
155
- return minSum;
228
+ return endIndexOfMinSumSequence == -1 ? null : minSumEndingAt.get(endIndexOfMinSumSequence);
156 229
}
157 230
158
- public int getStartIndexInclusiveOfMaxSumSequence() {
159
- return startIndexInclusiveOfMaxSumSequence;
231
+ public int getStartIndexOfMaxSumSequence() {
232
+ return startIndexOfMaxSumSequence.isEmpty() ? -1 : startIndexOfMaxSumSequence.get(endIndexOfMaxSumSequence);
160 233
}
161 234
162
- public int getEndIndexExclusiveOfMaxSumSequence() {
163
- return endIndexExclusiveOfMaxSumSequence;
235
+ public int getEndIndexOfMaxSumSequence() {
236
+ return endIndexOfMaxSumSequence;
164 237
}
165 238
166
- public int getStartIndexInclusiveOfMinSumSequence() {
167
- return startIndexInclusiveOfMinSumSequence;
239
+ public int getStartIndexOfMinSumSequence() {
240
+ return startIndexOfMinSumSequence.isEmpty() ? -1 : startIndexOfMinSumSequence.get(endIndexOfMinSumSequence);
168 241
}
169 242
170
- public int getEndIndexExclusiveOfMinSumSequence() {
171
- return endIndexExclusiveOfMinSumSequence;
243
+ public int getEndIndexOfMinSumSequence() {
244
+ return endIndexOfMinSumSequence;
172 245
}
173 246
174 247
@Override