Карта от целочисленных диапазонов до произвольных единственных целых чисел

Работая в C++ в среде Linux, у меня есть ситуация, где много целочисленных диапазонов определяются, и целочисленные исходные данные отображаются на различные произвольные целые числа, на основе которого диапазона они падают в. Ни одно из перекрытия диапазонов, и они не всегда непрерывны.

"Самый простой" способ решить эту проблему с набором операторов "if" для каждого диапазона, но количество диапазонов, их границ и целевых значений может все варьироваться, таким образом, операторы "if" не удобны в сопровождении.

Например, диапазоны могли бы быть [0, 70], названы r_a, [101, 150], назвать это r_b, и [201, 400], назвать это r_c. Исходные данные в r_a отображаются на 1 в карте r_b к 2, и r_c отображаются на 3. Что-либо не в r_a, r_b, r_c отображается на 0.

Я могу придумать структуру данных и алгоритм, который хранит кортежи (границы, цель карты) и выполняет итерации через них, так нахождение, что целевое значение занимает время в количестве пар границ. Я могу также вообразить схему, которая сохраняет пар заказанными и использует двоичный алгоритм выхода вида против всех нижних границ (или верхние границы), находит самое близкое к входу, затем выдерживает сравнение со связанным противопоставлением.

Существует ли лучший способ выполнить отображение, чем основанный на двоичном поиске алгоритм? Еще лучше есть ли некоторая библиотека C++, которая уже делает это?

15
задан Ian Durkan 9 March 2011 в 16:35
поделиться

10 ответов

Я бы использовал очень простую вещь: std::map.

class Range
{
public:
  explicit Range(int item);  // [item,item]
  Range(int low, int high);  // [low,high]

  bool operator<(const Range& rhs) const
  {
    if (mLow < rhs.mLow)
    {
      assert(mHigh < rhs.mLow); // sanity check
      return true;
    }
    return false;
  } // operator<

  int low() const { return mLow; }
  int high() const { return mHigh; }

private:
  int mLow;
  int mHigh;
}; // class Range

Затем, давайте возьмем карту:

typedef std::map<Range, int> ranges_type;

И напишем функцию поиска на этой карте:

int find(int item, const ranges_type& ranges)
{
  ranges_type::const_iterator it = ranges.lower_bound(Range(item));
  if (it != ranges.end() && it->first.low() <= item)
    return it->second;
  else
    return 0; // No mapping ?
}

Основные преимущества:

  • Проверим, что диапазоны фактически не пересекаются во время вставки в набор (можно сделать так, чтобы он находился только в отладочном режиме)
  • Поддерживает редактирование диапазонов на лету
  • Поиск происходит быстро (бинарный поиск)

Если диапазоны заморожены (даже если их значения не совпадают), можно воспользоваться Локи: :AssocVector, чтобы уменьшить нагрузку на память и немного улучшить производительность (в основном, это отсортированный вектор с интерфейсом карты).

8
ответ дан 1 December 2019 в 02:55
поделиться

Возможно, вам потребуется xmlns: xlink = «http://www.w3.org/1999/xlink» в качестве атрибута в < svg... >.

-121--1481896-

InstallShield Express предназначен для базовых развертываний (это не что иное, как прославленный WinZip). Вы также можете проверить мой избранный AdvancedInstaller . У них также есть бесплатное экспресс-издание, но я думаю, что оба они не будут вам полезны, потому что если вам нужно что-либо сделать с IIS, MS SQL, Active Directory, GAC и т.д., вам потребуются выпуски «корпоративного уровня». WiX свободен, но кривая обучения настолько крута, что учиться не стоит. Я сожалею, что узнал это.

Если вы нуждаетесь в этом только для внутренних развертываний и не можете потратить 1000 долларов на программу установки, просто создайте собственный «установочный» проект с нуля. Пространство имен System.StartServices.Internal содержит несколько полезных оболочек для IIS, GAC и т. д. System.Configuration. Install.ManagedInstallerClass может помочь в развертывании служб Windows. Другими словами, можно создать собственную программу с нуля, которая сможет выполнить все необходимые шаги для развертывания основного продукта. Многие компании не используют для своих флагманских продуктов коммерческие установщики, они делают свои собственные.

-121--2220770-

Можно использовать два сортированных массива: один для нижних границ, другой для верхних. Используйте std:: lower _ bound (lower_bound_array, значение) и std:: upper _ bound (upper_bound_array, значение) . Если индекс обоих результатов одинаков, возвращает индекс + 1 . В противном случае возвращает 0 .

Если возвращенные индексы совпадают, это означает, что значение равно > = нижней границе и < верхней границе. Если они этого не делают, то вы находитесь между диапазонами.

1
ответ дан 1 December 2019 в 02:55
поделиться

Лучшим подходом здесь действительно является двоичный поиск, но любой эффективный поиск на основе порядка будет работать отлично. Вам действительно не нужно явно реализовывать поиск и структуру данных. Вы можете использовать его косвенно, используя вместо него стандартный ассоциативный контейнер.

Поскольку ваши диапазоны не пересекаются, решение очень простое. Вы можете немедленно использовать std :: map для этой проблемы, чтобы решить ее всего несколькими строками кода.

Например, это один из возможных подходов. Предположим, что мы отображаем диапазон [int, int] на значение int . Давайте представим наши диапазоны как закрытые-открытые диапазоны, т.е. если исходный диапазон равен [0, 70] , давайте вместо этого рассмотрим диапазон [0, 71) . Кроме того, давайте использовать значение 0 как «зарезервированное» значение, которое означает «без отображения» (как вы просили в своем вопросе)

