From a555c472127a9d900ac239b91ae82f1802c8f8d7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 3 May 2022 15:02:57 +0900 Subject: [PATCH 1/2] Reduce sorting complexity in worst-case of diffcalc --- osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index bbd2f079aa..d13d4ebf2e 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -65,6 +65,10 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// private void saveCurrentPeak() { + // Ignore sections with 0 strain to avoid edge cases of long maps with a lot of spacing between objects (e.g. /b/2351871). + if (currentSectionPeak == 0) + return; + strainPeaks.Add(currentSectionPeak); } From 637f81769609d8d56223d2d70263b57d6617444a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 3 May 2022 16:06:20 +0900 Subject: [PATCH 2/2] Ignore zero-sections on a per-case basis --- .../Difficulty/Skills/OsuStrainSkill.cs | 6 +++++- .../Difficulty/TaikoDifficultyCalculator.cs | 8 +++++++- osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs | 10 +++++----- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs index e47edc37cc..dcd2c7d321 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs @@ -38,7 +38,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills double difficulty = 0; double weight = 1; - List strains = GetCurrentStrainPeaks().OrderByDescending(d => d).ToList(); + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + var peaks = GetCurrentStrainPeaks().Where(p => p > 0); + + List strains = peaks.OrderByDescending(d => d).ToList(); // We are reducing the highest strains first to account for extreme difficulty spikes for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs index 6afdef3f3c..a9d512f076 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoDifficultyCalculator.cs @@ -141,7 +141,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty double colourPeak = colourPeaks[i] * colour_skill_multiplier; double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier; double staminaPeak = (staminaRightPeaks[i] + staminaLeftPeaks[i]) * stamina_skill_multiplier * staminaPenalty; - peaks.Add(norm(2, colourPeak, rhythmPeak, staminaPeak)); + + double peak = norm(2, colourPeak, rhythmPeak, staminaPeak); + + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + if (peak > 0) + peaks.Add(peak); } double difficulty = 0; diff --git a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs index d13d4ebf2e..97266562e4 100644 --- a/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs +++ b/osu.Game/Rulesets/Difficulty/Skills/StrainSkill.cs @@ -65,10 +65,6 @@ namespace osu.Game.Rulesets.Difficulty.Skills /// private void saveCurrentPeak() { - // Ignore sections with 0 strain to avoid edge cases of long maps with a lot of spacing between objects (e.g. /b/2351871). - if (currentSectionPeak == 0) - return; - strainPeaks.Add(currentSectionPeak); } @@ -104,9 +100,13 @@ namespace osu.Game.Rulesets.Difficulty.Skills double difficulty = 0; double weight = 1; + // Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871). + // These sections will not contribute to the difficulty. + var peaks = GetCurrentStrainPeaks().Where(p => p > 0); + // Difficulty is the weighted sum of the highest strains from every section. // We're sorting from highest to lowest strain. - foreach (double strain in GetCurrentStrainPeaks().OrderByDescending(d => d)) + foreach (double strain in peaks.OrderByDescending(d => d)) { difficulty += strain * weight; weight *= DecayWeight;