@Override public Map<Long, Long> findFS() { int[] usedCounts = Arrays.copyOfRange(counts, 0, capwidth); // initial frequency is the same as what we see from counters Map<Integer, Long> freq = Arrays.stream(usedCounts) .boxed() .collect(Collectors.groupingBy(Integer::intValue, Collectors.counting())); Long zeroNum = freq.get(0); if (zeroNum != null && zeroNum == capwidth) { return new HashMap<>(); } // estimate the number of items int num = (int) (capwidth * Math.log(1.0 * capwidth / zeroNum)); Distribution newDistribution = new Distribution(freq); Distribution oldDistribution = new Distribution(); // A pattern is a setting of itmes+frequencies that sum to sumI // All patterns in this list will map to a common number sumI List<Map<Integer, Integer>> patterns = new ArrayList<>(); List<Double> probabilities = new ArrayList<>(); int iterations = 0; // iteratively update the distribution // while (notConverged()) { while (iterations < MAXIMUM_ITERATIONS) { oldDistribution.fillFrom(newDistribution); newDistribution.clear(); for (Map.Entry<Integer, Long> entry : freq.entrySet()) { int sumI = entry.getKey(); int freqI = entry.getValue().intValue(); if (sumI == 0) { // skip key=0 continue; } // find new probable patterns, get their probabilities and update the distribution getPatterns(patterns, sumI, freqI, oldDistribution); computeProbabilities(patterns, oldDistribution, probabilities); Iterator<Double> probabilityIterator = probabilities.iterator(); for (Map<Integer, Integer> pattern : patterns) { // for each pattern double probability = probabilityIterator.next(); for (Map.Entry<Integer, Integer> patternEntry : pattern.entrySet()) { newDistribution.addFreq( patternEntry.getKey(), freqI * patternEntry.getValue() * probability); } } } // scale factor to make sum of distribution equal to 1 double scale = num / newDistribution.sumFreq(); pw.println( String.format( DISTRIBUTION_TRACE_FORMATTER, getStep() + 1, iterations + 1, newDistribution.toString(scale))); iterations++; } pw.flush(); System.out.println(getStep()); Map<Long, Long> output = new HashMap<>(); double freqSum = newDistribution.sumFreq(); for (Map.Entry<Integer, Double> entry : newDistribution.freq.entrySet()) { output.put(entry.getKey().longValue(), (long) (entry.getValue() / freqSum * num)); } return output; }