
- •Содержание
- •1. Основные понятия и определения
- •1.1. Структура персональной ЭВМ
- •1.2. Размещение данных и программ в памяти ПЭВМ
- •1.3. Программные модули
- •1.4. Ошибки
- •1.5. Функциональная и модульная декомпозиции
- •1.6. Файловая система хранения информации
- •1.7. Операционная система
- •2. Понятие алгоритмов и способы их описания
- •2.1. Свойства алгоритмов
- •2.2. Способы описания алгоритмов
- •3. Базовые элементы языка Cи
- •3.1. Алфавит языка
- •3.2. Лексемы
- •3.3. Идентификаторы и ключевые слова
- •3.4. Знаки операций
- •3.5. Литералы (константы)
- •3.6. Комментарии
- •4. Базовые типы объектов
- •4.1. Простейшая программа
- •4.2. Основные типы данных
- •4.3. Декларация объектов
- •4.4. Данные целого типа (int)
- •4.5. Данные символьного типа (char)
- •4.6. Данные вещественного типа (float, double)
- •5. Константы в программах
- •5.1. Целочисленные константы
- •5.2. Константы вещественного типа
- •5.3. Символьные константы
- •5.4. Строковые константы
- •6. Обзор операций
- •6.1. Операции, выражения
- •6.2. Арифметические операции
- •6.3. Операции присваивания
- •6.4. Сокращенная запись операции присваивания
- •6.5. Преобразование типов операндов арифметических операций
- •6.6. Операция приведения типа
- •6.7. Операции сравнения
- •6.8. Логические операции
- •6.9. Побитовые логические операции, операции над битами
- •6.10. Операция «,» (запятая)
- •7. Обзор базовых инструкций языка Си
- •7.1. Стандартная библиотека языка Си
- •7.2. Стандартные математические функции
- •7.3. Функции вывода данных
- •7.4. Функции ввода информации
- •7.5. Ввод - вывод потоками
- •8. Синтаксис операторов языка Cи
- •8.1. Условные операторы
- •8.2. Условная операция «? :»
- •8.3. Оператор выбора альтернатив (переключатель)
- •9. Составление циклических алгоритмов
- •9.1. Понятие цикла
- •9.2. Оператор с предусловием while
- •9.3. Оператор цикла с постусловием do – while
- •9.4. Оператор цикла с предусловием и коррекцией for
- •10. Операторы передачи управления
- •10.1. Оператор безусловного перехода goto
- •10.2. Оператор continue
- •10.3. Оператор break
- •10.4. Оператор return
- •11.1. Операции над указателями (косвенная адресация)
- •11.2. Ссылка
- •12. Массивы
- •12.1. Одномерные массивы
- •12.2. Многомерные массивы
- •12.3. Операция sizeof
- •12.4. Применение указателей
- •12.5. Указатели на указатели
- •13. Работа с динамической памятью
- •13.1. Пример создания одномерного динамического массива
- •13.2. Пример создания двухмерного динамического массива
- •14. Строки в языке Си
- •14.1. Русификация под Visual
- •15. Функции пользователя
- •15.1. Декларация функции
- •15.2. Вызов функции
- •15.3. Операция typedef
- •15.4. Указатели на функции
- •16. Классы памяти и области действия объектов
- •16.1. Автоматические переменные
- •16.2. Внешние переменные
- •16.3. Область действия переменных
- •17. Структуры, объединения, перечисления
- •17.1. Структуры
- •17.2. Декларация структурного типа данных
- •17.3. Создание структурных переменных
- •17.4. Вложенные структуры
- •17.5. Массивы структур
- •17.6. Размещение структурных переменных в памяти
- •17.7. Объединения
- •17.8. Перечисления
- •18. Файлы в языке Си
- •18.1. Открытие файла
- •18.2. Закрытие файла
- •18.3. Запись – чтение информации
- •18.4. Текстовые файлы
- •18.5. Бинарные файлы
- •Литература
- •Приложение 1. Таблицы символов ASCII
- •Приложение 2. Операции языка Си
- •Приложение 3. Возможности препроцессора
- •1. Вид рабочего стола консольного приложения Visual C++
- •2. Создание нового проекта
- •3. Добавление к проекту файлов с исходным кодом
- •4. Компиляция, компоновка и выполнение проекта
- •5. Конфигурация проекта
- •6. Открытие существующего проекта
- •Приложение 5. Некоторые возможности отладчика Visual C++
- •1. Установка точки прерывания
- •2. Пошаговое выполнение программы
- •3. Проверка значений переменных во время выполнения программы
- •4. Окна Auto и Watch 1
- •5. Программные средства отладки
- •ЧАСТЬ 2
- •1. Системы счисления
- •1.1. Общие определения
- •1.2. Алгоритмы перевода из одной системы счисления в другую
- •2. Организация памяти и структуры данных
- •3. Линейные списки и рекурсия
- •4. Динамическая структура данных – СТЕК
- •5. Динамическая структура данных – ОЧЕРЕДЬ
- •6. Двунаправленный линейный список
- •7. Построение обратной польской записи
- •8. Нелинейные структуры данных
- •Пояснение к программе
- •9. Понятие хеширования
- •9.1. Хеш-функция и хеш-таблица
- •9.2. Схемы хеширования
- •10. Элементы теории погрешностей
- •10.1. Методы реализации математических моделей
- •10.2. Источники погрешностей
- •10.3. Приближенные числа и оценка их погрешностей
- •10.4. Прямая задача теории погрешностей
- •10.5. Обратная задача теории погрешностей
- •10.6. Понятия устойчивости, корректности и сходимости
- •11. Вычисление интегралов
- •11.1. Формулы численного интегрирования
- •11.2. Формула средних
- •11.3. Формула трапеций
- •11.4. Формула Симпсона
- •11.5. Схема с автоматическим выбором шага по заданной точности
- •12. Методы решения нелинейных уравнений
- •12.1. Итерационные методы уточнения корней
- •12.2. Метод Ньютона
- •12.3. Метод секущих
- •12.4. Метод Вегстейна
- •12.5. Метод парабол
- •12.6. Метод деления отрезка пополам
- •13. Решение систем линейных алгебраических уравнений
- •13.1. Прямые методы решения СЛАУ
- •13.2. Метод Гаусса
- •13.3. Метод прогонки
- •13.4. Метод квадратного корня
- •13.5. Итерационные методы решения СЛАУ
- •13.6. Метод простой итерации
- •14. Поиск и сортировка
- •14.1. Последовательный поиск в массиве
- •14.2. Барьерный последовательный поиск
- •14.3. Сортировка вставками (включениями)
- •14.4. Двоичный поиск
- •14.5. Сортировка выбором
- •14.6. Алгоритмы сортировки выбором
- •14.7. Сортировка обменом
- •14.8. Быстрая сортировка
- •15. Обработка статистических данных

