Опубликован: 02.02.2011 | Доступ: свободный | Студентов: 3378 / 987 | Оценка: 4.43 / 3.57 | Длительность: 33:06:00
Специальности: Программист
Лекция 47:

Решение задач на использование алгоритмов обработки данных

< Лекция 46 || Лекция 47: 1234 || Лекция 48 >
Аннотация: В лекции даются общие рекомендации по решению задач повышенной сложности, рассматривается примеры программной реализации задач обработки данных, которые решаются с помощью алгоритмов сортировок, обходов графа и сжатия данных.
Ключевые слова: эффективность алгоритма, массив, определение требований, универсальный алгоритм, упорядочивание данных, алгоритм, пирамидальной сортировки, сортировка, натуральное число, код программы, граф, матрица смежности, ребро, матрица, смежность, одномерный массив, множество вершин, список, запись, взвешенный граф, Неориентированный граф, систематический, поиск в глубину, поиск в ширину, кратчайший путь, алгоритм Дейкстры, вершины графа, поле, Пирамида, грани, входные данные, файл, output, вершина, путь, развертка, 3D, удобство использования, кодирование, двумерный массив, очередь, избыточность, потеря информации, RLE, run-length encoding, ASCII, энтропия, декодирование, очередной символ, бит, поток, CAT, hat, пробел, входной, конец файла, end, точность, представление, выходные данные, входная строка, программная реализация, анализ, рекурсивная функция, метод перебора, Дополнение

Цель лекции: изучить основные приемы разработки алгоритмов обработки данных, научиться применять их при решении задач и учитывать трудоемкость и эффективность используемых алгоритмов.

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

Приведем общую схему решения задач по программированию.

  1. Чтение условия. Необходимо внимательно прочесть условие задачи, не пропуская ни одной фразы.
  2. Построение математической модели. Необходимо понять, в чем заключается задача – построить ее математическую модель (на листке бумаги или образно), то есть достаточно формально и математически строго понять условие.
  3. Построение общей схемы решения. Теперь следует перейти от понимания того, что необходимо сделать, к пониманию того, как это сделать, то есть наметить эффективный алгоритм решения задачи и пути его реализации.
  4. Стыковка. Под стыковкой понимается уточнение решений, принятых на предыдущем этапе. Необходимо достаточно медленно и тщательно продумать, из каких частей будет состоять программа, какие массивы и структуры будут выделены и т.д.
  5. Реализация. На этом этапе собственно пишется сама программа. Иногда предпочтительнее программирование "сверху вниз", иногда – "снизу вверх" или их комбинация.
  6. Тестирование и отладка. Добившись того, чтобы программа компилировалась, необходимо убедиться в ее правильности. Проблемы могут быть в мелких ошибках, допущенных в процессе написания: перепутанные имена переменных, неверный знак в формуле и т.д. Решение может быть принципиально неправильным или неэффективным. Размер массивов может быть недостаточным или, напротив, чрезмерным, что будет вызывать ошибку "превышен предел памяти".

Алгоритмы сортировки данных

Данную тему следует рассматривать в двух аспектах. Во-первых, при решении различных задач повышенной сложности данные довольно часто требуется упорядочить по некоторому признаку (то есть отсортировать). При этом, если специально не оговорено иное, считается, что массив требуется отсортировать в порядке неубывания значений его элементов (для различных элементов – в порядке возрастания). Во-вторых, задача сама по себе может требовать построения оптимального в смысле определенных требований или нестандартного алгоритма сортировки. Помимо этого, специфика задач повышенной сложности может состоять в формализации критерия, по которому следует сортировать данные.

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

Название сортировки Количество сравнений Количество присваиваний
Простой обмен (пузырьковая) O(N2) O(N2)
Прямой выбор O(N2) O(N)
Простая вставка O(N2) O(N2)
Быстрая O(N2) (на практике O(N log N) ) O(N2) (на практике O(N log N) )
Слияниями O(N log N) O(N log N)
Пирамидальная O(N log N) O(N log N)

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

Пример 1. Задача "Поразрядная сортировка"

Поразрядная сортировка была изобретена в 1920-х годах как побочный результат использования сортирующих машин. Такая машина обрабатывала перфокарты, имевшие по 80 колонок. Каждая колонка представляла отдельный символ. В колонке было 12 позиций, и в них для представления того или иного символа пробивались отверстия. Цифру от 0 до 9 кодировали одним отверстием в соответствующей позиции (еще две позиции в колонке использовали для кодировки букв).

Запуская машину, оператор закладывал в ее приемное устройство стопку перфокарт и задавал номер колонки на перфокартах. Машина "просматривала" эту колонку на картах и по цифровому значению 0, 1, ..., 9 в ней распределяла ("сортировала") карты на 10 стопок.

