4

私は Java 8 ストリームを試してきましたが、これが最小スコアと最大スコアを削除する最良の方法です。

private final Set<MatchScore> scores = new HashSet<>(10);

. . .

public double OPR() {
    return scores.stream()
            .mapToDouble(MatchScore::getScore)
            .filter((num) -> { //Exclude min and max score
                return num != scores.stream()
                                    .mapToDouble(MatchScore::getScore)
                                    .max().getAsDouble() 
                        && 
                       num != scores.stream()
                                    .mapToDouble(MatchScore::getScore)
                                    .min().getAsDouble();
            })
            .average().getAsDouble();
}
4

3 に答える 3

8

より簡単なアプローチは次のとおりです。

return scores.stream()
        .mapToDouble(MatchScore::getScore)
        .sorted()
        .skip(1)
        .limit(scores.size() - 2)
        .average().getAsDouble();

注: セット内の要素は一意であるため、これは機能します。リストを使用すると、最小スコアまたは最大スコアに等しい複数の要素が存在する可能性があります。


パフォーマンスに関して*、スキップ/制限は 10 個の要素の小さなセットで大幅に高速です ([平均] 列は、サンプル呼び出しにかかる平均時間をナノ秒単位で示します)。

Benchmark                      Mode   Samples         Mean   Mean error    Units
c.a.p.SO22923505.maxMin        avgt         5     6996.190      284.287    ns/op
c.a.p.SO22923505.skipLimit     avgt         5      479.935        4.547    ns/op

*jmh を使用 -テストのソース コードは次のとおりです。

于 2014-04-07T21:35:43.657 に答える
4

を使用DoubleSummaryStatisticsして、データの 1 回のパスで必要な情報を収集し、最小値と最大値を差し引くことができます。

@GenerateMicroBenchmark
public double summaryStats() {
    DoubleSummaryStatistics stats =
        scores.stream()
              .collect(Collectors.summarizingDouble(Double::doubleValue));

    if (stats.getCount() == 0L) {
        return 0.0; // or something
    } else {
        return (stats.getSum() - stats.getMin() - stats.getMax()) / (stats.getCount() - 2);
    }
}

このコードを assylias のベンチマーク コードに追加すると、次の結果が得られます。私のマシンは全体的に低速ですがDoubleSummaryStatistics、1 回のパスで使用した場合の相対的なパフォーマンスは高速です。

Benchmark                         Mode   Samples         Mean   Mean error    Units
c.a.p.SO22923505.maxMin           avgt         5     9629.166     1051.585    ns/op
c.a.p.SO22923505.skipLimit        avgt         5      682.221       80.504    ns/op
c.a.p.SO22923505.summaryStats     avgt         5      412.740       85.372    ns/op
于 2014-04-08T02:01:41.040 に答える
2

これは、ストリームを複数回通過したり、並べ替えたりする必要なく、うまくいくと思います。

private static class ScoreData {
    public double min, max, sum;
    public int count;
    public ScoreData() {
        min = Double.POSITIVE_INFINITY;
        max = Double.NEGATIVE_INFINITY;
        sum = 0;
        count = 0;
    }
    public void add(double d) {
        if (d < min)
            min = d;
        if (d > max)
            max = d;
        sum += d;
        count++;
    }
    public void combine(ScoreData m) {
        if (m.min < min)
            min = m.min;
        if (m.max > max)
            max = m.max;
        sum += m.sum;
        count += m.count;
    }
}

private static ScoreData getScoreData(DoubleStream ds) {
    return ds.collect(ScoreData::new, ScoreData::add, ScoreData::combine);
}

これは、任意のDoubleStream. これで、次のように極値を除いた平均を取得できます

ScoreData sd = getScoreData(scores.stream().mapToDouble(MatchScore::getScore));
double answer = (sd.sum - sd.min - sd.max) / (sd.count - 2);

と仮定してsd.count > 2

編集:車輪を再発明したようです! Stuart は、JDK に既に存在するクラスを使用した、より優れたソリューションを提供しています。

于 2014-04-08T00:51:22.070 に答える