Научно-исследовательский университет Томский политехнический университет Институт Кибернетики Кафедра ОСУ Пояснительная записка к курсовой работе по дисциплине «Структуры и алгоритмы обработки данных» Вариант №15 Исполнитель студент, 8В83 ____________ Б.А. Сафронов (подпись) ____________ (дата) Руководитель доцент ____________ О.Б. Фофанов (подпись) ____________ (дата) Томск 2010 Содержание 1. Моделирование абстрактных типов данных (АТД) для различных реализаций ..........4 1.1 Постановка задачи .............................................................................................................4 1.2 Краткое теоретическое описание реализуемых объектов ............................................6 1.3 Набор допустимых операций ...........................................................................................6 1.4 Результат работы программы...........................................................................................7 Вывод по разделу ....................................................................................................................7 2. Поиск информации в различных структурах данных .......................................................8 2.1 Постановка задачи .............................................................................................................8 2.2 Краткое теоретическое описание реализуемых объектов ............................................8 2.3 Анализ результатов ...........................................................................................................9 Вывод по разделу ....................................................................................................................9 3. Исследование эффективности алгоритмов сортировок для различных структур и размерностей данных ...............................................................................................................10 3.1 Постановка задачи ...........................................................................................................10 3.2 Краткое теоретическое описание реализуемых объектов ..........................................10 Сортировка Шелла: ............................................................................................................10 Сортировка пузырьком: .....................................................................................................10 Глупая сортировка:.............................................................................................................10 3.3 Анализ результатов .........................................................................................................11 Вывод по разделу ..................................................................................................................14 4. Реализация структур данных типа дерево и типовые алгоритмы их обработки ..........15 4.1 Постановка задачи ...........................................................................................................15 4.2 Краткие теоретические сведения...................................................................................15 Добавление элемента в Б-дерево. ...................................................................................15 Удаление элементов из Б-дерева.....................................................................................15 Поиск в Б-дереве. ...............................................................................................................16 4.3 Структура дерева .............................................................................................................16 4.4 Анализ результатов .........................................................................................................17 Вывод по разделу ..................................................................................................................18 5. Реализация функций расстановки (хеширование) и разрешение коллизий методом построения цепочки. .................................................................................................................19 5.1 Постановка задачи ...........................................................................................................19 2 5.2 Краткие теоретические сведения...................................................................................19 5.3 Описание хеш-функции ...................................................................................................20 5.4 Анализ результатов .........................................................................................................21 Вывод по разделу ..................................................................................................................23 Список литературы ....................................................................................................................24 Приложение 1 ............................................................................................................................25 Приложение 2 ............................................................................................................................34 Приложение 3 ............................................................................................................................36 Приложение 4 ............................................................................................................................37 Приложение 5 ............................................................................................................................43 3 1. Моделирование абстрактных типов данных (АТД) для различных реализаций 1.1 Постановка задачи Номер задачи: 7; Система состоит из двух процессоров P1 и P2, трёх очередей F1, F2, F3 и стека (рис.1.7). В систему могут поступать запросы на выполнение задач трёх типов – Т1, Т2, Т3. Задача типа Т1 может выполняться только процессором P1. Задача типа Т2 может выполняться только процессором P2. Задача типа Т3 может выполняться любым процессором. Запрос можно представить записью. Type TInquiry= record Name: String[10]; {имя запроса} Time: Word; {время обслуживания} T: Byte; {тип запроса} end; Генератор задач О Ч Е Р Е Д Ь О Ч Е Р Е Д Ь О Ч Е Р Е Д Ь F1 F2 F3 P1 P2 Стек Рис.1 4 Поступающие запросы ставятся в соответствующие типам задач очереди. Если очередь F1 не пуста и процессор P1 свободен, то задача из очереди F1 поступает на обработку в процессор P1. Если процессор Р1 обрабатывает задачу типа Т3, а процессор Р2 свободен и очередь F2 пуста, то задача из процессора Р1 поступает в процессор Р2, а задача из очереди F1 в процессор Р1, если же процессор Р2 занят или очередь F2 не пуста, то задача из процессора Р1 помещается в стек. Если очередь F2 не пуста и процессор P2 свободен, то задача из очереди F2 поступает на обработку в процессор P2. Если процессор Р2 обрабатывает задачу типа Т3, а процессор Р1 свободен и очередь F1 пуста, то задача из процессора Р2 поступает в процессор Р1, а задача из очереди F2 - в процессор Р2, если же процессор Р1 занят или очередь F1 не пуста, то задача из процессора Р1 помещается в стек. Если очередь F3 не пуста и процессор P1 свободен, и очередь F1 пуста или свободен процессор Р2 и очередь F2 пуста, то задача из очереди F3 поступает на обработку в свободный процессор. Задача из стека поступает на обработку в свободный процессор Р1, если очередь F1 пуста, или в свободный процессора Р2, если очередь F2 пуста. Стек: 4; Стек в динамической памяти (связанная схема). Очередь: 5; Очередь на ОЛС. “Хвост” очереди – последний, а “голова” - первый элемент ОЛС 5 1.2 Краткое теоретическое описание реализуемых объектов Стек – это специальный тип списка, в котором все вставки и удаления выполняются в начале(FIFO-first in first out), называемом вершиной (top). То есть всегда есть доступ только к верхнему элементу, к дальнейшим можно обращаться только после удаления первого. В нашем случае стек представлен динамической структурой: элемент стека – запись, содержащая указатель на следующий элемент. Очередь – специальный тип списка - (queue), в котором вставка элементов осуществляется с одного конца (rear), а удаление с другого (front). В динамической реализации в записи присутствуют 3 указателя: на следующий элемент, на текущий и на предыдущий. 1.3 Набор допустимых операций Для стека: IndexLast () - возвращает индекс последнего элемента стека. Size() - возвращает размер стека. get() - удаляет элемент из вершины стека (выталкивает из стека) add (x). Помещает элемент х в вершину стека. Для очереди: getLast() – возвращает последний элемент очереди. add (x). Помещает элемент х в конец очереди. deq() удаляет последний элемент очереди, при этом возвращая его . Size() - возвращает размер очереди. 6 1.4 Результат работы программы Пример работы программы: Step:1 Processor "P1": Type1Works. Time left: 3. Processor "P2": Type2Works. Time left: 2. List:(49,1,2)(45,1,6)(44,1,2)(40,1,2)(33,1,3)(31,1,3)(30,1,5)(29,1,4)(23,1,2)(21,1,6)(19,1,2)(16,1 ,2)(14,1,2)(13,1,4)(11,1,2)(8,1,4)(7,1,6)(6,1,3)(5,1,3)(4,1,2) List:(47,2,5)(42,2,6)(38,2,5)(37,2,4)(36,2,2)(35,2,6)(32,2,6)(25,2,6)(24,2,5)(18,2,4)(17,2,2)(15,2 ,2)(12,2,3)(10,2,5)(2,2,4) List:(48,3,4)(46,3,3)(43,3,4)(41,3,4)(39,3,4)(34,3,4)(28,3,3)(27,3,2)(26,3,4)(22,3,3)(20,3,2)(9,3, 2)(1,3,5) Stack: … Step:154 Processor "P1": Type1Works. Time left: 5. Processor "P2": Idle. List:(22,1,4)(19,1,5)(17,1,2)(10,1,4)(9,1,6)(7,1,6)(6,1,5) List:(28,2,5)(27,2,2)(26,2,6)(18,2,4)(15,2,3)(13,2,2)(3,2,5)(2,2,4) List:(29,3,2)(25,3,4)(24,3,2)(23,3,2)(21,3,4)(20,3,6)(16,3,2)(14,3,2)(12,3,2)(11,3,2)(8,3,3)(1,3,3) (25,3,3)(21,3,4)(17,3,3)(16,3,6)(14,3,4)(10,3,2)(9,3,3)(1,3,2)(48,3,4)(46,3,3)(43,3,4)(41,3,4)(39, 3,4) Stack:(34,3,3) Step:155 Processor "P1": Type1Works. Time left: 4. Processor "P2": Type2Works. Time left: 3. List:(22,1,4)(19,1,5)(17,1,2)(10,1,4)(9,1,6)(7,1,6)(6,1,5) List:(28,2,5)(27,2,2)(26,2,6)(18,2,4)(15,2,3)(13,2,2)(3,2,5) List:(29,3,2)(25,3,4)(24,3,2)(23,3,2)(21,3,4)(20,3,6)(16,3,2)(14,3,2)(12,3,2)(11,3,2)(8,3,3)(1,3,3) (25,3,3)(21,3,4)(17,3,3)(16,3,6)(14,3,4)(10,3,2)(9,3,3)(1,3,2)(48,3,4)(46,3,3)(43,3,4)(41,3,4)(39, 3,4) Stack:(34,3,3) … Step:224 Processor "P1": Idle. Processor "P2": Idle. List: List: List: Stack: Вывод по разделу В ходе моделирования комплексной структуры состоящей из очереди, трех обработчиков задачи и стека, были показаны принципы работы АТД список и очередь, так же их возможная совместная реализации. 7 2. Поиск информации в различных структурах данных 2.1 Постановка задачи Изучение алгоритмов поиска контекста в больших объемах текстовой информации и закрепление навыков в проведении сравнительного анализа алгоритмов. 1. Реализовать алгоритм поиска Кнута — Морриса — Пратта. 2. Построить графики зависимости количества операций сравнения от количества символов в массиве. 3. Определить временную сложность алгоритма и сравнить с экспериментальными исследованиями 2.2 Краткое теоретическое описание реализуемых объектов Алгоритм Кнута — Морриса — Пратта использует предобработку искомой строки, а именно: на ее основе создается префикс-функция. При этом используется следующая идея: если префикс (он же суффикс) строки длинной i длиннее одного символа, то он одновременно и префикс подстроки длинной i-1. Таким образом, мы проверяем префикс предыдущей подстроки, если же тот не подходит, то префикс ее префикса, и т.д. Действуя так, находим наибольший искомый префикс. Следующий вопрос, на который стоит ответить: почему время работы процедуры линейно, ведь в ней присутствует вложенный цикл? Ну, во-первых, присвоение префикс-функции происходит четко m раз, остальное время меняется переменная k. Так как в цикле while она уменьшается (P[k]<k), но не становится меньше 0, то уменьшаться она может не чаще, чем возрастать. Переменная k возрастает на 1 не более m раз. Значит, переменная k меняется всего не более 2m раз. Выходит, что время работы всей процедуры есть O(m). 8 2.3 Анализ результатов Количество знаков в тексте 856415. Тест №1 ( Длинная строка ) Строка1: «который заснул тотчас же, как лег на постель, никто долго не спал эту ночь.» Время1: 43254170 Тест №2 ( Средняя строка ) Строка2: «который заснул тотчас же, как лег на постель,» Время2: 55077812 Тест №3 ( Короткая строка ) Строка3: «который зас» Время3: 66908248 Зависимость времени от длины строки 80000000 70000000 60000000 50000000 40000000 30000000 20000000 10000000 0 Строка1 Строка2 Строка3 На графике видно, что скорость поиска линейно зависима от длины строки. Вывод по разделу Исходя из экспериментальных данных, видно, что при увеличении сложности фразы время поиска линейно уменьшается. Алгоритм КМП хорош тем, что имеет сложность O(n+m), что обеспечивает более быстрый поиск, чем, например, поиск слова методом посимвольного сравнения. Однако нельзя полностью полагаться на этот алгоритм: при больших запросах, состоящих из неодинаковых символов, время работы алгоритма будет большим. 9 3. Исследование эффективности алгоритмов сортировок для различных структур и размерностей данных 3.1 Постановка задачи Провести экспериментальный сравнительный анализ различных методов сортировки. Для чистоты эксперимента сортировка должна проводиться на одинаковых наборах входных данных, которые генерируются случайным образом. Для более полного анализа методов сортировка должна проводиться для различных размерностей данных Проследить динамику роста требуемого для сортировки времени. Проверить, как ведут себя методы на различных входных данных: упорядоченных в прямом порядке, упорядоченных в обратном порядке и случайных. Сравнить теоретические оценки времени сортировки и числа требуемых операций с экспериментальными. 3.2 Краткое теоретическое описание реализуемых объектов Быстрая сортировка: выбрать элемент, называемый опорным. сравнить все остальные элементы с опорным, на основании сравнения разбить множество на три — «меньшие опорного», «равные» и «большие», расположить их в порядке меньшие-равные-большие. повторить рекурсивно для «меньших» и «больших». Сортировка Шелла: Идея сортировки состоит в сравнении элементов, стоящих не только рядом, но и на расстоянии друг от друга. Иными словами — сортировка вставками либо сортировка простыми обменами с предварительными «грубыми» проходами. При сортировке Шелла сначала сравниваются и сортируются между собой ключи, отстоящие один от другого на некотором расстоянии d. После этого процедура повторяется для некоторых меньших значений d, а завершается сортировка Шелла упорядочиванием элементов при d = 1 Сортировка пузырьком: Алгоритм состоит в повторяющихся проходах по сортируемому массиву. За каждый проход элементы последовательно сравниваются попарно и, если порядок в паре неверный, выполняется обмен элементов. Проходы по массиву повторяются до тех пор, пока на очередном проходе не окажется, что обмены больше не нужны, что означает — массив отсортирован. При проходе алгоритма, элемент, стоящий не на своём месте, «всплывает» до нужной позиции как пузырёк в воде, отсюда и название алгоритма. Глупая сортировка: Имеет нечто общее с сортировкой пузырьком: идёт поиск от начала массива, текущий элемент сравнивается со следующим, если следующий меньше, то производится обмен и возврат в начало цикла. 10 3.3 Анализ результатов Исследования проводились на одинаковых данных, для получения более точных результатов. Быстрая сортировка №1 – опорным элементом выбирается средний элемент. Быстрая сортировка №2 – опорным элементом выбирается самый крайний. Пример работы программы Сравнительный анализ алгоритмов сортировки в не отсортированном массиве размерностью N: N 500 1000 3000 5000 8000 10000 30000 60000 Пузырьком, нс 1343266 5410761 49207911 144599151 357047808 550873282 5177930700 21100235280 Быстрая 1 ,нс 67489 143822 471958 891783 1331624 1712354 5902243 12367204 Быстрая 2, нс 52594 113568 397487 702814 1167323 1482426 5334407 10575726 Шелла, нс Глупая, нс 49802 110310 399815 725620 1248310 1665344 5915276 12836367 32128987 248885732 6886380054 45285407760 184958931443 258014094452 - 11 Как видно из таблицы наиболее худшее время показала «Глупая» сортировка, что подтверждает ее теоретическая временная сложность O(n3). Наилучшие результаты, при больших размерах массива, показала Быстрая сортировка №2, при небольшом размере массива наиболее эффективная сортировка Шелла. Сортировка пузырьком показывает не плохие значения только при небольших размерах массива, при увеличении размера массива эффективность этого алгоритма резку уменьшается. В исследование были использованы два вида Быстрой сортировки, имеющие одинаковый алгоритм, но различную реализацию, что показывает то, что для достижения минимального времени обработки данных, не достаточно применить быстрейший алгоритм, но и важно осуществить его оптимальную реализацию. 30 25 20 Пузырька Быстрая 1 15 Быстрая 2 Шелла 10 Глуппая 5 0 500 1000 3000 5000 8000 10000 30000 60000 График зависимости времени выполнения от размерности не упорядоченного массива (мс) Сравнительный анализ алгоритмов сортировки в отсортированном массиве в прямом порядке размерностью N: N 500 1000 3000 5000 8000 10000 30000 60000 Пузырьком, нс Быстрая 1 ,нс Быстрая 2, нс Шелла, нс Глупая, нс 4189 5585 13963 20480 31650 41425 114498 227135 29789 58645 200604 322085 515242 726552 2155918 4483117 266232 1099835 9231535 26003197 66452720 - 18617 37236 125669 224808 373748 562251 1986963 3417260 3724 4189 9309 13963 20945 27927 74471 148476 В упорядоченном массиве результаты обратные чем полученные ранее, самыми 12 эффективными оказались сортировка пузырьком и «Глуппая» сортировка, так как они имеют самый простой алгоритм и реализацию, в упорядоченном массиве их временная сложность О(n), что подтверждается графиком(Рис. 4). Худшей оказалась «Быстрая 2» сортировка, при размерности массива более 8000, компилятор выдавал исключения из перегрузки стека, в следствии слишком глубокой рекурсии и не правильного выбора опорного элемента. «Быстрая 1» сортировка выполняется корректно, хотя имеет такой же алгоритм, но опорный элемент всегда выбирается по центру. Алгоритм Шелла показал стабильные результаты. 5 4.5 4 3.5 Пузырька 3 Быстрая 1 2.5 Быстрая 2 2 Шелла Глуппая 1.5 1 0.5 0 500 1000 3000 5000 8000 10000 30000 60000 График зависимости времени выполнения от размерности упорядоченного по возрастанию массива (мс) Сравнительный анализ алгоритмов сортировки отсортированном массиве в обратном порядке размерностью N: Быстрая 2, Шелла, нс Глупая, нс нс 1103092 32116 271352 25599 49428789 500 5193378 69816 1110074 61438 559108776 1000 38735306 220153 9379545 178264 15227837962 3000 108025813 511053 26131193 326739 50150923083 5000 276824583 514777 66425259 371421 209714885467 8000 439645241 679077 91452894 505467 415666856722 10000 2333839112 2478468 1943677 30000 16794486864 4936456 5198964 60000 Если массив упорядочен в обратном порядке «Глупая» сортировка опять показывает наихудший результат так как ее временная сложность О(n!). «Быстрая 2» сортировка так же оказалось не стабильной из-за глубокой рекурсии. Лучший результат при небольших N Пузырьком, нс в Быстрая 1 ,нс 13 размерностях массива показала сортировка Шелла, а при больших «Быстрая 1» сортировка. 5 4.5 4 3.5 Пузырька 3 Быстрая 1 2.5 Быстрая 2 2 Шелла 1.5 Глуппая 1 0.5 0 500 1000 3000 5000 8000 10000 30000 60000 График зависимости времени выполнения от размерности упорядоченного в обратном порядке массива (мс) Вывод по разделу Для получения наиболее стабильных результатов сортировки предпочтительней использовать сортировку Шелла, а если необходима обрабатывать массивы большой размерностью то эффективней будет быстрая сортировка но при оптимальной ее реализации, которая исключает глубокую рекурсию. Сортировку пузырьком и «Глупую» сортировку целесообразно использовать только в упорядоченных или частично упорядоченных массивах, из-за их простого алгоритма и реализации. 14 4. Реализация структур данных типа дерево и типовые алгоритмы их обработки 4.1 Постановка задачи Реализовать методы создания сильноветвящихся деревьев реализующих информацию соответствующей предметной области, а также методы поиска, удаления и добавления узлов в них 4.2 Краткие теоретические сведения Если узлы дерева имеют более двух потомков, то такие деревья принято называть сильноветвящимися деревьями. Одна из важных областей применения сильноветвящихся деревьев - это формирование и использование крупномасштабных деревьев поиска. Если множество данных, состоящее, например, из миллиона элементов, хранится в виде бинарного дерева, то для поиска элемента потребуется в среднем около log2106 т.е. приблизительно 20 шагов поиска. Поскольку каждый шаг поиска включает обращение к элементам структуры, для достижения минимального количества обращений сильноветвящееся дерево является идеальным решением. Добавление элемента в Б-дерево. 1. Элементы вставляются в страницу содержащую m<2n элементов. Тогда процесс включения ограничивается данной страницей. 2. Включение элемента в заполненную таблицу: a. Поиск по дереву элемента с данным ключом, и если элемента нет, то определение страницы, где элемент должен быть. b. Если на этой странице количество элементов m<2n, то включение производится только на данной странице. c. Иначе размещается новая страница, количество ключей m+1(ключ, который вставляется) поровну распределяется на две страницы, а средний ключ перемещается на уровень вверх d. Все выполняется , начиная с пункта b но уже со страницей верхнего уровня. Удаление элементов из Б-дерева. Возможны два случая: 1. Удаляемый элемент находиться на странице-листе. Тогда алгоритм прост и очевиден. 2. Удаляемый элемент находиться не на странице-листе. Алгоритм 1. Производим поиск смежного ключа(самый правый левого поддерева или самый левый правого поддерева ). Допустим, находим его на странице Р. 2. Заменяем удаляемый элемент на смежный , а Р уменьшаем на единицу. 3. Проверяем число элементов на уменьшенной странице Р. Если m<n, то нарушено свойство Б-деревьев и нужно совершить дополнительные действия: 15 Балансировку: вызывается соседняя страница Q и элементы страниц P и Q поровну распределяются на обе страницы. Слияние: используется, если страница Q достигла своего минимального значения. Процесс слияния полностью обратен процессу расщипления. Поиск в Б-дереве. Если ключ содержится в корне, он найден. Иначе определяем интервал и идём к соответствующему сыну. Повторяем, пока не дошли до листа. Схематичная структура узлов. 4.3 Структура дерева При создании дерева была использована предметная область служба занятости Для этого было реализовано Б-дерево, которое хранит список всех безработных, критерием сортировке в дереве был выбран алфавитный порядок по фамилии. 16 4.4 Анализ результатов Для изучения эффективности такого типа данных как дерево, были проведены эксперименты в которых исследовалось средняя скорость поиска и удаления в АТД дерево и список при различном количестве исходных данных: 25000000 Время, нс 20000000 15000000 10000000 5000000 1000 950 900 850 800 750 700 650 600 550 500 450 400 350 300 250 200 150 100 50 0 Количество объектов, т. шт. Как видно из графика скорость выполнения операции поиска и удаления в дереве практически не зависит от количества объектов, а в большей степени зависит от высоты дерева. Напротив скорость удаления и поиска в списке линейно зависит от количества объектов. Пример работы программы: Size:8 Добавление фамилии: Test Size:9 Удаление фамилии: Sidorov: Size:8 Поиск фамилии: Safronov 17 Вывод по разделу Тип данных дерево позволяет сократить количество обращений к объектам, это дает большое преимущество при хранении объектов на носителях информации с большим временем отклика. Так же операции с деревом дают лучшие временные показатели по сравнению с операциями списков. 18 5. Реализация функций расстановки (хеширование) и разрешение коллизий методом построения цепочки. 5.1 Постановка задачи Хеш-таблица представляет базу данных предметной области, соответствующей варианту. Реализовать: Выбор ключа для соответствующей базы данных По крайней мере, 3 различных функции хеширования для конкретных данных Заполнение таблицы для каждой функции Добавление новых данных для каждой функции Удаление данных Поиск данных по ключу Оценку качества хеширования для каждой функции Сравнение функций хеширования Двойное хеширование 5.2 Краткие теоретические сведения Хеширование – расширенный вариант поиска с использованием индексации по ключу, применяемый в более типовых приложениях поиска, в которых не приходиться рассчитывать на наличие ключей со столь удобными свойствами. Алгоритм поиска , которые используют хеширование, состоят из двух отдельных частей. Первый шаг – вычисление хеш-функции, которая преобразует ключи поиска в адрес в таблице. В идеале различные ключи должны были бы отображаться на различных адресах, но часто два и более различных ключа могут преобразоваться в один и тот же адрес в таблице. Поэтому вторая часть поиска методом хеширования – процесс разрешения коллизий, которые обрабатывают такие ключи. Существует много различных методов разрешения коллизий, например, линейное опробование (поиск следующего незанятого в таблице). Недостатком линейного опробования является возможность переполнения таблицы. Однако если строить цепочку (односвязный список) на каждый адрес коллизии, то переполнения таблицы можно избежать. Другим достоинством цепочки является то, что другие элементы не скапливаются вблизи первичного и не увеличивают, таким образом, вероятности возникновения других коллизий в этом районе. 19 Среди недостатков необходимо отметить относительную сложность в реализации (введение указателей и присущее им падение скорости) по сравнению с линейным опробованием. 5.3 Описание хеш-функции Хеш-таблица содержит список объектов «Книга» которые имеют текстовое поле Имя и числовое поле Количество страниц. Хеш-Функция №1 – Использует комплексный алгоритм хеширования включающий текстовое и числовое поле объекта. Хеш-Функция №2 – Использует только текстовое поле объекта, и более простые алгоритмы. Хеш-Функция №3 – Использует только числовое поле объекта, не применяя функций. 20 5.4 Анализ результатов Для трех разных хеш-функций были проведены исследования, в которых были получены зависимости количества коллизий от количества элементов(N)(считались все коллизии, включая те которые возникли при дальнейшем хешировании (двойное хеширование), для решения первой коллизии ) в хеш-таблице, при определенном размере хештаблице (100 000), полученные данные приведены ниже: N Количество коллизий, миллионов шт. 10000 20000 30000 40000 50000 60000 70000 80000 90000 100000 Хеш-функция 1 213730 1461716 3799095 7525307 12761750 22067937 39533473 76047918 162639909 415248303 Хеш-функция 2 3629714 38812821 135950276 283519526 430977922 681551206 984392742 1338561997 1666211950 2527274840 Хеш-функция 3 6845698 78163926 248101457 519513746 889194094 1361034476 1929252012 2600076076 3369783990 4239301377 4500 4000 3500 3000 2500 Hash1 2000 hash2 1500 Hash3 1000 500 0 10000 20000 30000 40000 50000 60000 70000 80000 90000 100000 Количество элементов, шт Как видно из графиков количество коллизий возрастает экспоненциально. Лучшие результаты показывает первая хеш-функция из сложного алгоритма и большого 21 диапазоны значений. Худшие результаты показывает хеш-функция №3 так как она имеет диапазон значений много меньше чем количество элементов. Так же для первой функции, для получения хороших результатов, следует не допускать заполнения таблицы более 50%. Зависимость количества коллизий от размера хеш-таблицы при фиксированном количестве добавляемых объектов в хеш-таблицу (10000 объектов). Размер 11000 21000 31000 41000 51000 61000 71000 81000 91000 101000 Хеш-Функция 1 984852 249446 239175 224635 198098 194154 229861 190343 174189 195330 Хеш-Функция 2 7778899 2751223 2684380 2983706 2754744 2281584 2253284 2595359 2635975 2433977 Хеш-Функция 3 6726262 6691327 6830156 6664464 6849117 6684149 6921839 6898305 6625759 6790354 9000000 Количество колизий 8000000 7000000 6000000 5000000 Hash1 4000000 Hash2 3000000 Hash3 2000000 1000000 0 11000 21000 31000 41000 51000 61000 71000 81000 91000 101000 Размер хеш-таблицы При наполненности хеш-таблицы более 90% количество коллизий резко возрастает . Hash3 имеет стабильно большое количество коллизий из того , что у него очень маленький диапазон значений хеш-функции, порядка 1500 значений и функция двойного хеширование имеет небольшие значения. Наиболее оптимальной хешфункцией является Hash1, так как ее алгоритмы наиболее запутаны, и дают наиболее 22 рассеянные и в большем диапазоне результаты. При наполненности менее 40% все хешфункции показывают стабильные результаты вследствие ограниченного диапазона значений и не совершенства алгоритмов хеширования. Пример работы программы: Hash1 collisions(10000):195931 Hash2 collisions(10000):2668034 Hash3 collisions(10000):6758486 Hash1 collisions(20000):1372783 Hash2 collisions(20000):73758493 Hash3 collisions(20000):77969000 Hash1 collisions(30000):3857532 Hash2 collisions(30000):247281076 Hash3 collisions(30000):247580921 Hash1 collisions(40000):7457602 Hash2 collisions(40000):522236138 Hash3 collisions(40000):518361767 Hash1 collisions(50000):12821905 Hash2 collisions(50000):896274527 Hash3 collisions(50000):888439511 Hash1 collisions(60000):20967275 Hash2 collisions(60000):1372106088 Hash3 collisions(60000):1359837921 Hash1 collisions(70000):36867706 Hash2 collisions(70000):1945767204 Hash3 collisions(70000):1929221346 Hash1 collisions(80000):73648426 Hash2 collisions(80000):2617168891 Hash3 collisions(80000):2597547521 Hash1 collisions(90000):172512680 Hash2 collisions(90000):3396294430 Hash3 collisions(90000):3371072896 Hash1 collisions(100000):427674036 Hash2 collisions(100000):4268782428 Hash3 collisions(100000):4240361188 Вывод по разделу Для создания наиболее эффективной хеш-таблицы не достаточно использовать хорошую хеш-функцию надо чтобы функция подходила конкретным данным с точки зрения диапазона и рассеивания так же что бы процент наполненности таблицы не превышал определенного значения. Двойное хеширование позволяет избежать скопление значений в одной области хеш-таблицы, при большой наполненности таблицы эффективность этого метода значительно снижается. 23 Список литературы Альфред Ахо, Джон Э. Хопкрофт, Д. Ульман Структуры данных и алгоритмы. М., Изд-во "Вильямс", 2000 г. Н. Вирт Алгоритмы и структуры данных. М., Издат-во "Вильямс", 1998г. Д. Кнут. Искусство программирования для ЭВМ. Т.1. Основные алгоритмы. М., Издво "Вильямс", 2000г. Уильям Топп, Уильям Форд. Структуры данных в С++. М., Изд-во "Бином", 2000 г. Роберт Седвидж. Фундаментальные алгоритмы на С++ Д. Кнут. Искусство программирования для ЭВМ. Т.1. Основные алгоритмы. М., "Мир", 1976 г., переиздание - М.,Изд-во "Вильямс", 2000г. Д. Кнут. Искусство программирования для ЭВМ. Т.3. Сортировка и поиск. М., "Мир", 1978 г., переиздание - М.,Изд-во "Вильямс", 2000 г. Н. Вирт Алгоритмы и структуры данных. М., Издат-во "Вильямс", 1998г. Хэширование-Википедия.-10.12.2010.-Режим доступа: http://ru.wikipedia.org/wiki/Хэширование Методы сортировки массивов и поисков в них параметрических элементов26.11.2010.- Режим доступа: http://serzik.ru/?id=31 24 Приложение 1 Исходный код программы по реализации АТД структур PACKAGE PART_1; IMPORT JAVA.UTIL.RANDOM; PUBLIC CLASS MAIN { PUBLIC STATIC VOID MAIN(STRING[] ARGS) { STACK S = NEW STACK(); QUEUE Q1 = NEW QUEUE(); QUEUE Q2 = NEW QUEUE(); QUEUE Q3 = NEW QUEUE(); RANDOM RND = NEW RANDOM(); FOR (INT I = 0; I < 50; I++) { INT R = RND.NEXTINT(3) + 1; INT TI = RND.NEXTINT(5) + 2; TASK T = NEW TASK("" + I, R, TI); IF (R == 1) { Q1.ENQUEUE(T); } IF (R == 2) { Q2.ENQUEUE(T); } IF (R == 3) { Q3.ENQUEUE(T); } } PROCESSOR P1 = NEW PROCESSOR("P1"); PROCESSOR P2 = NEW PROCESSOR("P2"); INT STEP = 1; WHILE (Q1.SIZE() > 0 || Q2.SIZE() > 0 || Q3.SIZE() > 0 || P1.ISWORK() || P2.ISWORK() || S.TAIL 25 != NULL) { SYSTEM.OUT.PRINTLN("STEP:" + STEP); TASK T; IF (Q1.SIZE() > 0 && !P1.ISWORK()) { T = Q1.DEQUEUE(); P1.ADDTASK(T); } IF (Q1.SIZE() > 0 && P1.ISWORK() && P1.GETTASK().PRIORITY == 3) { S.PUSH(P1.GETTASK()); P1.ADDTASK(Q1.DEQUEUE()); } IF (Q2.SIZE() > 0 && P2.ISWORK() && P2.GETTASK().PRIORITY == 3) { S.PUSH(P2.GETTASK()); P2.ADDTASK(Q2.DEQUEUE()); } IF (((Q1.ISEMPTY() && !P1.ISWORK()) || (Q2.ISEMPTY() && !P2.ISWORK())) && !S.ISEMPTY()) { IF (Q1.ISEMPTY()) { P1.ADDTASK(S.POP()); } ELSE { P2.ADDTASK(S.POP()); } } IF (Q2.SIZE() > 0 && !P2.ISWORK()) { T = Q2.DEQUEUE(); P2.ADDTASK(T); } IF (((Q1.ISEMPTY() && !P1.ISWORK()) || (Q2.ISEMPTY() && !P2.ISWORK())) && !Q3.ISEMPTY()) { IF (Q1.ISEMPTY()) { P1.ADDTASK(Q3.DEQUEUE()); 26 } ELSE { P2.ADDTASK(Q3.DEQUEUE()); } } P1.NEXTSTEP(); P2.NEXTSTEP(); SYSTEM.OUT.PRINTLN(P1.GETINFO()); SYSTEM.OUT.PRINTLN(P2.GETINFO()); Q1.SHOW(); Q2.SHOW(); Q3.SHOW(); S.SHOW(); IF (STEP == 30 || STEP == 150) { FOR (INT I = 0; I < 30; I++) { INT R = RND.NEXTINT(3) + 1; INT TI = RND.NEXTINT(5) + 2; T = NEW TASK("" + I, R, TI); IF (R == 1) { Q1.ENQUEUE(T); } IF (R == 2) { Q2.ENQUEUE(T); } IF (R == 3) { Q3.ENQUEUE(T); } } } STEP++; } 27 } } PACKAGE PART_1; PUBLIC CLASS PROCESSOR { PRIVATE BOOLEAN INWORK; PRIVATE TASK TASK; PRIVATE INT TIME; PRIVATE STRING NAME; PUBLIC PROCESSOR(STRING NAME) { NAME = NAME; INWORK = FALSE; TASK = NULL; TIME = 0; } PUBLIC VOID ADDTASK(TASK TASK) { TASK = TASK; TIME = TASK.TIME; INWORK = TRUE; } PUBLIC STRING GETNAME() { RETURN NAME; } PUBLIC BOOLEAN ISWORK() { RETURN INWORK; } PUBLIC TASK GETTASK() { RETURN TASK; } PUBLIC INT TIMELEFT() { RETURN TIME; 28 } PUBLIC BOOLEAN NEXTSTEP() { IF (TASK != NULL) { TASK.TIME--; } IF (TIME-- == 0) { INWORK = FALSE; TASK = NULL; RETURN FALSE; } ELSE { RETURN TRUE; } } PUBLIC STRING GETINFO() { STRING S = "PROCESSOR \"" + NAME + "\": "; IF (INWORK) { S += "TYPE" + TASK.PRIORITY + "WORKS. " + "TIME LEFT: " + TIME + "."; } ELSE { S += "IDLE."; } RETURN S; } } PACKAGE PART_1; PUBLIC CLASS QUEUE { PRIVATE CLASS NODE { PUBLIC TASK VALUE; PUBLIC NODE NEXT; PUBLIC NODE() { } 29 PUBLIC NODE(TASK TASK, NODE NEXT) { THIS.VALUE = TASK; THIS.NEXT = NEXT; } } PRIVATE NODE TOPOFQUEUE; PUBLIC QUEUE() { TOPOFQUEUE = NULL; } PUBLIC BOOLEAN ISEMPTY() { RETURN TOPOFQUEUE == NULL; } PUBLIC VOID MAKEEMPTY() { TOPOFQUEUE = NULL; } PUBLIC VOID ENQUEUE(TASK X) { TOPOFQUEUE = NEW NODE(X, TOPOFQUEUE); } PUBLIC TASK DEQUEUE() { IF (ISEMPTY()) { THROW NEW UNDERFLOWEXCEPTION("LISTSTACK EMPTY"); } IF (SIZE() == 1) { TASK E = TOPOFQUEUE.VALUE; TOPOFQUEUE = NULL; RETURN E; } NODE T = TOPOFQUEUE; NODE T1 = NULL; FOR (INT I = 0; I < SIZE() - 1; I++) { T1 = T; 30 T = T.NEXT; } T1.NEXT = NULL; RETURN T.VALUE; } PUBLIC VOID SHOW() { NODE LISTNODE = TOPOFQUEUE; SYSTEM.OUT.PRINT("LIST:"); INT I = 0; IF (!ISEMPTY()) { WHILE (I < SIZE()) { I++; SYSTEM.OUT.PRINT("(" + LISTNODE.VALUE.NAME + "," + LISTNODE.VALUE.PRIORITY + "," + LISTNODE.VALUE.TIME + ")"); LISTNODE = LISTNODE.NEXT; } } SYSTEM.OUT.PRINTLN(); } PUBLIC INT SIZE() { IF (ISEMPTY()) { RETURN 0; } ELSE { INT I = 1; NODE LISTNODE = TOPOFQUEUE; WHILE (LISTNODE.NEXT != NULL) { LISTNODE = LISTNODE.NEXT; I++; } RETURN I; } }} PACKAGE PART_1; PUBLIC CLASS STACK { 31 PRIVATE CLASS NODE { PUBLIC TASK VALUE; PUBLIC NODE PREV; PUBLIC NODE() { } PUBLIC NODE(TASK TASK, NODE PREV) { THIS.VALUE = TASK; THIS.PREV = PREV; } } NODE TAIL; PUBLIC STACK() { } PUBLIC VOID PUSH(TASK TASK) { TAIL = NEW NODE(TASK, TAIL); } PUBLIC TASK POP() { IF (TAIL != NULL) { NODE TEMP = TAIL; TAIL = TAIL.PREV; RETURN TEMP.VALUE; } ELSE { RETURN NULL; } } PUBLIC TASK TOP() { RETURN TAIL.VALUE; } PUBLIC VOID CLEAN() { TAIL = NULL; } PUBLIC BOOLEAN ISEMPTY() { RETURN TAIL == NULL; } 32 PUBLIC VOID SHOW() { NODE T; SYSTEM.OUT.PRINT("STACK:"); IF (!ISEMPTY()) { T = TAIL; WHILE (T.PREV != NULL) { SYSTEM.OUT.PRINT("(" + T.VALUE.NAME + "," + T.VALUE.PRIORITY + "," + T.VALUE.TIME + ")"); T = T.PREV; + ")"); } SYSTEM.OUT.PRINT("(" + T.VALUE.NAME + "," + T.VALUE.PRIORITY + "," + T.VALUE.TIME } SYSTEM.OUT.PRINTLN(); }} PACKAGE PART_1; PUBLIC CLASS TASK { PUBLIC INT TIME; STRING NAME; INT PRIORITY; PUBLIC TASK() { NAME = ""; PRIORITY = 0; TIME = 0; } PUBLIC TASK(STRING NAME, INT PRIORITY, INT TIME) { THIS.NAME = NAME; THIS.PRIORITY = PRIORITY; THIS.TIME = TIME; } PUBLIC STRING TOSTRING() { RETURN NAME + "(" + PRIORITY + "," + TIME + ")"; } PUBLIC INT GETPRIORITY() { RETURN PRIORITY; }} 33 Приложение 2 Исходный код программы по реализации поиска PACKAGE PART_2; IMPORT JAVA.IO.BUFFEREDREADER; IMPORT JAVA.IO.FILEREADER; IMPORT JAVA.IO.IOEXCEPTION; PUBLIC CLASS MAIN { PUBLIC STATIC VOID MAIN(STRING[] ARGS) THROWS IOEXCEPTION { STRING FILENAME = "E:\\TEXT.TXT";//ИМЯ ФАЙЛА STRING SEARCHSTRING = "ЛЕВ НИКОЛАЕВИЧ ТОЛСТОЙ";//ТО, ЧТО НУЖНО НАЙТИ BUFFEREDREADER FILEIN = NEW BUFFEREDREADER(NEW FILEREADER(FILENAME)); STRING LINE; INT LINECOUNTER = 0; LONG STARTTIME = SYSTEM.NANOTIME(); WHILE ((LINE = FILEIN.READLINE()) != NULL) { LINECOUNTER++; INT OFFSET = KMP.KMP(LINE.TOCHARARRAY(), SEARCHSTRING.TOCHARARRAY()); IF (OFFSET != -1) { SYSTEM.OUT.PRINTLN("СТРОКА " + LINECOUNTER); STARTTIME = SYSTEM.NANOTIME() - STARTTIME; SYSTEM.OUT.PRINTLN(STARTTIME); BREAK; } }}} PACKAGE PART_2; PUBLIC CLASS KMP { PUBLIC STATIC INT KMP(CHAR[] HAYSTACK, CHAR[] NEEDLE) { INT M = NEEDLE.LENGTH; INT N = HAYSTACK.LENGTH; INT[] PF = NEW INT[M + 1]; PF[0] = -1; 34 FOR (INT I = 1; I < M; I++) { PF[I] = PF[I - 1] + 1; WHILE (PF[I] > 0 && NEEDLE[I - 1] != NEEDLE[PF[I] - 1]) { PF[I] = PF[PF[I] - 1] + 1; }} FOR (INT I = 0, J = 0; I < N; I++) { WHILE (J > 0 && NEEDLE[J] != HAYSTACK[I]) { J = PF[J]; } IF (NEEDLE[J] == HAYSTACK[I]) { J++; } IF (J == M) { J = PF[J]; RETURN (I - M + 1); }} RETURN -1; }} 35 Приложение 3 Исходный код программы по реализации сортировки. PACKAGE PART_4; 36 Приложение 4 Исходный код программы по реализации сильноветвящегося дерева PACKAGE PART_4; IMPORT JAVA.UTIL.ARRAYLIST; CLASS FAMILY { PUBLIC STRING NAME; PUBLIC FAMILY(STRING S) { NAME = S; }} CLASS NODE { PUBLIC NODE PREV; PUBLIC ARRAYLIST NODE; PUBLIC ARRAYLIST FAMILY; PUBLIC NODE() { NODE = NEW ARRAYLIST(); FAMILY = NEW ARRAYLIST(); }} CLASS TREE { PUBLIC NODE ROOT = NULL; PUBLIC STATIC INT MIN = 2; PUBLIC STATIC INT MAX = MIN * 2; STATIC INT SIZE = 0; PUBLIC INT LVL = 1; PUBLIC FAMILY BK; PUBLIC TREE() { } PUBLIC INT SIZE() { SIZE = 0; FOR (INT I = 0; I < ROOT.FAMILY.SIZE(); I++) { SIZE++; 37 } FOR (INT I = 0; I < ROOT.NODE.SIZE(); I++) { S1(((NODE) ROOT.NODE.GET(I))); } RETURN SIZE; } PRIVATE VOID S1(NODE N) { FOR (INT I = 0; I < N.FAMILY.SIZE(); I++) { SIZE++; } FOR (INT I = 0; I < N.NODE.SIZE(); I++) { S1(((NODE) N.NODE.GET(I))); } } PRIVATE VOID S(NODE N) { SYSTEM.OUT.PRINTLN(N.FAMILY.SIZE()); FOR (INT I = 0; I < N.FAMILY.SIZE(); I++) { SYSTEM.OUT.PRINTLN(((FAMILY) N.FAMILY.GET(I)).NAME); } FOR (INT I = 0; I < N.NODE.SIZE(); I++) { SYSTEM.OUT.PRINTLN(N.NODE.SIZE()); S(((NODE) N.NODE.GET(I))); } } PUBLIC VOID SHOW() { SYSTEM.OUT.PRINTLN(ROOT.FAMILY.SIZE()); FOR (INT I = 0; I < ROOT.FAMILY.SIZE(); I++) { SYSTEM.OUT.PRINTLN(((FAMILY) ROOT.FAMILY.GET(I)).NAME); } FOR (INT I = 0; I < ROOT.NODE.SIZE(); I++) { S(((NODE) ROOT.NODE.GET(I))); } } 38 PRIVATE NODE SRCH(NODE N, FAMILY B) { IF (N.NODE.ISEMPTY()) { RETURN N; } ELSE { FOR (INT I = 0; I < N.FAMILY.SIZE(); I++) { IF (B.NAME.CHARAT(0) == ((FAMILY) N.FAMILY.GET(I)).NAME.CHARAT(0)) { RETURN N; } IF (B.NAME.CHARAT(0) < ((FAMILY) N.FAMILY.GET(I)).NAME.CHARAT(0)) { RETURN SRCH((NODE) N.NODE.GET(I), B); } } RETURN SRCH((NODE) N.NODE.GET(N.FAMILY.SIZE()), B); } } PUBLIC NODE SEARCH(FAMILY N) { IF (ROOT.NODE.ISEMPTY()) { RETURN ROOT; } ELSE { FOR (INT I = 0; I < ROOT.FAMILY.SIZE(); I++) { IF (N.NAME.CHARAT(0) == ((FAMILY) ROOT.FAMILY.GET(I)).NAME.CHARAT(0)) { RETURN ROOT; } IF (N.NAME.CHARAT(0) < ((FAMILY) ROOT.FAMILY.GET(I)).NAME.CHARAT(0)) { RETURN SRCH((NODE) ROOT.NODE.GET(I), N); } } WHILE (ROOT.NODE.SIZE() <= ROOT.FAMILY.SIZE()) { ROOT.FAMILY.REMOVE(ROOT.FAMILY.SIZE() - 1); } RETURN SRCH((NODE) ROOT.NODE.GET(ROOT.FAMILY.SIZE()), N); } } PRIVATE INT SET(NODE D, FAMILY B) { FOR (INT I = 0; I < D.FAMILY.SIZE(); I++) { 39 IF (B.NAME.CHARAT(0) < ((FAMILY) D.FAMILY.GET(I)).NAME.CHARAT(0)) { D.FAMILY.ADD(I, B); RETURN I; } } D.FAMILY.ADD(D.FAMILY.SIZE(), B); RETURN D.FAMILY.SIZE() - 1; } PRIVATE VOID OVERROOT(NODE D, FAMILY B) { NODE N1 = NEW NODE(); NODE N2 = NEW NODE(); SET(D, B); INT T = SET(N2, (FAMILY) (D.FAMILY.REMOVE(MIN))); N2.NODE.ADD(T, N1); FOR (INT I = 0; I < MIN; I++) { SET(N1, (FAMILY) (D.FAMILY.REMOVE(0))); } N2.NODE.ADD(T + 1, D); N1.PREV = N2; D.PREV = N2; ROOT = N2; } PUBLIC NODE OVER(NODE D, FAMILY B) { NODE N1 = NEW NODE(); SET(D, B); SET(D.PREV, (FAMILY) D.FAMILY.GET(MIN)); D = D.PREV; RETURN D; } PUBLIC VOID ADD(FAMILY B) { IF (ROOT == NULL) { 40 ROOT = NEW NODE(); ROOT.FAMILY.ADD(B); } ELSE { NODE D = SEARCH(B); IF (D.FAMILY.SIZE() < MAX) { SET(D, B); } ELSE { IF (D.PREV == NULL) { OVERROOT(D, B); } ELSE { WHILE (D.FAMILY.SIZE() == MAX) { IF (D.PREV == NULL) { OVERROOT(D, B); RETURN; } ELSE { NODE N1 = NEW NODE(); SET(D, B); INT T = SET(D.PREV, (FAMILY) D.FAMILY.REMOVE(MIN)); D.PREV.NODE.ADD(T, N1); FOR (INT I = MIN; I < MAX; I++) { SET(N1, (FAMILY) (D.FAMILY.REMOVE(MIN))); } N1.PREV = D.PREV; D = D.PREV; } } } } } } PUBLIC VOID DEL(STRING S) { FAMILY B = NEW FAMILY(S); NODE D = SEARCH(B); FOR (INT I = 0; I < D.FAMILY.SIZE(); I++) { IF (((FAMILY) D.FAMILY.GET(I)).NAME.EQUALS(B.NAME)) { 41 D.FAMILY.REMOVE(I); } } } PUBLIC VOID FIND(STRING S) { FAMILY B = NEW FAMILY(S); NODE D = SEARCH(B); FOR (INT I = 0; I < D.FAMILY.SIZE(); I++) { IF (((FAMILY) D.FAMILY.GET(I)).NAME.EQUALS(B.NAME)) { SYSTEM.OUT.PRINTLN("НАЙДЕНА ФАМИЛИЯ:" + ((FAMILY) D.FAMILY.GET(I)).NAME); } } }} PUBLIC CLASS MAIN { PUBLIC STATIC VOID MAIN(STRING[] ARGS) { TREE T = NEW TREE(); FAMILY B = NEW FAMILY("IVANOV"); FAMILY B1 = NEW FAMILY("PETROV"); FAMILY B2 = NEW FAMILY("SIDOROV"); FAMILY B3 = NEW FAMILY("STAVCHUK"); FAMILY B4 = NEW FAMILY("SAFRONOV"); FAMILY B5 = NEW FAMILY("CHISTYAKOV"); FAMILY B6 = NEW FAMILY("LAYCOM"); FAMILY B7 = NEW FAMILY("MIN"); FAMILY B8 = NEW FAMILY("TEST"); T.ADD(B); T.ADD(B1); T.ADD(B2); T.ADD(B3); T.ADD(B4); T.ADD(B5); T.ADD(B6); T.ADD(B7); SYSTEM.OUT.PRINTLN("SIZE:" + T.SIZE()); 42 SYSTEM.OUT.PRINTLN("ДОБАВЛЕНИЕ ФАМИЛИИ:" + B8.NAME); T.ADD(B8); SYSTEM.OUT.PRINTLN("SIZE:" + T.SIZE()); SYSTEM.OUT.PRINTLN("УДАЛЕНИЕ ФАМИЛИИ: SIDOROV:"); T.DEL("SIDOROV"); SYSTEM.OUT.PRINTLN("SIZE:" + T.SIZE()); SYSTEM.OUT.PRINTLN("ПОИСК ФАМИЛИИ: SAFRONOV"); T.FIND("SAFRONOV"); }} Приложение 5 Исходный код программы по реализации хеширования и разрешения коллизий (Двойное хеширование). PACKAGE PART_5; IMPORT JAVA.UTIL.RANDOM; CLASS BOOK { PUBLIC STRING NAME = ""; PUBLIC INT PAGE = 0; PUBLIC BOOK() { } PUBLIC BOOK(STRING N, INT PG) { NAME = N; PAGE = PG; }} CLASS HASHTABLE { PUBLIC LONG COL = 0; PUBLIC INT HASHSIZE; PUBLIC BOOK HT[]; PUBLIC HASHTABLE() { } PUBLIC HASHTABLE(INT SIZE) { HT = NEW BOOK[SIZE]; HASHSIZE = SIZE; } PUBLIC INT HF(BOOK KEY) { INT H = 0; FOR (INT I = 0; I < KEY.NAME.LENGTH(); I++) { INT Y = KEY.NAME.CHARAT(I); Y *= I * Y; H += Y + KEY.PAGE * I; } RETURN H % HASHSIZE; } PUBLIC INT HF_2(BOOK KEY) { INT H = 0; FOR (INT I = 0; I < KEY.NAME.LENGTH(); I++) { INT Y = KEY.NAME.CHARAT(I); 43 H += Y; } RETURN H % HASHSIZE; } PUBLIC INT HF_3(BOOK KEY) { INT H = 0; H = KEY.PAGE; RETURN H % HASHSIZE; } PUBLIC INT HF2(BOOK KEY, INT INDEX) { INT H = INDEX; FOR (INT I = 0; I < KEY.NAME.LENGTH(); I++) { H += KEY.NAME.CHARAT(I); } RETURN H % HASHSIZE; } PUBLIC VOID SHOWTABLE() { FOR (INT I = 0; I < HT.LENGTH; I++) { STRING N = ""; INT P = 0; IF (HT[I] != NULL) { N = HT[I].NAME; P = HT[I].PAGE; } SYSTEM.OUT.PRINT("IN:" + I); SYSTEM.OUT.PRINT(" NAME:" + N); SYSTEM.OUT.PRINTLN(" , PAGE:" + P); } } PUBLIC VOID ADD(BOOK B) { IF (HT[HF(B)] == NULL) { HT[HF(B)] = B; } ELSE { INT H = HF(B); INT SW = 0; WHILE (TRUE) { COL++; IF (HT[HF2(B, H)] == NULL) { HT[HF2(B, H)] = B; BREAK; } ELSE { IF (SW > 10) { H++; H %= HASHSIZE; IF (SW > HASHSIZE) { SYSTEM.OUT.PRINTLN("OOPS"); } } ELSE { H = HF2(B, H); } } SW++; } } } PUBLIC VOID DEL(STRING S, INT P) { BOOK B = NEW BOOK(S, P); IF (HT[HF(B)] != NULL && B.NAME.EQUALS(HT[HF(B)].NAME) && B.PAGE == HT[HF(B)].PAGE) { 44 HT[HF(B)] = NULL; } ELSE { INT H = HF(B); INT SW = 0; WHILE (TRUE) { IF (HT[HF2(B, H)] != NULL && B.NAME.EQUALS(HT[HF2(B, H)].NAME) && B.PAGE == HT[HF2(B, H)].PAGE) { HT[HF2(B, H)] = NULL; BREAK; } ELSE { IF (SW > 10) { H++; H %= HASHSIZE; IF (SW > HASHSIZE) { SYSTEM.OUT.PRINTLN("OOPS"); BREAK; } } ELSE { H = HF2(B, H); } } SW++; } } } PUBLIC VOID SEARCH(STRING S, INT P) { BOOK B = NEW BOOK(S, P); IF (HT[HF(B)] != NULL && B.NAME.EQUALS(HT[HF(B)].NAME) && B.PAGE == HT[HF(B)].PAGE) { SYSTEM.OUT.PRINTLN("BOOK: " + B.NAME + " (" + B.PAGE + ") IS FOUND AT INDEX " + HF(B)); } ELSE { INT H = HF(B); INT SW = 0; WHILE (TRUE) { IF (HT[HF2(B, H)] != NULL && B.NAME.EQUALS(HT[HF2(B, H)].NAME) && B.PAGE == HT[HF2(B, H)].PAGE) { SYSTEM.OUT.PRINTLN("BOOK: " + B.NAME + " (" + B.PAGE + ") IS FOUND AT INDEX " + HF2(B, H)); BREAK; } ELSE { IF (SW > 10) { H++; H %= HASHSIZE; IF (SW > HASHSIZE) { SYSTEM.OUT.PRINTLN("OOPS"); BREAK; } } ELSE { H = HF2(B, H); } } SW++; } } } PUBLIC VOID SEARCH_2(STRING S, INT P) { BOOK B = NEW BOOK(S, P); IF (HT[HF_2(B)] != NULL && B.NAME.EQUALS(HT[HF_2(B)].NAME) && B.PAGE == HT[HF_2(B)].PAGE) { SYSTEM.OUT.PRINTLN("BOOK: " + B.NAME + " (" + B.PAGE + ") IS FOUND AT INDEX " + HF_2(B)); 45 } ELSE { INT H = HF_2(B); INT SW = 0; WHILE (TRUE) { IF (HT[HF2(B, H)] != NULL && B.NAME.EQUALS(HT[HF2(B, H)].NAME) && B.PAGE == HT[HF2(B, H)].PAGE) { SYSTEM.OUT.PRINTLN("BOOK: " + B.NAME + " (" + B.PAGE + ") IS FOUND AT INDEX " + HF2(B, H)); BREAK; } ELSE { IF (SW > 10) { H++; H %= HASHSIZE; IF (SW > HASHSIZE) { SYSTEM.OUT.PRINTLN("OOPS"); BREAK; } } ELSE { H = HF2(B, H); } } SW++; } } } PUBLIC VOID SEARCH_3(STRING S, INT P) { BOOK B = NEW BOOK(S, P); IF (HT[HF_3(B)] != NULL && B.NAME.EQUALS(HT[HF_3(B)].NAME) && B.PAGE == HT[HF_3(B)].PAGE) { SYSTEM.OUT.PRINTLN("BOOK: " + B.NAME + " (" + B.PAGE + ") IS FOUND AT INDEX " + HF_3(B)); } ELSE { INT H = HF_3(B); INT SW = 0; WHILE (TRUE) { IF (HT[HF2(B, H)] != NULL && B.NAME.EQUALS(HT[HF2(B, H)].NAME) && B.PAGE == HT[HF2(B, H)].PAGE) { SYSTEM.OUT.PRINTLN("BOOK: " + B.NAME + " (" + B.PAGE + ") IS FOUND AT INDEX " + HF2(B, H)); BREAK; } ELSE { IF (SW > 10) { H++; H %= HASHSIZE; IF (SW > HASHSIZE) { SYSTEM.OUT.PRINTLN("OOPS"); BREAK; } } ELSE { H = HF2(B, H); } } SW++; } } } PUBLIC VOID DEL_2(STRING S, INT P) { BOOK B = NEW BOOK(S, P); IF (HT[HF_2(B)] != NULL && B.NAME.EQUALS(HT[HF_2(B)].NAME) && B.PAGE == HT[HF_2(B)].PAGE) { HT[HF_2(B)] = NULL; } ELSE { INT H = HF_2(B); INT SW = 0; 46 WHILE (TRUE) { IF (HT[HF2(B, H)] != NULL && B.NAME.EQUALS(HT[HF2(B, H)].NAME) && B.PAGE == HT[HF2(B, H)].PAGE) { HT[HF2(B, H)] = NULL; BREAK; } ELSE { IF (SW > 10) { H++; H %= HASHSIZE; IF (SW > HASHSIZE) { SYSTEM.OUT.PRINTLN("OOPS"); BREAK; } } ELSE { H = HF2(B, H); } } SW++; } } } PUBLIC VOID DEL_3(STRING S, INT P) { BOOK B = NEW BOOK(S, P); IF (HT[HF_3(B)] != NULL && B.NAME.EQUALS(HT[HF_3(B)].NAME) && B.PAGE == HT[HF_3(B)].PAGE) { HT[HF_3(B)] = NULL; } ELSE { INT H = HF_3(B); INT SW = 0; WHILE (TRUE) { IF (HT[HF2(B, H)] != NULL && B.NAME.EQUALS(HT[HF2(B, H)].NAME) && B.PAGE == HT[HF2(B, H)].PAGE) { HT[HF2(B, H)] = NULL; BREAK; } ELSE { IF (SW > 10) { H++; H %= HASHSIZE; IF (SW > HASHSIZE) { SYSTEM.OUT.PRINTLN("OOPS"); BREAK; } } ELSE { H = HF2(B, H); } } SW++; } } } PUBLIC VOID ADD_2(BOOK B) { IF (HT[HF_2(B)] == NULL) { HT[HF_2(B)] = B; } ELSE { INT H = HF_2(B); INT SW = 0; WHILE (TRUE) { COL++; IF (HT[HF2(B, H)] == NULL) { HT[HF2(B, H)] = B; BREAK; } ELSE { IF (SW > 10) { 47 H++; H %= HASHSIZE; IF (SW > HASHSIZE) { SYSTEM.OUT.PRINTLN("OOPS"); } } ELSE { H = HF2(B, H); } } SW++; } } } PUBLIC VOID ADD_3(BOOK B) { IF (HT[HF_3(B)] == NULL) { HT[HF_3(B)] = B; } ELSE { INT H = HF_3(B); INT SW = 0; WHILE (TRUE) { COL++; IF (HT[HF2(B, H)] == NULL) { HT[HF2(B, H)] = B; BREAK; } ELSE { IF (SW > 10) { H++; H %= HASHSIZE; } ELSE { H = HF2(B, H); } } SW++; } } }} PUBLIC CLASS MAIN { PUBLIC STATIC VOID MAIN(STRING[] ARGS) { RANDOM RND = NEW RANDOM(); INT SIZE = 0; WHILE (SIZE < 95000) { HASHTABLE TABLE = NEW HASHTABLE(100000); HASHTABLE TABLE1 = NEW HASHTABLE(100000); HASHTABLE TABLE2 = NEW HASHTABLE(100000); SIZE += 10000; FOR (INT I = 0; I < SIZE; I++) { INT P = RND.NEXTINT(1500) + 1; INT G = RND.NEXTINT(10) + 1; STRING S = ""; FOR (INT J = 0; J < G; J++) { CHAR Q = (CHAR) (RND.NEXTINT(26) + 97); S += Q; } BOOK B = NEW BOOK(S, P); TABLE.ADD(B); TABLE1.ADD_2(B); TABLE2.ADD_3(B); } SYSTEM.OUT.PRINTLN("HASH1 COLLISIONS(" + SIZE + "):" + TABLE.COL); SYSTEM.OUT.PRINTLN("HASH2 COLLISIONS(" + SIZE + "):" + TABLE1.COL); SYSTEM.OUT.PRINTLN("HASH3 COLLISIONS(" + SIZE + "):" + TABLE2.COL); } }} 48