Несколько колонок (разрядов) с закодированными цифрами представляли натуральное число, т.е. номер. Чтобы получить стопку карт, упорядоченных по номерам, оператор действовал так. Вначале он распределял карты на 10 стопок по значению младшем разряде. Эти стопки в порядке возрастания значений в младшем разряде он складывал в одну и повторял процесс, но со следующим разрядом, и т.д. Получив стопки карт, распределенных по значениям в старшем разряде, оператор складывал их по возрастанию этих значений и получал то, что нужно.

Значения в разрядах номеров заданы цифрами, поэтому поразрядную сортировку еще называют цифровой. Заметим, что цифры от 0 до 9 упорядочены по возрастанию, поэтому цифровая сортировка располагает числа в лексикографическом порядке.

Пример.

Входные данные Выходные данные
733 877 323 231 777 721 123 123 231 323 721 733 777 877

Описание решения.

Принцип решения разберем на конкретном примере. Пусть задана последовательность трехзначных номеров:

733   877   323   231   777   721   123

Распределим данную последовательность по младшей цифре на стопки:

231   721
733   323   123
877   777

Далее сложим получившиеся стопки в одну в порядке возрастания последней цифры.

231   721  733   323   123  877   777

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

721   323   123
231   733
877   777

Затем из них также образуется одна последовательность.

721   323   123   231   733   877   777

Обратим внимание, что перед последним шагом все номера с числом сотен 7, благодаря предыдущим шагам, расположены один относительно другого по возрастанию.

На последнем шаге номера распределяются по старшей цифре на стопки:

123
231
323
721   733   777
877

и образуется окончательная последовательность:

123   231   323   721   733   777   877.

Далее приведем код программы.

#include "stdafx.h"
#include <iostream>
using namespace std;
const int D = 3;
const int B = 10;

typedef int T[D];
typedef T *List;

void SortD(int k);
void Done();
void outDigs(int i);

List Data;
int PFirst[B], PLast[B], *PQNext;
int first, n, newL, tempL, i, nextI;

int _tmain(int argc, _TCHAR* argv[]){
  int k;
  cout << "Введите количество элементов массива n  ";
  cin >> n;
  Data = new T[n];
  PQNext = new int[n];
  for ( k = 0 ; k < n ; k++ ){
    PQNext[k] = k + 1;
    for ( int r = 0 ; r < D ; r++ )
      Data[k][r] = 0;
  }
  for ( k = 0 ; k < n ; k++ )
    for ( int r = 0 ; r < D ; r++ )
      Data[k][r] = rand()%B;
  first = 0;
  Done();
  cout << endl;
  for ( k = D - 1 ; k >= 0 ; k-- )
    SortD(k);
  Done();
  cout << endl;
  delete [] PQNext;
  delete [] Data;
  system("pause");
  return 0;
}

// описание функции поразрядной сортировки
void SortD(int k){
  for ( tempL = 0 ; tempL < B ; tempL++ ){
    PFirst[tempL] = n;
    PLast[tempL] = n;
  }
  i = first;
  while (i != n){
    tempL = Data[i][k];
    nextI = PQNext[i];
    PQNext[i] = n;
    if ( PFirst[tempL] == n )
      PFirst[tempL] = i;
    else PQNext[PLast[tempL]] = i;
    PLast[tempL] = i;
    i = nextI;
  }
  tempL = 0;
  while ( tempL < B && PFirst[tempL] == n )
    tempL++;
  first = PFirst[tempL];
  while ( tempL < B - 1 ){
    newL = tempL + 1;
    while ( newL < B && PFirst[newL] == n )
      newL++;
    if ( newL < B )
      PQNext[PLast[tempL]] = PFirst[newL];
    tempL = newL;
  }
}

/*описание функции вывода элементов в соответсвии со списком индесов в массиве PQNext*/
void Done(){
  int i = first;
  while ( i != n ){
    outDigs(i);
    i = PQNext[i];
  }
}

/*описание функции вывода элементов из массива Data, индекс которого задан ее аргументом*/
void outDigs(int i){
  int j = 0;
  while ( Data[i][j] == 0 && j < D )
    j++;
  if ( j == D )
    cout << 0;
  else
    while ( j < D )
      cout << Data[i][j++];
  cout << "  ";
}
Листинг .
< Лекция 46 || Лекция 47: 1234 || Лекция 48 >
Денис Курбатов
Денис Курбатов
Владислав Нагорный
Владислав Нагорный

Подскажите, пожалуйста, планируете ли вы возобновление программ высшего образования? Если да, есть ли какие-то примерные сроки?

Спасибо!