Я хотел бы улучшить скорость работы моего метода подсчета ящиков, который я использую в фрактальном анализе.
Об этой задаче
У меня есть поток ints (около n = 2 ^ 24 long), и я должен рассчитать, сколько разных значений в потоке. Там не допускается верхняя граница и отрицательные значения (но количество отрицательных значений, возможно, меньше, чем sqrt (n)). Там небольшая корреляция в потоке, то есть фактический элемент, вероятно, будет равен или не слишком далеко от предыдущего. Во многих случаях у меня много одинаковых значений во всем диапазоне.
Методы, которые я уже пробовал
вектор, сортировка, uniqe
Моя первая реализация заключалась в том, чтобы поместить все элементы в вектор, а затем я применил std:: sort, а затем std:: unique.
Сложность этого метода O (n * log (n)), и я не думаю, что любой другой алгоритм может быть быстрее вообще, когда дело доходит до масштабирования. Но я уверен, что код должен существовать быстрее, чем это, но с теми же свойствами масштабирования - так быстрее, только с постоянным коэффициентом. Причины таковы:
- У меня есть много равных значений, хранящихся в векторе, поэтому сортировка не так эффективна, вектор чрезмерно большой
- В этом методе я не использую информацию о том, что фактический элемент и предыдущие близки друг к другу.
- Мне не нужна информация о том, каковы эти уникальные значения, мне нужно только количество различных элементов
набор, вставка, размер
Чтобы исключить первую точку неэффективности, я помещаю все элементы в набор с помощью set:: insert. И в конце я подсчитал количество элементов с set:: size.
Я ожидал, что этот код должен быть быстрее, потому что в наборе хранятся только уникальные значения, и ему не нужно сравнивать новые элементы с большим количеством равных значений. Но, к сожалению, этот метод был в 1,5 раза медленнее предыдущего.
set, emplace_hint, размер
Чтобы исключить вторую недействительную точку, я не только помещаю все элементы в набор, но и с помощью функции set:: emplace_hint. И каждый раз давал подсказку, чтобы добавить новый элемент рядом с предыдущим. И в конце я попросил размер набора с набором:: size
Я ожидал, что этот код будет быстрее предыдущего, потому что я могу угадать значение нового элемента, и это лучше, чем ничего. Но, к сожалению, этот метод был в 5 раз медленнее предыдущего.
Вопрос
Можно ли предложить какой-либо эффективный метод, который может вычислять количество различных элементов (ints) в потоке? Можете ли вы оптимизировать код, если известно, что
- существует измеримая корреляция в числах
- некоторые цифры отображаются повторно
Целевая архитектура - современный процессор x86 или x86-64 PC (с sse, sse2), и подходит только один код потока. Я предпочитаю не использовать boost, но С++ 11.
Решение
Во-первых, спасибо за многие предложения, терпение и понимание, и я сожалею, что не могу проверить все методы, и я также уверен, что эффективность зависит от деталей потока ints, что у меня нет предоставлена. Однако я разделяю результаты, полученные с помощью компилятора VS2013. (Код тестируется под gcc4.7, но не измеряется.) Этот вопрос стоит гораздо больше времени для исследования, но у меня есть решение, соответствующее моим потребностям.
О методах:
- вектор bool: решение BitVector от Dieter Lücking
- двоичный поиск: метод, предложенный Tony D
- неупорядоченный набор: просто введите все элементы в std:: unordered_set, а затем спросите количество его элементов, как предложено Ixanezis
- векторная вставка отсортирована: используя Дитер Люкинг Сортированный векторный подход
- set insert: метод, описанный в форме вопроса
- radix sort: предложение Ixanezis, используя популярный алгоритм сортировки по вектору
- установить emplace hint: используя std:: emplace_hint, как описано в форме вопроса