Работая в 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++, которая уже делает это?
Я бы использовал очень простую вещь: 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
, чтобы уменьшить нагрузку на память и немного улучшить производительность (в основном, это отсортированный вектор с интерфейсом карты).
Возможно, вам потребуется
xmlns: xlink = «http://www.w3.org/1999/xlink»
в качестве атрибута в < svg... >
.
InstallShield Express предназначен для базовых развертываний (это не что иное, как прославленный WinZip). Вы также можете проверить мой избранный AdvancedInstaller . У них также есть бесплатное экспресс-издание, но я думаю, что оба они не будут вам полезны, потому что если вам нужно что-либо сделать с IIS, MS SQL, Active Directory, GAC и т.д., вам потребуются выпуски «корпоративного уровня». WiX свободен, но кривая обучения настолько крута, что учиться не стоит. Я сожалею, что узнал это.
Если вы нуждаетесь в этом только для внутренних развертываний и не можете потратить 1000 долларов на программу установки, просто создайте собственный «установочный» проект с нуля. Пространство имен System.StartServices.Internal
содержит несколько полезных оболочек для IIS, GAC и т. д. System.Configuration. Install.ManagedInstallerClass
может помочь в развертывании служб Windows. Другими словами, можно создать собственную программу с нуля, которая сможет выполнить все необходимые шаги для развертывания основного продукта. Многие компании не используют для своих флагманских продуктов коммерческие установщики, они делают свои собственные.
Можно использовать два сортированных массива: один для нижних границ, другой для верхних. Используйте std:: lower _ bound (lower_bound_array, значение)
и std:: upper _ bound (upper_bound_array, значение)
. Если индекс обоих результатов одинаков, возвращает индекс + 1
. В противном случае возвращает 0
.
Если возвращенные индексы совпадают, это означает, что значение равно > =
нижней границе и <
верхней границе. Если они этого не делают, то вы находитесь между диапазонами.
Лучшим подходом здесь действительно является двоичный поиск, но любой эффективный поиск на основе порядка будет работать отлично. Вам действительно не нужно явно реализовывать поиск и структуру данных. Вы можете использовать его косвенно, используя вместо него стандартный ассоциативный контейнер.
Поскольку ваши диапазоны не пересекаются, решение очень простое. Вы можете немедленно использовать 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
из декрементированного итератора, и все готово.
Запишите пределы в набор
(или карту
). Когда вы вызываете insert
, вы получите возвращаемое значение, которое является парой. Итератор и логическое значение. Если логическое значение истинно, создается новый элемент, который необходимо удалить позже. После этого шага с итератором и посмотрите, что вы нашли.
http://www.cplusplus.com/reference/stl/set/insert/ См. Возвращаемое значение
Идеальным вариантом является интервальное дерево (специализированное двоичное дерево). Википедия полностью описывает метод. Лучше, чем я. Вы не получите ничего более оптимального, чем это, без ущерба для производительности.
Ваши примеры диапазонов перекрываются, но вопрос говорит, что они не будут. Я предполагаю, что диапазон - это опечатка. Вы могли бы, могли бы, хранить назначения в массиве и использовать индексы в качестве диапазонов. Это довольно просто, но уродливо и не очень удобно для обслуживания. Нужно инициализировать массив до 0, затем для каждого диапазона провести итерацию по этим индексам и установить каждый индекс на целевое значение. Очень уродливо, но постоянное время поиска, так что может быть полезно, если цифры не будут слишком высоки, а диапазоны не будут меняться очень часто.
Простой связанный список, содержащий записи диапазона должно быть достаточно быстрым, скажем, даже для 50–100 диапазонов. Более того, вы можете реализовать Список пропуска , скажем, на верхних границах, чтобы ускорить эти запросы диапазона. Еще одна возможность - это Дерево интервалов .
В конечном итоге я бы выбрал самое простое: бинарный поиск.
Это одномерный пространственный индекс. Например, подойдет двоичное дерево в стиле квадродерева - и есть несколько других широко используемых методов.
Вы можете найти функцию минимального идеального хеширования полезной, http://cmph.sourceforge.net/ .
Разве простого массива не достаточно? Вы не говорите, сколько у вас элементов, но, безусловно, самая быстрая структура данных - это простой массив.
Если диапазоны:
Тогда массив будет просто таким:
[25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42]