Positions Average in Java


The challenge

Suppose you have 4 numbers: '0', '9', '6', '4' and 3 strings composed with them:

s1 = "6900690040"
s2 = "4690606946"
s3 = "9990494604"

Compare s1 and s2 to see how many positions they have in common: `` at index 3, 6 at index 4, 4 at index 8 ie 3 common positions out of ten.

Compare s1 and s3 to see how many positions they have in common: 9 at index 1, `` at index 3, 9 at index 5 ie 3 common positions out of ten.

Compare s2 and s3. We find 2 common positions out of ten.

So for the 3 strings we have 8 common positions out of 30 ie 0.2666… or 26.666…%

Given n substrings (n >= 2) in a string s our function pos_average will calculate the average percentage of positions that are the same between the (n * (n-1)) / 2 sets of substrings taken amongst the given n substrings. It can happen that some substrings are duplicate but since their ranks are not the same in s they are considered as different substrings.

The function returns the percentage formatted as a float with 10 decimals but the result is tested at 1e.-9 (see function assertFuzzy in the tests).

Example:

Given string s = “444996, 699990, 666690, 096904, 600644, 640646, 606469, 409694, 666094, 606490” composing a set of n = 10 substrings (hence 45 combinations), pos_average returns 29.2592592593.

In a set the n substrings will have the same length ( > 0 ).

The solution in Java code

Option 1:

public class PositionAverage {

  public static double posAverage(String s) {
    String[] strings = s.split(",");
    int matchs = 0;
    double of = 0;
    for (int i = 0; i < strings.length; i++)
      strings[i] = strings[i].trim();
    for (int i = 0; i < strings.length; i++) {
      for (int j = i + 1; j < strings.length; j++)
        for (int k = 0; k < strings[i].length(); k++) {
          matchs += (strings[i].charAt(k) == strings[j].charAt(k)) ? 1 : 0;
          of++;
        }
    }
    return matchs * 100 / of;
  }
}

Option 2:

class PositionAverage {
    private static double pairPercentage(String s1, String s2) {
        int lg = s1.length(); int count = 0;
        for (int pos = 0; pos < lg; pos++) {
            if (s1.charAt(pos) == s2.charAt(pos))
                count += 1;
        }
        return (double)count / lg;
    }
    public static double posAverage(String s) {
        String[] strings = s.split(", ");
        double result = 0.0; int cnt = 0; int lg = strings.length;
        for (int k = 0; k < lg; k++) {
            for (int i = k + 1; i < lg; i++) {
                result += pairPercentage(strings[k], strings[i]);
                cnt += 1;
            }
        }
        result = 100.0 * result / cnt;
        return Math.floor(result * Math.pow(10.0, 10)) / Math.pow(10.0, 10);
    }
}

Option 3:

public class PositionAverage {

    public static double posAverage(String s) {
        long posCount = 0;
        long matchCount = 0;

        String[] parts = s.split(", ");

        for(int i=0; i<parts.length-1; i++){
            for(int k=i+1; k<parts.length; k++){
                for(int z=0; z<parts[k].length(); z++){
                    posCount++;
                    if(parts[i].charAt(z)==parts[k].charAt(z)) matchCount++;
                }
            }
        }

        return (double)matchCount / (double) posCount * 100.0;
    }
}

Test cases to validate our solution

import org.junit.Test;
import org.junit.runners.JUnit4;
import static org.junit.Assert.*;
import org.junit.Test;

public class PositionAverageTest {

    private static void assertFuzzy(String s, double exp){
        System.out.println("Testing " + s);
        boolean inrange;
        double merr = 1e-9;
        double actual = PositionAverage.posAverage(s);
        inrange = Math.abs(actual - exp) <= merr;
        if (inrange == false) {
            System.out.println("Expected mean must be near " + exp +", got " + actual);
        }
        assertEquals(true, inrange);         
    }
    @Test
    public void test() {
        assertFuzzy("466960, 069060, 494940, 060069, 060090, 640009, 496464, 606900, 004000, 944096", 26.6666666667);
        assertFuzzy("444996, 699990, 666690, 096904, 600644, 640646, 606469, 409694, 666094, 606490", 29.2592592593);
        assertFuzzy("4444444, 4444444, 4444444, 4444444, 4444444, 4444444, 4444444, 4444444", 100);
        assertFuzzy("0, 0, 0, 0, 0, 0, 0, 0", 100);
    }
}