|
t |
|
|
|
|
|
Root |
||
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
|||
Left |
|
|
|
|
|
|
Right |
||
|
0 |
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Prev t
25
0
20
0 0
и т.д.
Результат работы программы для ключей: 10, 25, 20, 6, 21, 8, 1, 30 (стрелками показана связь между выводимыми значениями):
|
|
|
|
30 |
|
|
|
25 |
|
|
|
|
|
|
|
|
|
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
|
|
|
|
|
|
|
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
|
|
|
|
|
|
|
|
|
|
6 |
|
|
|
|
|
|
|
|
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9. Понятие хеширования
До сих пор среди данных большого объема остается проблемой поиск необходимого элемента. Если эти данные расположены беспорядочно в массиве (линейном списке, файле и т.п.), то осуществляют линейный поиск, эффективность которого 0(n/2). Если же данные в массиве упорядочены (например, сбалансированное двоичное дерево), то возможен двоичный поиск с эффективностью
0(log2n).
Однако при работе с двоичным деревом и упорядоченным массивом затруднены операции вставки и удаления элементов, так, например, дерево – разбалансируется, и вновь требуется его балансировка. Что можно придумать в данном случае более эффективное?
В данном случае был предложен алгоритм хеширования (hashing – перемешивание), при котором создаются ключи, определяющие данные массива и на их основании данные записываются в таблицу, названную хеш-таблицей. Ключи
155

для записи определяются при помощи функции i = h(key), называемой хешфункцией. Алгоритм хеширования определяет положение искомого элемента в хеш-таблице по значению его ключа, полученного хеш-функцией.
Хеширование – это способ сведения хранения большого множества к более меньшему.
Возьмем, например, словарь или энциклопедию. В этом случае буквы алфавита могут быть приняты за ключи поиска, т.е. основным элементом алгоритма хеширования является ключ (key)! В большинстве приложений ключ обеспечивает косвенную ссылку на данные.
Термин «хеширование» в литературе по программированию появился не так давно – в 1967 году, ввел его Хеллерман (Hellerman). Дословный перевод означает – рубить, крошить, но академик А.П.Ершов предложил довольно удачный эквивалент – «расстановка».
Фактически хеширование – это специальный метод адресации данных для быстрого поиска нужной информации по ключам.
Или хеширование – это разбиение общего (базового) набора уникальных
ключей элементов данных на непересекающиеся наборы с определенным свойством.
Если базовый набор содержит N элементов, то его можно разбить на 2N различных подмножеств.
9.1. Хеш-функция и хеш-таблица
Функция, которая описывает определенное свойство подмножеств, т.е. преобразует ключ элемента данных в некоторый индекс в таблице (хеш-таблица),
называется функцией хеширования или хеш-функцией: i = h(key);
где key – преобразуемый ключ, i – получаемый индекс таблицы, т.е. ключ отображается во множестве, например, целых чисел (хеш-адреса), которые впоследствии используются для доступа к данным.
Однако функция расстановки может для нескольких значений ключа давать одинаковое значение позиции i в таблице. Ситуация, при которой два или более ключа получают один и тот же индекс (хеш-адрес) называется коллизией при хешировании.
Хорошей хеш-функцией считается такая функция, которая минимизирует коллизии и распределяет данные равномерно по всей таблице.
Совершенная хеш-функция – это функция, которая не порождает коллизий:
Хеш-таблица
0
i = h(key)
ключ |
|
i |
|
1 |
|
|
|||
|
||||
(key) |
|
. . . |
... |
m
156

