Это сильно зависит от ваших диапазонов. Диапазон может быть большим или маленьким, кластеризованным или не кластеризованным. Если у вас есть большие кластеризованные диапазоны (например, «все положительные 32-разрядные целые числа, которые можно разделить на 2), простой подход с Range (нижний, верхний) не будет успешным.
Полагаю, я могу сказать следующее:
если у вас есть небольшие диапазоны (кластеризация или нет, кластеризация здесь не имеет значения), рассмотрите битовые векторы. Эти маленькие существа быстро справляются с проверкой объединений, пересечений и членства, хотя итерация по всем элементам может занять некоторое время, в зависимости от размера. Более того, поскольку они просто используют один бит для каждого элемента, они довольно малы, если только вы не выбрасываете на них огромные диапазоны.
если у вас меньше и больше диапазонов, то класса Range, как описано другими, будет достаточно. Этот класс имеет атрибуты низший и верхний, а пересечение (a, b) в основном равно b.upper b.lower. Объединение и пересечение могут быть реализованы в постоянное время для отдельных диапазонов и для составных диапазонов, время увеличивается с числом поддиапазонов (таким образом, вы не хотите, чтобы не слишком много маленьких диапазонов)
Если у вас есть огромное пространство, где могут быть ваши числа, и диапазоны распределены в неприятном свете, вам следует взглянуть на диаграммы двоичных решений (BDD). Эти изящные диаграммы имеют два конечных узла, Истинный и Ложный и узлы принятия решения для каждого бита входа. Узел принятия решения имеет бит, на который он смотрит, и два следующих узла графа - один для «бит равен одному» и один для «бит равен нулю». Учитывая эти условия, вы можете кодировать большие диапазоны в крошечном пространстве. Все положительные целые числа для произвольно больших чисел могут быть закодированы в 3-х узлах на графике - в основном это единственный узел решения для младшего значащего бита, который переходит в False в 1 и в True в 0.
Пересечение и объединение являются довольно элегантными рекурсивными алгоритмами, например, пересечение в основном берет два соответствующих узла в каждом BDD, пересекает 1-ребро, пока не появится некоторый результат и не проверит: является ли один из результатов False-Terminal, создать 1-ветку к False-терминалу в результате BDD. Если оба являются True-Terminal, создайте 1-ветвь для True-Terminal в BDD результата. Если это что-то еще, создайте 1-ветку для этого чего-то еще в BDD результата. После этого начинается некоторая минимизация (если 0- и 1-ветвь узла идут к одному и тому же следующему BDD / терминалу, удалите его и перетащите входящие переходы к цели), и вы станете золотым. Мы даже пошли дальше, мы работали над моделированием добавления наборов целых чисел в BDD, чтобы улучшить прогнозирование значений для оптимизации условий.
Эти соображения подразумевают, что ваши операции ограничены количеством битов в вашем диапазоне номеров, то есть log_2 (MAX_NUMBER). Подумайте об этом, вы можете пересекать произвольные наборы 64-битных целых чисел практически за постоянное время.
Дополнительную информацию можно получить, например, в Википедии и ссылочных документах.
Кроме того, если ложные срабатывания терпимы и вам нужна только проверка на существование, вы можете посмотреть фильтры Блума. Фильтры Блума используют вектор хэшей, чтобы проверить, содержится ли элемент в представленном наборе. Пересечение и Союз это постоянное время. Основная проблема здесь заключается в том, что вы получаете увеличивающийся уровень ложноположительных результатов, если вы слишком много заполняете фильтр Блума.
Информация, опять же, в Википедии , например.
Хач, представление множества - это забавное поле. :)