1

I have this sensor data. It's x,y,z data plus a,b,c cal data. But we think someone did something wrong and put them in the wrong order. So I wanted to try to combine these two sets of numbers by adding them every way possible (with no repeated pairings). So that means there are six unique pairings (x+a, y+b, z+c | x+b, y+a, z+c | etc.). But since each number can be positive or negative that gives me 384 different options as I change the signs for each value. I was going to brute force it until I calculated how many options I had :)

I've thought about brute forcing it and just typing it all out. And I was kind of thinking about typing out the six combos and then multiplying them by several matrices like 1,1,1,1,1,1 | 1,1,1,1,1,-1, | etc.

This is an example of the data (I'm in Java right now):

float[] rawValues = {169.58185f, 20.343851f, 1.4982328f};
float[] calValues = {110.131454f, 4.442127f, 7.5981708f};

After each combo I'm going to run it through the sensor processing routine to and print out the results to sift through later.

processPair(float[] raw, float[] cal){...}

So is there a smarter way to generate my combos without typing it out that will get me full coverage of all the combos?

Thank you

confused
  • 713
  • 1
  • 5
  • 16

3 Answers3

1

This is my approach to this task:

First I put all of the possible calculations into the TreeMap with two numbers as a key e.g. 00 indicates first values from rawValues and calValues, 20 would be third value from rawValues and first value from calValues.

In the end I get results with 9 keys each containing 4 values (e.g. x+a, x-a, -x+a, -x-a)

float[] rawValues = {169.58185f, 20.343851f, 1.4982328f};
float[] calValues = {110.131454f, 4.442127f, 7.5981708f};

Map<String, List<Float>> results = new TreeMap<>();
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        List<Float> result = new ArrayList<>();
        for (int k = -1; k <= 1; k += 2) {
            for (int l = -1; l <= 1; l += 2) {
                result.add(k * rawValues[i] + l * calValues[j]);
            }
        }
        results.put(String.valueOf(i) + j, result);
    }
}

At the end I mix those values together. Because I used TreeMap my keys are sorted and I can be sure that first 3 entries are generated with first value from rawValues, 3 next entries are generated with second value from rawValues and finally last 3 entries are generated with third value from rawValues.

To make sure that all of those values are mixed correctly (e.g. there is no x+a, y+a, z+c - a appears two times) I used i + j + k != 12 || (i == 1 && j == 4 && k == 7) condition. You can write down all permutations and notice that the sum of indexes has to be equal 12 except of single case when i == 1, j == 4 and k == 7.

List<List<Float>> values = results.values().stream().toList();
List<List<Float>> finalResults = new ArrayList<>();
for (int i = 0; i < 3; i++) {
    for (int j = 3; j < 6; j++) {
        for (int k = 6; k < 9; k++) {
            if (i + j + k != 12 || (i == 1 && j == 4 && k == 7)) {
                continue;
            }
            var xValues = values.get(i);
            var yValues = values.get(j);
            var zValues = values.get(k);
            for (int a = 0; a < 4; a++) {
                for (int b = 0; b < 4; b++) {
                    for (int c = 0; c < 4; c++) {
                        List<Float> pairing = new ArrayList<>();
                        pairing.add(xValues.get(a));
                        pairing.add(yValues.get(b));
                        pairing.add(zValues.get(c));
                        finalResults.add(pairing);
                    }
                }
            }
        }
    }
}

At the end what you get is a finalResult - List with 384 entries. Every entry is a List containing 3 floats - unique trio of results that you wanted.

1

The hard thing seems to be the permutations, especially when having repeated values in an array.

For that I order the permutations lexically with boolean nextPermution(double[] values). An increasingly sorted array brings the lowest permutation: 235, followed by 253, 325, 352, 523, 532.

/**
 * Find next higher permutation.
 */
static boolean nextPermution(double[] values) {
    // Find the first position i from the right, that can made larger out of the right part.
    // ... [4] < 7 > 5 > 3 > 2 > 0
    //      i
    int i = values.length - 2;
    while (i >= 0 && values[i] >= values[i + 1]) {
        --i;
    }
    if (i < 0) {
        return false;
    }
    // Take the next larger:
    // ... [5] < 7 > 4 > 3 > 2 > 0
    //      \________/
    //      i        j
    double xi = values[i];
    int j = values.length - 1;
    while (j > i && xi >= values[j]) {
        --j;
    }
    values[i] = values[j];
    values[j] = xi;

    // And for the right part the smallest permutation:
    // By reversal of the order.
    // ... [5] < 0 < 2 < 3 < 4 < 7
    int nright = values.length - (i + 1);
    for (int k = 0; k < nright / 2; ++k) {
        double xk = values[i + 1 + k];
        values[i + 1 + k] = values[values.length - 1 - k];
        values[values.length - 1 - k] = xk;
    }
    return true;
}

Taking the negative or positive values can be done by counting upto 2³ for both arrays: 2³*2³.

static void processPairsNegating(double[] raw, double[] cal) {
    double[] rawSigned = raw.clone();
    double[] calSigned = cal.clone();
    long rawM = 1L << raw.length;
    long calM = 1L << cal.length;
    LongStream.range(0L, rawM).forEach(rawI -> {
        Arrays.setAll(rawSigned, i -> ((1L << i) & rawI) != 0 ? raw[i] : -raw[i]);
        LongStream.range(0L, calM).forEach(calI -> {
            Arrays.setAll(calSigned, i -> ((1L << i) & calI) != 0 ? cal[i] : -cal[i]);
            processPair(rawSigned, calSigned);
        });
    });
    //System.out.println(Arrays.toString(raw));
}

static int counter = 0;

static void processPair(double[] raw, double[] cal) {
    //double[] result = raw.clone();
    System.out.printf("%d. %s%n", counter++, Arrays.toString(raw));
}

Note that I start with using the absolute values, and sorting them. Thus { -3,3 } will not give unnessary repeated combinations. One could make an method initPermutation(doubl[]. values).

public static void main(String[] args) {
    double[] rawValues = {169.58185f, 20.343851f, 1.4982328f};
    //double[] rawValues = {1, 2, 3, 3};
    double[] calValues = {110.131454f, 4.442127f, 7.5981708f};

    Arrays.setAll(rawValues, i -> Math.abs(rawValues[i]));
    Arrays.sort(rawValues);
    Arrays.setAll(calValues, i -> Math.abs(calValues[i]));
    Arrays.sort(calValues);

    for (;;) {
        processPairsNegating(rawValues, calValues);
        if (!nextPermution(rawValues)) {
            break;
        }
    }
}

The code above is not nicely packaged, but relatively simple. I used double instead of float, because it hast more support.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
0

I would write a function that gives me all possible permutations of the values in an array. All possible orderings and let that function also add the sign permutations. That then results in a list like: abc, -abc, bac, b-ac, bca, bc-a, etc

Call that method for both arrays and then walk through the first permutations array and combine every permutation with every permutation of the second permutations array. That would the result in a result like: abc def, abc -def, abc edf, abc e-df, etc

From that it is very easy to create something readable.

Is this indeed a solution? I'm looking forward to your solution :)

See also How to generate all permutations of a list? but that is for Python. https://www.baeldung.com/cs/array-generate-all-permutations is for Java

Milo van der Zee
  • 1,031
  • 11
  • 30