Разрешить коллизии при хешировании можно двумя методами:
–методом открытой адресации с линейным опробыванием;
–методом цепочек.
Хеш-таблица
Хеш-таблица представляет собой обычный массив с необычной адресацией, задаваемой хеш-функцией.
Хеш-структуру считают обобщением массива, который обеспечивает быстрый прямой доступ к данным по индексу. С точки зрения хеширования, массив задает отображение А множества индексов I на множество элементов Е, т.е. А:I→Е и позволяет по индексу быстро найти нужный элемент.
Имеется множество схем хеширования, различающихся как выбором удачной функции h(key), так и алгоритма разрешения конфликтов. Эффективность решения реальной практической задачи будет существенно зависеть от выбираемой стратегии.
Примеры хеш-функций
Функция хеширования является важной частью процесса хеширования. Она используется для преобразования ключей в адреса таблицы. Она должна легко вычисляться и преобразовывать ключи (обычно целочисленные или строковые значения) в целые числа в интервале до m – максимальный размер формируемой таблицы.
Выбираемая функция должна создавать как можно меньше коллизий, т.е. должна равномерно распределять ключи на имеющиеся индексы в таблице. Конечно, нельзя определить, будет ли некоторая конкретная хеш-функция распределять ключи правильно, если эти ключи заранее не известны. Однако, хотя до выбора хеш-функции редко известны сами ключи, некоторые свойства этих ключей, которые влияют на их распределение, обычно известны. Рассмотрим наиболее распространенные методы задания хеш-функции.
Метод деления. Исходными данными являются – некоторый целый ключ key и размер таблицы m. Результатом данной функции является остаток от деления этого ключа на размер таблицы. Общий вид функции:
int h(int key, int m) {
return key % m; // Значения
}
Для m = 10 хеш-функция возвращает младшую цифру ключа.
Хеш-таблица (m=10)
Хеш-адреса i : |
0 |
1 |
2 |
|
3 |
4 |
5 |
6 |
7 |
8 |
9 |
|
||
|
|
|
342 |
|
|
|||||||||
|
|
key |
|
= |
key = 756 |
Коллизия |
||||||||
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
key = 55556 |
|
|
157

Для m = 100 хеш-функция возвращает две младших цифры ключа.
Хеш-таблица (m=100)
Хеш-адреса i : |
00 |
01 |
02 |
|
. . . |
|
97 |
98 |
99 |
|
|
|
|
|
|
key = 7597 |
|
|
Коллизия |
||||
|
|
|
|
|
|
|
|||||
|
|
key = 3402 |
|
|
|
||||||
|
|
|
|
|
|
|
|
key = 55597 |
Аддитивный метод, в котором ключом является символьная строка С++. В хеш-функции строка преобразуется в целое, суммированием всех символов и возвращается остаток от деления на m (обычно размер таблицы m=256).
int h(char *key, int m) { int s = 0; while(*key)
s += *key++; return s % m;
}
Коллизии возникают в строках, состоящих из одинакового набора символов, например, abc и cab.
Данный метод можно несколько модифицировать, получая результат, суммируя только первый и последний символы строки-ключа.
int h(char *key, int m) {
int len = strlen(key), s = 0;
if(len < 2) |
// Если длина ключа равна 0 или 1, |
s = key[0]; |
// возвратить key[0] |
else |
|
s = key[0] + key[len–1]; return s % m;
}
В этом случае коллизии будут возникать только в строках, например, abc и
amc.
Метод середины квадрата, в котором ключ возводится в квадрат (умножается сам на себя) и в качестве индекса используются несколько средних цифр полученного значения.
Например, ключом является целое 32-битное число, а хеш-функция возвращает средние 10 бит его квадрата:
int h(int key) { key *= key;
key >>= 11; // Отбрасываем 11 младших бит return key % 1024; // Возвращаем 10 младших бит
}
158