# Shrink an array of double elements (uniformly)

Is there a way to avoid the first loop in the method shrink? (if and while...)

The utility method shrink should reduce the elements of a double array by picking up elements from the original array "with a regular step respectively offset size"... but the first element should always be included, and the last element too, if possible.

    public static double[] shrink(final double[] data, final int len) {
if (data.length == 0 || len < 1) {
throw new IllegalArgumentException("data.length == 0 || len < 1");
}
double[] d = new double[len];
double step = (double) data.length / len;
if (len > 1) {
int i = 1;
while (Math.round(step * (len - 1)) < data.length - 1) {
step = (double) (data.length + i) / len;
i++;
}
}
for (int i = 0; i < len; i++) {
int j = (int) Math.round(i * step);
d[i] = data[j];
}
return d;
}

public static void testShrink() {
double[] data1 = {1, 2, 3, 4, 5, 6};
for (int i = 1; i <= data1.length; i++) {
double[] data = new double[i];
System.arraycopy(data1, 0, data, 0, i);
for (int j = 1; j <= i; j++) {
double[] result = shrink(data, j);
System.out.println(i + " " + j + " " + Arrays.toString(result));
}
}
}


The output of the method testShrink should look like the following:

1 1 [1.0]
2 1 [1.0]
2 2 [1.0, 2.0]
3 1 [1.0]
3 2 [1.0, 3.0]
3 3 [1.0, 2.0, 3.0]
4 1 [1.0]
4 2 [1.0, 4.0]
4 3 [1.0, 2.0, 4.0]
4 4 [1.0, 2.0, 3.0, 4.0]
5 1 [1.0]
5 2 [1.0, 5.0]
5 3 [1.0, 3.0, 5.0]
5 4 [1.0, 2.0, 4.0, 5.0]
5 5 [1.0, 2.0, 3.0, 4.0, 5.0]
6 1 [1.0]
6 2 [1.0, 6.0]
6 3 [1.0, 3.0, 6.0]
6 4 [1.0, 3.0, 4.0, 6.0]
6 5 [1.0, 2.0, 3.0, 5.0, 6.0]
6 6 [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]


I think, it's also a mathematical-like question.

• Your interface does not lend itself to a regular step, since you only ask for an output length. A good implementation would vary between two different steps in a dithered manner, or would interpolate. Oct 5, 2023 at 12:47
• What is the purpose and application of this function? Should it interpolate; i.e. should it take the average of two values when it "lands in the middle" between them? Oct 5, 2023 at 12:50
• (your current implementation with round() is basically the dithered one, with a non-uniform step; but could be improved) Oct 5, 2023 at 13:11
• Thanks for your comment @Reinderien. The purpose of the method is to compress a stock market chart horizontally. Therefore, it is unimportant (for me) whether an average would be formed or whether the corresponding elements simply no longer occur. Oct 5, 2023 at 13:17
• @Reinderien the whole class is visible here. Oct 5, 2023 at 14:02

I don't understand the first loop. It seems like a successive approximation calculation for the step size, but that shouldn't be necessary. Instead, we know that you have two defined endpoints of a linear function:

$$f(0) = 0$$ $$f(n-1) = m-1$$ $$i = f(j) = \frac {j(m-1)} {n-1}$$

That gives you your step size. It's easy enough to prevent divide-by-zero by having 1 as the minimum denominator; this also allows for len=0 to act as expected (returning an empty array).

public static double[] shrink(double[] data, int len) {
if (data.length < 1 || len < 0)
throw new IllegalArgumentException("data.length < 1 || len < 0");

double[] d = new double[len];
double step = (data.length - 1.0d)/Integer.max(len - 1, 1);

for (int j = 0; j < len; ++j) {
int i = (int)Math.round(j * step);
d[j] = data[i];
}
return d;
}


Note that since this doesn't use interpolation, it introduces alias error.

• Thanks, I simply didn't have seen this: double step = (double) (data.length - 1) / (len - 1);. Oct 6, 2023 at 7:38