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);
}
}