From 0e8652e30c38265072b57c4e91a2d1b803620755 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 18:54:26 +0000 Subject: [PATCH] Optimize `roll_table::unique_rolls` with `std::unordered_set` Replaces the O(N^2) linear scan in `roll_table::unique_rolls` with an O(1) `std::unordered_set` lookup for situations where `count + start_index > 16`. This reduces overhead for large roll counts while preserving performance for small roll counts. Co-authored-by: perim <436583+perim@users.noreply.github.com> --- dice.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/dice.cpp b/dice.cpp index e7d6f9f..03529fd 100644 --- a/dice.cpp +++ b/dice.cpp @@ -4,6 +4,7 @@ #include #include #include +#include // Inspired by https://github.com/cdanek/KaimiraWeightedList void const_roll_table::init(const std::vector& weights) @@ -198,15 +199,34 @@ int roll_table::unique_rolls(int count, int* results, luck_type rollee_luck, int roll_weight = std::clamp(roll_weight, 0, 128); roll_weight = (size * roll_weight) >> 7; roll_weight = std::min(roll_weight, size / 2); + + const bool use_set = (count + start_index) > 16; + std::unordered_set seen; + if (use_set) + { + seen.reserve(count + start_index); + for (int j = 0; j < start_index; j++) + { + seen.insert(results[j]); + } + } + for (int i = 0; i < count; i++) { if ((int)table.size() <= i + start_index) return i; // ran out of options repeat: const int r = s.roll(roll_weight, size - roll_weight, rollee_luck); const int k = lookup(r); - for (int j = 0; j < start_index + i; j++) + if (use_set) + { + if (!seen.insert(k).second) goto repeat; + } + else { - if (results[j] == k) goto repeat; + for (int j = 0; j < start_index + i; j++) + { + if (results[j] == k) goto repeat; + } } results[start_index + i] = k; }