Подтвердить что ты не робот

Алгоритм - Сортировка массива с различными элементами LogLogN

Это не моя домашняя работа в школе. Это моя собственная домашняя работа, и я являюсь самообучающимися алгоритмами.

В Руководство по разработке алгоритмов, есть такой акциз

4-25 Предположим, что массив A [1..n] имеет только цифры из {1,.,, n ^ 2}, но не более, чем log log n этих чисел. Разработать алгоритм, который сортирует A по существу меньше O (n log n).

У меня есть два подхода:


Первый подход:

В основном я хочу сделать подсчет сортировки для этой проблемы. Я могу сначала просмотреть весь массив (O (N)) и поместить все различные числа в массив размера loglogN (int [] K).

Затем примените сортировку counting. Однако при настройке счетного массива (int [] C) мне не нужно устанавливать его размер как N ^ 2, вместо этого я также устанавливаю размер как loglogN.

Но таким образом, при подсчете частот каждого отдельного числа, я должен сканировать массив K, чтобы получить этот индекс элемента (O (NloglogN), а затем обновить массив C.


Второй подход:

Опять же, мне нужно отсканировать весь массив, чтобы получить отдельный массив чисел K с размером loglogN.

Затем я просто делаю вид быстрой сортировки, но раздел основан на медиане массива K (т.е. каждый раз, когда стержень является элементом массива K), рекурсивно.

Я думаю, что этот подход будет лучшим, с O (NlogloglogN).


Я прав? или есть лучшие решения?

Подобные акцизы существуют в Руководстве по проектированию алгоритмов, например

4-22 Покажите, что n положительных целых чисел в диапазоне от 1 до k можно сортировать по времени O (n log k). Интересным случаем вл етс, когда k < п.

4-23 Мы стремимся сортировать последовательность S из n целых чисел со многими дублированиями, так что число различных целых чисел в S равно O (log n). Дайте алгоритм наихудшего временного времени O (n log log n) для сортировки таких последовательностей.

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

Спасибо

4b9b3361

Ответ 1

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

Отсортируйте эту карту в log(n)*log(log(n)) время i.e(klogk) с помощью любого алгоритма сортировки.

Теперь сканирует хэш-карту и добавляет элементы к новому числу частот в массиве. Например:

total time = 2n+log(n)*log(log(n)) = O(n) 

Ответ 2

Сортировка сортировки - один из возможных способов:

  • Я продемонстрирую это решение на примерах 2, 8, 1, 5, 7, 1, 6, и все числа равны <= 3 ^ 2 = 9. Я использую больше элементов, чтобы сделать мою идею более понятной.
  • Сначала для каждого числа A [i] вычислить A [i]/N. Позволяет называть это число first_part_of_number.
  • Сортировка этого массива с помощью сортировки count first_part_of_number.
  • Результаты приведены в форме (пример для N = 3)

    (0, 2)
     (0, 1)
     (0, 1)
     (2, 8)
     (2, 6)
     (2, 7)
     (2, 6)

  • Разделите их на группы на first_part_of_number.

  • В этом примере у вас будут группы  (0, 2)  (0, 1)  (0, 1)

    и

    (2, 8)  (2, 6)  (2, 7)  (2, 6)

  • Для каждого числа вычислить X по модулю N. Позволяет называть его second_part_of_number. Добавьте этот номер в каждый элемент
     (0, 2, 2)  (0, 1, 1)  (0, 1, 1)

    и

    (2, 8, 2)  (2, 6, 0)  (2, 7, 1)  (2, 6, 0)

  • Сортировка каждой группы с помощью сортировки подсчета second_part_of_number

    (0, 1, 1)  (0, 1, 1)  (0, 2, 2)

    и

    (2, 6, 0)  (2, 6, 0)  (2, 7, 1)  (2, 8, 2)

  • Теперь объедините все группы, и у вас есть результат 1, 1, 2, 6, 6, 7, 8.

Сложность: Вы использовали только подсчет сортировки по элементам <= N. Каждый элемент принимал участие ровно в 2 "сортах". Таким образом, общая сложность O (N).

Ответ 3

Обновление: после того, как я написал ответ ниже, @Nabb показал мне, почему это было неправильно. Для получения дополнительной информации см. Краткое описание Википедии на Õ и ссылки из нее. По крайней мере потому, что по-прежнему необходимо придавать контекст комментариям @Nabb и @Blueshift, и поскольку все обсуждение остается интересным, мой первоначальный ответ сохраняется следующим образом.

ОРИГИНАЛЬНЫЙ ОТВЕТ (НЕПРАВИЛЬНЫЙ)

Позвольте мне предложить нетрадиционный ответ: хотя действительно существует разница между O (n * n) и O (n), нет разницы между O (n) и O (n * log (n)).

Теперь, конечно, мы все знаем, что то, что я только что сказал, ошибочно, не так ли? В конце концов, различные авторы согласны с тем, что O (n) и O (n * log (n)) отличаются.

За исключением того, что они не отличаются.

Такая радикально-кажущаяся позиция, естественно, требует оправдания, поэтому рассмотрите следующее, а затем создайте свой собственный разум.

Математически, по существу, порядок m функции f (z) таков, что f (z)/(z ^ (m + epsilon)) сходится, а f (z)/(z ^ (m-epsilon)) расходится для z большой величины и реального положительного эпсилона сколь угодно малой величины. Z может быть реальным или сложным, хотя, как мы сказали, epsilon должен быть реальным. При таком понимании примените правило L'Hospital к функции O (n * log (n)), чтобы увидеть, что он не отличается по порядку от функции O (n).

Я бы утвердил, что принятая в настоящее время компьютерно-научная литература немного ошибочна в этом вопросе. Эта литература в конечном итоге усовершенствует свою позицию в этом вопросе, но пока этого не сделала.

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

Кстати, противоположная импликация этой позиции ответа заключается в том, что можно получить доступ к сбалансированному двоичному дереву в O (1) раз. Опять же, мы все знаем, что это ложь, не так ли? Он должен быть O (log (n)). Но помните: нотация O() никогда не предназначалась для точной оценки вычислительных требований. Если n не очень велико, другие факторы могут быть более важными, чем порядок функций. Но даже при n = 1 миллион log (n) составляет всего 20, сравнивается, скажем, с sqrt (n), что равно 1000. И я мог бы продолжать в этом ключе.

В любом случае, подумайте. Даже если, в конце концов, вы решите, что не согласны со мной, вы можете найти позицию, интересную тем не менее. Со своей стороны, я не уверен, насколько полезной является O() нотация, когда дело доходит до O (log something).

@Blueshift задает некоторые интересные вопросы и поднимает некоторые правильные моменты в комментариях ниже. Я рекомендую вам прочитать его слова. Мне действительно нечего добавить к тому, что он должен сказать, за исключением того, что это видно из-за того, что немногие программисты (или нуждаются) в обосновании математической теории сложной переменной, O (log (n)) нотация, скорее всего, ввела в заблуждение, буквально сотни тысяч программистов, полагая, что они достигают в основном иллюзорных достижений в вычислительной эффективности. Редко на практике уменьшает O (n * log (n)) до O (n), действительно покупает вам то, что вы можете подумать, что оно покупает вас, если у вас нет четкого мысленного образа того, насколько невероятно медленной функция логарифма - тогда как уменьшение O (n) даже до O (sqrt (n)) может купить вас много. Математик сказал бы компьютерному ученому эти десятилетия назад, но компьютерный ученый не слушал, спешил или не понимал смысла. И все в порядке. Я не против. Есть много и много очков по другим предметам, которые я не понимаю, даже когда очки тщательно объясняются мне. Но это то, что я считаю, что я действительно понимаю. По сути, это математическая точка, а не компьютерная точка, и это точка, с которой я столкнулся с Лебедевым и математиками, а не с Кнутом и компьютерными учеными. Это все.

Ответ 4

Я собираюсь предать мои ограниченные знания алгоритмической сложности здесь, но:

Не имеет смысла сканировать массив один раз и строить что-то вроде дерева с балансировкой? Поскольку мы знаем, что количество узлов в дереве будет расти только до (log log n), это относительно дешево (?), Чтобы каждый раз находить число. Если число повторений найдено (вероятно), счетчик в этом случае node увеличивается. Затем, чтобы построить отсортированный массив, прочитайте дерево в порядке.

Возможно, кто-то может прокомментировать сложность этого и любые недостатки.