const int EMPTY = 0;

Все, что вам нужно сделать, это объявить карту из int до int :

typedef std::map<int, int> Map;
Map map;

и заполните его каждым концом ваших закрытых-открытых диапазонов. Левый (закрытый) конец должен быть сопоставлен с желаемым значением, которому сопоставляется весь диапазон, а правый (открытый) конец должен быть сопоставлен с нашим значением EMPTY .В вашем примере это будет выглядеть следующим образом

map[0] = r_a;
map[71] = EMPTY;

map[101] = r_b;
map[251] = EMPTY;

map[260] = r_c; // 260 adjusted from 201
map[401] = EMPTY;

(я скорректировал ваш последний диапазон, поскольку в вашем исходном примере он перекрывал предыдущий диапазон, и вы сказали, что ваши диапазоны не перекрываются).

Вот и все, что касается инициализации.

Теперь, чтобы определить, где заданное значение i отображается на все, что вам нужно сделать, это

Map::iterator it = map.upper_bound(i);

Если it == map.begin () , то i не находится ни в каком диапазоне. В противном случае выполните

--it;

. Если it-> second (для декрементированного it ) равно EMPTY , то i не находится в любой диапазон.

Комбинированная проверка «промахов» может выглядеть следующим образом

Map::iterator it = map.upper_bound(i);
if (it == map.begin() || (--it)->second == EMPTY)
  /* Missed all ranges */;

В противном случае, it-> second (для уменьшенного it ) - ваше отображаемое значение

int mapped_to = it->second;

Обратите внимание, что если исходные диапазоны были «касающимися», как в [40, 60] и [61, 100] , то закрытые-открытые диапазоны будут выглядеть как [40, 61 ) и [61, 101) означают, что значение 61 будет отображено дважды во время инициализации карты. В этом случае важно убедиться, что значение 61 сопоставлено с правильным значением назначения, а не со значением EMPTY . Если вы сопоставите диапазоны, как показано выше, в порядке слева направо (т. Е. По возрастанию), он будет работать правильно сам по себе.

Обратите внимание, что в карту вставляются только конечные точки диапазонов, а это означает, что потребление памяти и производительность поиска зависят только от общего количества диапазонов и полностью не зависят от их общей длины.


При желании вы можете добавить на карту элемент «охранник» во время инициализации

map[INT_MIN] = EMPTY;

(соответствует «отрицательной бесконечности»), и проверка «промаха» станет проще

Map::iterator it = map.upper_bound(i);

assert(it != map.begin());
if ((--it)->second == EMPTY)
  /* Missed all ranges */;

, но это всего лишь вопрос личных предпочтений.

Конечно, если вы просто хотите вернуть 0 для не отображенных значений, вам вообще не нужно выполнять какие-либо проверки. Просто возьмите it-> second из декрементированного итератора, и все готово.

13
ответ дан 1 December 2019 в 02:55
поделиться

Запишите пределы в набор (или карту ). Когда вы вызываете insert , вы получите возвращаемое значение, которое является парой. Итератор и логическое значение. Если логическое значение истинно, создается новый элемент, который необходимо удалить позже. После этого шага с итератором и посмотрите, что вы нашли.

http://www.cplusplus.com/reference/stl/set/insert/ См. Возвращаемое значение

0
ответ дан 1 December 2019 в 02:55
поделиться

Идеальным вариантом является интервальное дерево (специализированное двоичное дерево). Википедия полностью описывает метод. Лучше, чем я. Вы не получите ничего более оптимального, чем это, без ущерба для производительности.

1
ответ дан 1 December 2019 в 02:55
поделиться

Ваши примеры диапазонов перекрываются, но вопрос говорит, что они не будут. Я предполагаю, что диапазон - это опечатка. Вы могли бы, могли бы, хранить назначения в массиве и использовать индексы в качестве диапазонов. Это довольно просто, но уродливо и не очень удобно для обслуживания. Нужно инициализировать массив до 0, затем для каждого диапазона провести итерацию по этим индексам и установить каждый индекс на целевое значение. Очень уродливо, но постоянное время поиска, так что может быть полезно, если цифры не будут слишком высоки, а диапазоны не будут меняться очень часто.

0
ответ дан 1 December 2019 в 02:55
поделиться

Простой связанный список, содержащий записи диапазона должно быть достаточно быстрым, скажем, даже для 50–100 диапазонов. Более того, вы можете реализовать Список пропуска , скажем, на верхних границах, чтобы ускорить эти запросы диапазона. Еще одна возможность - это Дерево интервалов .

В конечном итоге я бы выбрал самое простое: бинарный поиск.

0
ответ дан 1 December 2019 в 02:55
поделиться

Это одномерный пространственный индекс. Например, подойдет двоичное дерево в стиле квадродерева - и есть несколько других широко используемых методов.

0
ответ дан 1 December 2019 в 02:55
поделиться

Вы можете найти функцию минимального идеального хеширования полезной, http://cmph.sourceforge.net/ .

0
ответ дан 1 December 2019 в 02:55
поделиться

Разве простого массива не достаточно? Вы не говорите, сколько у вас элементов, но, безусловно, самая быстрая структура данных - это простой массив.

Если диапазоны:

  • 0..9 --> 25
  • 10..19 --> 42

Тогда массив будет просто таким:

[25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]
2
ответ дан 1 December 2019 в 02:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: