Алгоритмизация и программирование. Язык C. Целочисленные алгоритмы (§ 38 - § 45)

Содержание

Слайд 2

Алгоритмизация и программирование. Язык C § 38. Целочисленные алгоритмы

Алгоритмизация и программирование. Язык C

§ 38. Целочисленные алгоритмы

Слайд 3

Решето Эратосфена Эратосфен Киренский (Eratosthenes, Ερατοσθδνη) (ок. 275-194 до н.э.) Новая

Решето Эратосфена

Эратосфен Киренский (Eratosthenes, Ερατοσθδνη) (ок. 275-194 до н.э.)

Новая версия – решето

Аткина.

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

Алгоритм:
начать с k = 2
«выколоть» все числа через k, начиная с 2·k
перейти к следующему «невыколотому» k
если k <= N , то перейти к шагу 2
напечатать все числа, оставшиеся «невыколотыми»

высокая скорость, количество операций

нужно хранить в памяти все числа от 1 до N

O((N·log N)·log log N )

2

3

k·k

k·k <= N

Слайд 4

Решето Эратосфена Задача. Вывести все простые числа от 2 до N.

Решето Эратосфена

Задача. Вывести все простые числа от 2 до N.

Объявление переменных:

const

int N = 100;
bool A[N+1];
int i, k;

Сначала все невычеркнуты:

for ( i = 2; i <= N; i++ )
A[i] = true;

выделяем на 1 элемент больше, чтобы начать с A[1]

Слайд 5

Решето Эратосфена Вычёркивание непростых: k = 2; while ( k*k if

Решето Эратосфена

Вычёркивание непростых:

k = 2;
while ( k*k <= N ) {

if ( A[k] ) {
i = k*k;
while ( i <= N )
{
A[i] = false;
i += k;
}
}
k ++;
}
Слайд 6

Решето Эратосфена Вывод результата: for ( i = 2; i if

Решето Эратосфена

Вывод результата:

for ( i = 2; i <= N; i++

)
if ( A[i] )
printf ( "%d ", i );
Слайд 7

«Длинные» числа Ключи для шифрования: ≥ 256 битов. Целочисленные типы данных:

«Длинные» числа

Ключи для шифрования: ≥ 256 битов.
Целочисленные типы данных: ≤ 64

битов.

Длинное число – это число, которое не помещается в переменную одного из стандартных типов данных языка программирования.

«Длинная арифметика» – алгоритмы для работы с длинными числами.

Слайд 8

«Длинные» числа A = 12345678 нужно хранить длину числа неудобно вычислять

«Длинные» числа

A = 12345678

нужно хранить длину числа
неудобно вычислять (с младшего разряда!)
неэкономное

расходование памяти

Обратный порядок элементов:

Слайд 9

«Длинные» числа Упаковка элементов: 12345678 = 12·10002 + 345·10001 + 678·10000

«Длинные» числа

Упаковка элементов:

12345678 = 12·10002 + 345·10001 + 678·10000

система счисления с

основанием 1000!

от –231 = – 2 147 483 648 до 231 – 1 = 2 147 483 647.

long int:

должны помещаться все промежуточные результаты!

A = 12345678

Слайд 10

Вычисление факториала Задача 1. Вычислить точно значение факториала 100! = 1·2·3·…·99·100

Вычисление факториала

Задача 1. Вычислить точно значение факториала
100! = 1·2·3·…·99·100

1·2·3·…·99·100

< 100100

201 цифра

6 цифр в ячейке ⇒ 34 ячейки

const int N = 33;
long int A[N+1];

Основной алгоритм:

[A] = 1;
for ( k = 2; k <= 100; k ++ )
[A] = [A] * k;

длинное число

основание 1000000

Слайд 11

Вычисление факториала основание d = 1 000 000 [A] = 12345678901734567

Вычисление факториала

основание d = 1 000 000

[A] = 12345678901734567

734 567·3 = 2 203 701

*3

остаётся

в A[0]

r = перенос в A[1]

s = A[0]*k;
A[0] = s % d;
r = s / d;

s:= A[1]*k + r;

Слайд 12

Вычисление факториала r = 0; for ( i = 0; i

Вычисление факториала

r = 0;
for ( i = 0; i <= N;

i++ ) {
s = A[i] * k + r;
A[i] = s % d;
r = s / d;
}

Умножение «длинного» числа на k:

Вычисление 100!:

for ( k = 2; k <= 100; k++ )
{
...
}

все разряды

Слайд 13

Вывод длинного числа [A] = 1000002000003 найти старший ненулевой разряд вывести

Вывод длинного числа

[A] = 1000002000003

найти старший ненулевой разряд
вывести этот разряд
вывести все

следующие разряды, добавляя лидирующие нули до 6 цифр

i = N;
while ( ! A[i] )
i --;

printf ( "%ld", A[i] );

Слайд 14

Вывод длинного числа for ( k = i-1; k >= 0;

Вывод длинного числа

for ( k = i-1; k >= 0; k--

)
Write6 ( A[k] );

Вывод остальных разрядов:

со старшего

Write6:

x = 12345

012345

x / 100000

x % 100000

Слайд 15

Вывод длинного числа Вывод числа с лидирующими нулями: void Write6 (

Вывод длинного числа

Вывод числа с лидирующими нулями:

void Write6 ( long int

x )
{
long int M = 100000;
while ( M > 0 )
{
printf ( "%d", x / M );
x %= M;
M /= 10;
}
}
Слайд 16

Алгоритмизация и программирование. Язык C § 39. Структуры

Алгоритмизация и программирование. Язык C

§ 39. Структуры

Слайд 17

Зачем нужны структуры? Книги в библиотеках: автор название количество экземпляров …

Зачем нужны структуры?

Книги в библиотеках:

автор
название
количество экземпляров

символьные строки

целое число

Задачa: объединить разнотипные данные

в один блок.

Несколько массивов:

char authors[N][40];
char titles[N][80];
int count[N];
...

неудобно работать (сортировать и т.д.), ошибки

Слайд 18

Структуры Структура – это тип данных, который может включать в себя

Структуры

Структура – это тип данных, который может включать в себя несколько

полей – элементов разных типов (в том числе и другие структуры).

typedef struct
{
char author[40]; // автор, строка
char title[80]; // название, строка
int count; // количество, целое
} TBook;

новый тип данных

структура

название типа данных

Слайд 19

Объявление структур const int N = 100; TBook B; TBook Books[N];

Объявление структур

const int N = 100;
TBook B;
TBook Books[N];

printf ( "%d\n", sizeof(TBook)

); // 124
printf ( "%d\n", sizeof(B) ); // 124
printf ( "%d\n", sizeof(Books) ); // 12400

typedef struct
{
char author[40];
char title[80];
int count;
} TBook;

4 байта

40 байт

80 байт

Слайд 20

Обращение к полям структур B.author // поле author структуры B Точечная

Обращение к полям структур

B.author // поле author структуры B

Точечная нотация:

Books[5].count

// поле count структуры
// Books[5]

printf ( "%d\n", sizeof(B.author) ); // 40
printf ( "%d\n", sizeof(B.title) ); // 80
printf ( "%d\n", sizeof(B.count) ); // 4

gets ( B.author );
gets ( B.title );
scanf ( "%d", &B.count );

printf ( "%s %s. %d шт.",
B.author, B.title, B.count );

Слайд 21

Обращение к полям структур strcpy ( B.author,"Пушкин А.С." ); strcpy (

Обращение к полям структур

strcpy ( B.author,"Пушкин А.С." );
strcpy ( B.title, "Полтава"

);
B.count = 1;

B.count --; // одну книгу взяли
if( B.count == 0)
puts ( "Этих книг больше нет!" );

Присваивание:

Использование:

Слайд 22

Запись структур в файлы 'Пушкин А.С.';'Полтава';12 'Лермонтов М.Ю.';'Мцыри';8 Текстовые файлы: символ-разделитель

Запись структур в файлы

'Пушкин А.С.';'Полтава';12
'Лермонтов М.Ю.';'Мцыри';8

Текстовые файлы:

символ-разделитель

Двоичные файлы:

FILE *Fout;
Fout = fopen

( "books.dat", "wb" );
fwrite ( &B, sizeof(B), 1, Fout );
fwrite ( &Books[0], sizeof(TBook),
12, Fout );
fclose ( Fout );

"wb" – запись в двоичный файл
"rb" – чтение двоичного файла
"ab" – добавление к двоичному файлу

binary, двоичный

адрес в памяти

размер блока

сколько блоков

Слайд 23

Чтение структур из файла FILE *Fin; Fin = fopen ( "books.dat",

Чтение структур из файла

FILE *Fin;
Fin = fopen ( "books.dat", "rb" );
fread

( &B, sizeof(B), 1, Fin );
printf ( "%s %s. %d шт.",
B.author, B.title, B.count );
fсlose ( Fin );

fread ( &Books[0], sizeof(TBook), 5, Fin );

Одна структура:

Сразу несколько структур:

адрес в памяти

размер блока

сколько блоков

адрес в памяти

размер структуры

сколько структур

Слайд 24

Чтение структур из файла const int N = 100; int M;

Чтение структур из файла

const int N = 100;
int M;
...
M = fread

( &Books[0], sizeof(TBook),
N, Fin );
printf ( "Прочитано %d структур.", M );

Число структур неизвестно:

Слайд 25

Сортировка структур Ключ – фамилия автора: for ( i = 0;

Сортировка структур

Ключ – фамилия автора:

for ( i = 0; i <

N - 1; i++ )
for ( j = N - 2; j >= i; j-- )
if ( strcmp(Books[j].author,
Books[j+1].author) > 0 )
{
B = Books[j];
Books[j] = Books[j+1];
Books[j+1] = B;
}

структуры перемещаются в памяти

B = Books[j];
Books[j] = Books[j+1];
Books[j+1] = B;

TBook В;

Слайд 26

Указатели Указатель – это переменная, в которой можно сохранить адрес любой

Указатели

Указатель – это переменная, в которой можно сохранить адрес любой переменной

заданного типа.

TBook *p;

указатель на переменную типа TBook

p = &B;

p = &Books[2];

p->author ⇔ Books[2].author

Слайд 27

Сортировка по указателям TBook *p[N], *p1; for ( i = 0;

Сортировка по указателям

TBook *p[N], *p1;

for ( i = 0; i <

N; i++ )
p[i] = &Books[i];

Задача – переставить указатели:

Слайд 28

Сортировка по указателям for ( i = 0; i for (

Сортировка по указателям

for ( i = 0; i < M-1; i++

)
for ( j = M-2; j >= i; j-- )
if ( strcmp ( p[j]->author,
p[j+1]->author ) > 0 )
{
p1 = p[j]; p[j]= p[j+1];
p[j+1]= p1;
}

TBook *p1;

обращение к полям через указатели

Вывод результата:

for ( i = 0; i < M; i++ )
printf ( "%s, %s, %d\n",
p[i]->author, p[i]->title,
p[i]->count );

переставляем указатели!

Слайд 29

Алгоритмизация и программирование. Язык C § 40. Динамические массивы

Алгоритмизация и программирование. Язык C

§ 40. Динамические массивы

Слайд 30

Чем плох обычный массив? const int N = 100; int A[N];

Чем плох обычный массив?

const int N = 100;
int A[N];

статический массив

память выделяется

при трансляции
нужно заранее знать размер
изменить размер нельзя

Задача. В файле записаны фамилии (сколько – неизвестно!). Вывести их в другой файл в алфавитном порядке.

выделить заранее большой блок (с запасом)
выделять память во время работы программы (динамически!)

Слайд 31

Динамические структуры данных создавать новые объекты в памяти изменять их размер

Динамические структуры данных

создавать новые объекты в памяти
изменять их размер
удалять из памяти,

когда не нужны

… позволяют

Задача. Ввести с клавиатуры целое значение N, затем – N целых чисел, и вывести на экран эти числа в порядке возрастания.

// прочитать данные из файла в массив
// отсортировать их по возрастанию
// вывести массив на экран

Слайд 32

Динамические массивы Объявление: int *A; Выделение памяти: #include ... A =

Динамические массивы

Объявление:

int *A;

Выделение памяти:

#include
...
A = (int*) calloc ( N, sizeof(int)

);

преобразовать к указателю на int

количество блоков

размер блока

Слайд 33

Динамические массивы Использование массива: for ( i = 0; i scanf

Динамические массивы

Использование массива:

for ( i = 0; i < N; i++

)
scanf ( "%d", &A[i] );
...
for ( i = 0; i < N; i++ )
{
A[i] = i;
printf ( "%d ", A[i] );
}

Освобождение памяти:

free ( A );

Слайд 34

Динамические матрицы Указатель на матрицу: typedef int *pInt; pInt *A; Выделение

Динамические матрицы

Указатель на матрицу:

typedef int *pInt;
pInt *A;

Выделение памяти под массив указателей:

A

=(pInt*)calloc( N, sizeof(pInt) );

указатель на указатель

Выделение памяти под элементы матрицы:

A[0] =(int*)calloc( N*M, sizeof(int));

число элементов матрицы

новый тип данных: указатель

Слайд 35

Динамические матрицы массив указателей for ( i = 1; i A[i]

Динамические матрицы

массив указателей

for ( i = 1; i < N; i++

)
A[i] = A[i-1] + M;

Расстановка указателей:

Работа с матрицей:

for ( i = 0; i < N; i++ )
for ( j = 0; j < M; j++ )
A[i][j] = i + j;

Удаление:

free ( A[0] );
free ( A );

Слайд 36

Динамические матрицы массив указателей

Динамические матрицы

массив указателей

Слайд 37

Динамические матрицы for ( i = 0; i A[i] =(int*) calloc(M,

Динамические матрицы

for ( i = 0; i < N; i++ )

A[i] =(int*) calloc(M, sizeof(int));

Выделение памяти:

for ( i = 0; i < N; i++ )
free ( A[i] );

Освобождение памяти:

free ( A );

освободить память для отдельных строк

освободить массив указателей

Слайд 38

Расширение массива Задача. С клавиатуры вводятся натуральные числа, ввод заканчивается числом

Расширение массива

Задача. С клавиатуры вводятся натуральные числа, ввод заканчивается числом 0.

Нужно вывести на экран эти числа в порядке возрастания.

Расширение по 1 элементу:

N = 0;
scanf ( "%d", &x );
while ( x!= 0 ) {
N ++;
A = (int*) realloc ( A, N*sizeof(int) );
A[N-1] = x;
scanf ( "%d", &x );
}

перераспределить память

Слайд 39

Расширение массива Расширение по 10 элементов: N = 0; scanf (

Расширение массива

Расширение по 10 элементов:

N = 0;
scanf ( "%d", &x );
while

( x!= 0 )
{
N ++;
if ( N > length )
{
length += 10;
A = (int*) realloc ( A,
length*sizeof(int) );
}
A[N-1] = x;
scanf ( "%d", &x );
}
Слайд 40

Алгоритмизация и программирование. Язык C § 41. Списки

Алгоритмизация и программирование. Язык C

§ 41. Списки

Слайд 41

Зачем нужны списки? Задача. В файле находится список слов, среди которых

Зачем нужны списки?

Задача. В файле находится список слов, среди которых есть

повторяющиеся. Каждое слово записано в отдельной строке. Построить алфавитно-частотный словарь: список слов в алфавитном порядке, справа от каждого слова должно быть указано, сколько раз оно встречается в исходном файле.

Список – это упорядоченный набор элементов одного типа, для которого введены операции вставки (включения) и удаления (исключения).

Слайд 42

Алгоритм (псевдокод) пока есть слова в файле { прочитать очередное слово

Алгоритм (псевдокод)

пока есть слова в файле
{
прочитать очередное слово


если оно есть в списке то
увеличить на 1 счётчик для этого слова
иначе
{
добавить слово в список
записать 1 в счетчик слова
}
}
Слайд 43

Хранение данных typedef struct { char word[40]; int count; } TPair;

Хранение данных

typedef struct
{
char word[40];
int count;
} TPair;


Пара «слово-счётчик»:

typedef struct
{
TPair *data;
int capacity; // размер массива
int size;
} TWordList;

Список таких пар:

динамический массив

количество слов в списке

Слайд 44

Хранение данных TWordList L; Переменная-список: L.size = 0; L.capacity = 10;

Хранение данных

TWordList L;

Переменная-список:

L.size = 0;
L.capacity = 10;
L.data = (TPair*) calloc (

L.capacity,
sizeof(TPair) );

Начальные значения:

Вывод результата:

F = fopen ( "output.txt", "w" );
for ( p = 0; p < L.size; p++ )
fprintf ( F, "%s: %d\n",
L.data[p].word, L.data[p].count );
fclose ( F );

автомат: 1
ананас: 12
...

Слайд 45

Основной цикл F = fopen ( "input.txt", "r" ); while (

Основной цикл

F = fopen ( "input.txt", "r" );
while ( 1 ==

fscanf(F, "%s", s) ) // 1
{
p = Find ( L, s ); // 2
if ( p >= 0 )
L.data[p].count ++; // 3
else
{
p = FindPlace ( L, s ); // 4
InsertWord ( &L, p, s ); // 5
}
}
fclose ( F );
Слайд 46

Поиск слова int Find( TWordList L, char word[] ) { int

Поиск слова

int Find( TWordList L, char word[] )
{
int i;
for

( i = 0; i < L.size; i++ )
if ( 0 == strcmp(L.data[i].word, word) )
return i;
return -1;
}

вернуть -1, если нет в списке

вернуть номер элемента в списке

Слайд 47

Поиск места вставки int FindPlace ( TWordList L, char word[] )

Поиск места вставки

int FindPlace ( TWordList L, char word[] )
{
int

i;
for ( i = 0; i < L.size; i++ )
if ( strcmp(L.data[i].word, word) > 0 )
return i;
return L.size;
}

если не найдено, вставить в конец

первое слово «больше» заданного

Слайд 48

Вставка слова дерево for ( i = L.size-1; i > k;

Вставка слова

дерево

for ( i = L.size-1; i > k; i-- )


L.data[i] = L.data[i-1];

Сдвиг вниз:

с последнего

Слайд 49

Вставка слова void InsertWord ( TWordList *pL, int k, char word[]

Вставка слова

void InsertWord ( TWordList *pL, int k,
char word[]

)
{
int i;
IncSize ( pL );
for ( i = pL->size-1; i > k; i-- )
pL->data[i] = pL->data[i-1]; // 3
strcpy ( pL->data[k].word, word );
pL->data[k].count = 1;
}

список меняется, обращение по указателю

увеличить размер, если нужно

сдвиг вниз

Слайд 50

Расширение списка void IncSize ( TWordList *pL ) { pL->size ++;

Расширение списка

void IncSize ( TWordList *pL )
{
pL->size ++;
if (

pL->size > pL->capacity )
{
pL->capacity += 10;
pL->data =
(TPair*) realloc ( pL->data,
sizeof(TPair)*pL->capacity );
}
}

список меняется

если новый размер больше ёмкости массива

добавить 10 элементов

Слайд 51

Вся программа // объявления типов TPair и TWordList // процедуры и

Вся программа

// объявления типов TPair и TWordList
// процедуры и функции


void main()
{
TWordList L;
int p;
char s[80];
FILE *F;
L.size = 0;
L.capacity = 10;
L.data = (TPair*) calloc ( L.capacity,
sizeof(TPair) );
// основной цикл: чтение списка слов
// вывод результата в файл
}
Слайд 52

Модули main() { ... } // процедура 1 // процедура 2

Модули

main()
{
...
}

// процедура 1
// процедура 2
// процедура 3
//

процедура 4
...
// процедура N-1
// процедура N

проще разбираться («разделяй и властвуй»)
модуль пишет другой программист

wordlist.c

alphalist.c

Слайд 53

Модули main() { ... } void IncSize (...) { ... }

Модули

main()
{
...
}

void IncSize (...)
{ ... }
int Find ( ... )
{

... }
int FindPlace ( ... )
{ ... }
void InsertWord ( ... )
{ ... }

wordlist.c

alphalist.c

#include "wordlist.h"

#include "wordlist.h"

Слайд 54

Заголовочный файл wordlist.h typedef struct { char word[40]; int count; }

Заголовочный файл wordlist.h

typedef struct {
char word[40];
int count;
} TPair;
typedef

struct {
TPair *data;
int capacity;
int size;
} TWordList;
void IncSize ( TWordList *pL );
int Find ( TWordList L, char word[] );
int FindPlace ( TWordList L, char word[] );
void InsertWord ( TWordList *pL, int k,
char word[] );

«интерфейс» – общедоступная информация:
объявление типов данных
объявления процедур и функций

Слайд 55

Проект (Dev-C++, Windows) alphalist.с wordlist.с alphalist.o wordlist.o исходные файлы объектные файлы исполняемый файл

Проект (Dev-C++, Windows)

alphalist.с

wordlist.с

alphalist.o

wordlist.o

исходные файлы

объектные файлы

исполняемый файл

Слайд 56

Связные списки узлы могут размещаться в разных местах в памяти только

Связные списки

узлы могут размещаться в разных местах в памяти

только последовательный доступ

Рекурсивное

определение:

пустой список – это список
список – это узел и связанный с ним список

конец списка

Слайд 57

Связные списки Head Циклический список: Двусвязный список: Head Tail «хвост» обход

Связные списки

Head

Циклический список:

Двусвязный список:

Head

Tail

«хвост»

обход в двух направлениях

сложнее вставка и удаление

Слайд 58

Алгоритмизация и программирование. Язык C § 42. Стек, дек, очередь

Алгоритмизация и программирование. Язык C

§ 42. Стек, дек, очередь

Слайд 59

Что такое стек? Стек (англ. stack – стопка) – это линейный

Что такое стек?

Стек (англ. stack – стопка) – это линейный список,

в котором элементы добавляются и удаляются только с одного конца («последним пришел – первым ушел»).

LIFO = Last In – First Out.

Системный стек:

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

Слайд 60

Реверс массива Задача. В файле записаны целые числа. Нужно вывести их

Реверс массива

Задача. В файле записаны целые числа. Нужно вывести их в

другой файл в обратном порядке.

пока файл не пуст
{
прочитать x
добавить x в стек
}

пока стек не пуст
{
вытолкнуть число из стека в x
записать x в файл
}

1

2

3

4

5

5

4

3

2

1

Слайд 61

Использование динамического массива typedef struct { int *data; int capacity; int

Использование динамического массива

typedef struct {
int *data;
int capacity;
int size;

} TStack;

void Push ( TStack *pS, int x )
{
pS->size ++;
if ( pS->size > pS->capacity ) {
pS->capacity += 10;
pS->data = (int*) realloc ( pS->data,
sizeof(int)*pS->capacity );
}
pS->data[pS->size-1] = x;
}

указатель

«Втолкнуть» x в стек:

Слайд 62

Использование динамического массива int Pop ( TStack *pS ) { pS->size

Использование динамического массива

int Pop ( TStack *pS )
{
pS->size --;
return

pS->data[pS->size];
}

указатель

void InitStack ( TStack *pS, int capacity )
{
pS->data = (int*) calloc ( capacity,
sizeof(int) );
pS->capacity = capacity;
pS->size = 0;
}

«Вытолкнуть» из стека в x :

Инициализация стека :

Слайд 63

Использование динамического массива InitStack ( &S, 10 ); while ( 1

Использование динамического массива

InitStack ( &S, 10 );
while ( 1 == fscanf(Fin,

"%d", &x) )
Push ( &S, x );

Заполнение стека:

while ( S.size > 0 )
{
x = Pop( &S );
fprintf ( Fout, "%d\n", x );
}

Вывод результата в файл:

FILE *Fin;

Слайд 64

Вычисление арифметических выражений (5+15)/(4+7-1) инфиксная форма (знак операции между данными) первой

Вычисление арифметических выражений

(5+15)/(4+7-1)

инфиксная форма (знак операции между данными)

первой стоит последняя

операция (вычисляем с конца)

1920 (Я. Лукашевич): префиксная форма
(знак операции перед данными)

/ + 5 15 - + 4 7 1

/ 20 - + 4 7 1

/ 20 - 11 1

/ 20 10

2

не нужны скобки

Слайд 65

Вычисление арифметических выражений (5+15)/(4+7-1) 1950-е: постфиксная форма (знак операции после данных)

Вычисление арифметических выражений

(5+15)/(4+7-1)

1950-е: постфиксная форма
(знак операции после данных)

не нужны

скобки
вычисляем с начала

5 15 + 4 7 + 1 - /

20 4 7 + 1 - /

20 11 1 - /

20 10 /

2

Слайд 66

Использование стека 5 15 + 4 7 + 1 - /

Использование стека

5

15

+

4

7

+

1

-

/

5 15 + 4 7 + 1 - /

если число

– «втолкнуть» в стек
если операция – выполнить с верхними элементами стека
Слайд 67

Скобочные выражения Задача. Вводится символьная строка, в которой записано некоторое (арифметическое)

Скобочные выражения

Задача. Вводится символьная строка, в которой записано некоторое (арифметическое) выражение,

использующее скобки трёх типов: ( ), [ ] и { }. Проверить, правильное ли расставлены скобки.

()[{()[]}]

[()

)(

[()}

([)]

Для одного типа скобок:

( ) ( ( ) ( ( ) ) )

счётчик 0

1

0

1

2

1

2

3

2

1

0

счётчик всегда ≥ 0
в конце счётчик = 0

({[)}]

Слайд 68

Скобочные выражения (стек) когда встретили закрывающую скобку, на вершине стека лежит

Скобочные выражения (стек)

когда встретили закрывающую скобку, на вершине стека лежит соответствующая

открывающая
в конце работы стек пуст

если открывающая скобка – «втолкнуть» в стек
если закрывающая скобка – снять парную со стека

Слайд 69

Скобочные выражения (стек) typedef struct { char *data; int capacity; int

Скобочные выражения (стек)

typedef struct {
char *data;
int capacity;
int size;

} TStack;

Модель стека:

Cтек пуст:

bool isEmpty ( TStack S )
{
return S.size == 0;
}

Слайд 70

Скобочные выражения (стек) const char L[] = "([{", // открывающие R[]

Скобочные выражения (стек)

const char L[] = "([{", // открывающие
R[] =

")]}"; // закрывающие
char str[80]; // рабочая строка
TStack S; // стек
bool err; // была ли ошибка?
int i;
char c, *p;

Константы и переменные:

Вывод результата:

if ( ! err )
printf ( "Скобки расставлены верно." );
else
printf ( "Скобки расставлены неверно." );

Слайд 71

Скобочные выражения (стек) for ( i = 0; i p =

Скобочные выражения (стек)

for ( i = 0; i < strlen(str); i++

) {
p = strchr ( L, str[i] );
if ( p!= NULL )
Push ( &S, str[i] );
p = strchr ( R, str[i] );
if ( p!= NULL ) {
if ( isEmpty ( S ) )
err = true;
else {
c = Pop ( &S );
if ( p-R!= strchr(L,c)-L )
err = true;
}
if ( err ) break;
}
}

открывающую скобку в стек

если закрывающая скобка…

если не та скобка…

Слайд 72

Что такое очередь? Очередь – это линейный список, для которого введены

Что такое очередь?

Очередь – это линейный список, для которого введены две

операции:
• добавление элемента в конец
• удаление первого элемента

FIFO = Fist In – First Out.

Применение:

очереди сообщений в операционных системах
очереди запросов ввода и вывода
очереди пакетов данных в маршрутизаторах

Слайд 73

Заливка области Задача. Рисунок задан в виде матрицы A, в которой

Заливка области

Задача. Рисунок задан в виде матрицы A, в которой элемент

A[y][x] определяет цвет пикселя на пересечении строки y и столбца x. Перекрасить в цвет 2 одноцветную область, начиная с пикселя (x0,y0).

(1,2)

Слайд 74

Заливка: использование очереди добавить в очередь точку (x0,y0) запомнить цвет начальной

Заливка: использование очереди

добавить в очередь точку (x0,y0)
запомнить цвет начальной точки
пока очередь

не пуста
{
взять из очереди точку (x,y)
если A[y][x] = цвету начальной точки то
{
A[y][x] = 2;
добавить в очередь точку (x-1,y)
добавить в очередь точку (x+1,y)
добавить в очередь точку (x,y-1)
добавить в очередь точку (x,y+1)
}
}
Слайд 75

Очередь (динамический массив) typedef struct { int x, y; } TPoint;

Очередь (динамический массив)

typedef struct {
int x, y;
} TPoint;
typedef struct

{
TPoint *data;
int capacity;
int size;
} TQueue;

TPoint Point ( int x, int y )
{
TPoint P;
P.x = x; P.y = y;
return P;
}

Построение структуры «точка»:

структура «точка»

структура «очередь» (динамический массив)

Слайд 76

Очередь (динамический массив) Добавить точку в очередь: void Put ( TQueue

Очередь (динамический массив)

Добавить точку в очередь:

void Put ( TQueue *pQ, TPoint

P )
{
pQ->size ++;
if ( pQ->size > pQ->capacity ) {
pQ->capacity += 10;
pQ->data =(TPoint*) realloc ( pQ->data,
sizeof(TPoint)*pQ->capacity );
}
pQ->data[pQ->size-1] = P;
}

расширить, если нужно

4

5

Слайд 77

Очередь (динамический массив) Получить первую точку в очереди: TPoint Get (

Очередь (динамический массив)

Получить первую точку в очереди:

TPoint Get ( TQueue *pQ

)
{
TPoint P = pQ->data[0];
int i;
pQ->size --;
for ( i = 0; i < pQ->size; i++ )
pQ->data[i] = pQ->data[i+1];
return P;
}

уменьшить размер

продвинуть оставшиеся элементы

2

4

3

4

5

1

Слайд 78

Заливка const int XMAX = 5, YMAX = 5, NEW_COLOR =

Заливка

const int XMAX = 5, YMAX = 5,
NEW_COLOR =

2;

// заполнить матрицу A
y0 = 0; x0 = 1; // начать заливку отсюда
color = A[y0][x0]; // цвет начальной точки
Put ( &Q, Point(x0,y0) );

Константы и переменные:

Начало программы:

int A[YMAX][XMAX]; // матрица
TQueue Q; // очередь
int i, j, x0, y0, color;
TPoint pt;

Слайд 79

Заливка (основной цикл) while ( ! isEmpty(Q) ) { pt =

Заливка (основной цикл)

while ( ! isEmpty(Q) ) {
pt = Get

( &Q );
if ( A[pt.y][pt.x] == color ) {
A[pt.y][pt.x] = NEW_COLOR;
if ( pt.x > 0 )
Put ( &Q, Point(pt.x-1,pt.y) );
if ( pt.y > 0 )
Put ( &Q, Point(pt.x,pt.y-1) );
if ( pt.x < XMAX-1 )
Put ( &Q, Point(pt.x+1,pt.y) );
if ( pt.y < YMAX-1 )
Put ( &Q, Point(pt.x,pt.y+1) );
}
}

пока очередь не пуста

Слайд 80

Очередь: статический массив нужно знать размер не двигаем элементы голова хвост Удаление элемента: Добавление элемента:

Очередь: статический массив

нужно знать размер

не двигаем элементы

голова

хвост

Удаление элемента:

Добавление элемента:

Слайд 81

Очередь: статический массив Замыкание в кольцо: Очередь заполнена: Очередь пуста:

Очередь: статический массив

Замыкание в кольцо:

Очередь заполнена:

Очередь пуста:

Слайд 82

Что такое дек? Дек – это линейный список, в котором можно

Что такое дек?

Дек – это линейный список, в котором можно добавлять

и удалять элементы как с одного, так и с другого конца.

Моделирование:

статический массив (кольцо)
динамический массив
связный список

Слайд 83

Алгоритмизация и программирование. Язык C § 43. Деревья

Алгоритмизация и программирование. Язык C

§ 43. Деревья

Слайд 84

Что такое дерево? «Сыновья» А: B, C. «Родитель» B: A. «Потомки»

Что такое дерево?

«Сыновья» А: B, C.

«Родитель» B: A.

«Потомки» А: B, C,

D, E, F, G.

«Предки» F: A, C.

Корень – узел, не имеющий предков (A).

Лист – узел, не имеющий потомков (D, E, F, G).

Слайд 85

Рекурсивные определения пустая структура – это дерево дерево – это корень

Рекурсивные определения

пустая структура – это дерево
дерево – это корень и несколько

связанных с ним отдельных (не связанных между собой) деревьев

Двоичное (бинарное) дерево:

пустая структура – это двоичное дерево
двоичное дерево – это корень и два связанных с ним отдельных двоичных дерева («левое» и «правое» поддеревья)

Применение:

поиск в большом массиве неменяющихся данных
сортировка данных
вычисление арифметических выражений
оптимальное сжатие данных (метод Хаффмана)

Слайд 86

Деревья поиска Ключ – это значение, связанное с узлом дерева, по

Деревья поиска

Ключ – это значение, связанное с узлом дерева, по которому

выполняется поиск.

слева от узла – узлы с меньшими или равными ключами
справа от узла – узлы с большими или равными ключами

O(log N)

Двоичный поиск O(log N)

Линейный поиск O(N)

Слайд 87

Обход дерева Обойти дерево ⇔ «посетить» все узлы по одному разу.

Обход дерева

Обойти дерево ⇔ «посетить» все узлы по одному разу.

список узлов

КЛП – «корень-левый-правый» (в прямом порядке):

посетить корень
обойти левое поддерево
обойти правое поддерево

ЛКП – «левый-корень-правый» (симметричный):

посетить корень
обойти левое поддерево
обойти правое поддерево

ЛПК – «левый-правый-корень» (в обратном порядке):

посетить корень
обойти левое поддерево
обойти правое поддерево

Слайд 88

Обход дерева ЛПК: КЛП: ЛКП: * + 1 4 – 9

Обход дерева

ЛПК:

КЛП:

ЛКП:

* + 1 4 – 9 5

1 + 4 *

9 - 5

1 4 + 9 5 - *

префиксная форма

инфиксная форма

постфиксная форма

Обход «в ширину»: «сыновья», потом «внуки», …

* + - 1 4 9 5

«в глубину»

Слайд 89

Обход КЛП – обход «в глубину» записать в стек корень дерева

Обход КЛП – обход «в глубину»

записать в стек корень дерева
пока стек

не пуст
{
выбрать узел V с вершины стека
посетить узел V
если у узла V есть правый сын то
добавить в стек правого сына V
если у узла V есть левый сын то
добавить в стек левого сына V
}
Слайд 90

Обход КЛП – обход «в глубину» * + 1 4 – 9 5

Обход КЛП – обход «в глубину»

*

+

1

4


9

5

Слайд 91

Обход «в ширину» записать в очередь корень дерева пока очередь не

Обход «в ширину»

записать в очередь корень дерева
пока очередь не пуста
{


выбрать узел V из очереди
посетить узел V
если у узла V есть левый сын то
добавить в очередь левого сына V
если у узла V есть правый сын то
добавить в очередь правого сына V
}
Слайд 92

Обход «в ширину» (1+4)*(9-5) * + - 1 4 9 5 голова очереди

Обход «в ширину»

(1+4)*(9-5)

*

+

-

1

4

9

5

голова очереди

Слайд 93

Вычисление арифметических выражений 40–2*3–4*5 В корень дерева нужно поместить последнюю из операций с наименьшим приоритетом.

Вычисление арифметических выражений

40–2*3–4*5

В корень дерева нужно поместить последнюю из операций с

наименьшим приоритетом.
Слайд 94

Вычисление арифметических выражений найти последнюю выполняемую операцию если операций нет то

Вычисление арифметических выражений

найти последнюю выполняемую операцию
если операций нет то
{
создать

узел-лист
выход
}
поместить операцию в корень дерева
построить левое поддерево
построить правое поддерево

построить левое поддерево
построить правое поддерево

Построение дерева:

Слайд 95

Вычисление арифметических выражений n1 = значение левого поддерева n2 = значение

Вычисление арифметических выражений

n1 = значение левого поддерева
n2 = значение правого поддерева
результат

= операция(n1, n2)

значение левого поддерева
значение правого поддерева

Вычисление по дереву:

Слайд 96

Использование связанных структур Дерево – нелинейная структура ⇒ динамический массив неудобен!

Использование связанных структур

Дерево – нелинейная структура ⇒ динамический массив неудобен!

typedef struct

TNode *PNode;
typedef struct TNode
{
char data[20];
PNode left;
PNode right;
} TNode;

ссылка вперёд

новый тип: адрес узла

Слайд 97

Работа с памятью Выделить память для узла: PNode p; // указатель

Работа с памятью

Выделить память для узла:

PNode p; // указатель на узел


p = (PNode) calloc ( 1, sizeof(TNode) );

Обращение к новому узлу (по указателю):

strcpy ( p->data, s );
p->left = NULL;
p->right = NULL;

Освобождение памяти:

free(p);

Слайд 98

Основная программа main() { PNode T; char s[80]; // ввести строку

Основная программа

main()
{
PNode T;
char s[80];
// ввести строку s
T

= MakeTree ( s );
printf ( "Результат: %d", Calc(T) );
}
Слайд 99

Построение дерева PNode MakeTree ( char s[] ) { int k;

Построение дерева

PNode MakeTree ( char s[] )
{
int k;
PNode Tree;


char sLeft[80] = "";
Tree = (PNode) calloc ( 1, sizeof(TNode) );
k = LastOp ( s );
if ( k == -1 ) {
// новый узел – лист (число)
}
else {
// новый узел – операция
// построить поддеревья
}
return Tree;
}

вернёт адрес нового дерева

пустая строка!

Слайд 100

Построение дерева Tree->data[0] = s[k]; Tree->data[1] = '\0'; strncpy ( sLeft,

Построение дерева

Tree->data[0] = s[k];
Tree->data[1] = '\0';
strncpy ( sLeft, s, k );
Tree->left

= MakeTree ( sLeft );
Tree->right = MakeTree ( &s[k+1] );

MakeTree ( sLeft );
MakeTree ( &s[k+1] );

strcpy ( Tree->data, s );
Tree->left = NULL;
Tree->right = NULL;

Новый узел – лист:

Новый узел – операция:

нет сыновей!

один символ!

k

s

sLeft

дальше – нули!

Слайд 101

Вычисление по дереву int Calc ( PNode Tree ) { int

Вычисление по дереву

int Calc ( PNode Tree )
{
int n1, n2,

res;
if ( Tree->left == NULL )
res = atoi ( Tree->data );
else {
n1 = Calc ( Tree->left );
n2 = Calc ( Tree->right );
switch ( Tree->data[0] ) {
case '+': res = n1 + n2; break;
case '-': res = n1 - n2; break;
case '*': res = n1 * n2; break;
case '/': res = n1 / n2; break;
default: res = 99999;
}
}
return res;
}

Calc ( Tree->left );
Calc ( Tree->right );

это число (лист)

Слайд 102

Приоритет операции int Priority ( char op ) { switch (

Приоритет операции

int Priority ( char op )
{
switch ( op )


{
case '+':
case '-': return 1;
case '*':
case '/': return 2;
}
return 100;
}
Слайд 103

Последняя выполняемая операция int LastOp ( char s[] ) { int

Последняя выполняемая операция

int LastOp ( char s[] )
{
int i, minPrt,

res;
minPrt = 50; // любое между 2 и 100
res = -1;
for ( i = 0; i < strlen(s); i++ )
if ( Priority(s[i]) <= minPrt )
{
minPrt = Priority(s[i]);
res = i;
}
return res;
}

<=

вернёт номер символа

Слайд 104

Двоичное дерево в массиве ? ?

Двоичное дерево в массиве

?

?

Слайд 105

Алгоритмизация и программирование. Язык C § 44. Графы

Алгоритмизация и программирование. Язык C

§ 44. Графы

Слайд 106

Что такое граф? Граф – это набор вершин и связей между

Что такое граф?

Граф – это набор вершин и связей между

ними (рёбер).

петля

Матрица смежности:

Список смежности:

( A(B, C), B(A, C, D), C(A, B, С, D), D(B, C) )

Слайд 107

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

Связность графа

Связный граф – это граф, между любыми вершинами которого

существует путь.
Слайд 108

Дерево – это граф? дерево ABC ABDC BCD CCC… Дерево –

Дерево – это граф?

дерево

ABC ABDC
BCD CCC…

Дерево – это связный граф без циклов

(замкнутых путей).
Слайд 109

Взвешенные графы Весовая матрица: вес ребра

Взвешенные графы

Весовая матрица:

вес ребра

Слайд 110

Ориентированные графы (орграфы) Рёбра имеют направление (начало и конец), рёбра называю дугами.

Ориентированные графы (орграфы)

Рёбра имеют направление (начало и конец), рёбра называю дугами.

Слайд 111

Жадные алгоритмы Жадный алгоритм – это многошаговый алгоритм, в котором на

Жадные алгоритмы

Жадный алгоритм – это многошаговый алгоритм, в котором на каждом

шаге принимается решение, лучшее в данный момент.

Задача. Найти кратчайший маршрут из А в F.

Слайд 112

Жадные алгоритмы Задача. Найти кратчайший маршрут из А в F.

Жадные алгоритмы

Задача. Найти кратчайший маршрут из А в F.

Слайд 113

Задача Прима-Крускала Задача. Между какими городами нужно проложить линии связи, чтобы

Задача Прима-Крускала

Задача. Между какими городами нужно проложить линии связи, чтобы все

города были связаны в одну систему и общая длина линий связи была наименьшей? (минимальное остовное дерево)

Алгоритм Крускала:

начальное дерево – пустое
на каждом шаге добавляется ребро минимального веса, которое ещё не входит в дерево и не приводит к появлению цикла

Слайд 114

Раскраска вершин 4 B 2 1 2 9 7 8 1

Раскраска вершин

4

B

2

1

2

9

7

8

1

3

D

E

F

A

C

ищем ребро минимальной длины среди всех рёбер, концы которых окрашены

в разные цвета;
найденное ребро (iMin,jMin) добавляется в список выбранных, и все вершины, имеющие цвет col[jMin], перекрашиваются в цвет col[iMin].

Сделать N-1 раз:

for (i = 0; i < N; i ++) col[i] = i;

каждой вершине свой цвет

Слайд 115

Раскраска вершин const int N = 6; int W[N][N]; // весовая

Раскраска вершин

const int N = 6;
int W[N][N]; // весовая матрица


int col[N]; // цвета вершин
// номера вершин для выбранных ребер
int ostov[N-1][2];
int i, j, k, iMin, jMin, min, c;

Данные:

Вывод результата:

for ( i = 0; i < N-1; i ++ )
printf ( "(%d,%d)\n", ostov[i][0],
ostov[i][1] );

Слайд 116

Раскраска вершин for ( k = 0; k // поиск ребра

Раскраска вершин

for ( k = 0; k < N-1; k++ )

{
// поиск ребра с минимальным весом
minDist = 99999;
for ( i = 0; i < N; i ++ )
for ( j = 0; j < N; j ++ )
if ( col[i] != col[j] &&
W[i][j] < minDist ) {
iMin = i; jMin = j; minDist = W[i][j];
}
// добавление ребра в список выбранных
ostov[k][0] = iMin; ostov[k][1] = jMin;
// перекрашивание вершин
c = col[jMin];
for ( i = 0; i < N; i ++ )
if ( col[i] == c ) col[i] = col[iMin];
}

нет цикла

Слайд 117

Кратчайший маршрут Алгоритм Дейкстры (1960): кратчайшее расстояние откуда ехать ближайшая от A невыбранная вершина

Кратчайший маршрут

Алгоритм Дейкстры (1960):

кратчайшее расстояние

откуда ехать

ближайшая от A невыбранная вершина

Слайд 118

Кратчайший маршрут Алгоритм Дейкстры (1960): кратчайшее расстояние откуда ехать W[x,z] +

Кратчайший маршрут

Алгоритм Дейкстры (1960):

кратчайшее расстояние

откуда ехать

W[x,z] + W[z,y] < W[x,y]

может быть

так, что

9

B

Слайд 119

Кратчайший маршрут Алгоритм Дейкстры (1960): кратчайшее расстояние откуда ехать W[x,z] +

Кратчайший маршрут

Алгоритм Дейкстры (1960):

кратчайшее расстояние

откуда ехать

W[x,z] + W[z,y] < W[x,y]

может быть

так, что

5

C

Слайд 120

Кратчайший маршрут Алгоритм Дейкстры (1960): кратчайшее расстояние откуда ехать 7 E 8 E

Кратчайший маршрут

Алгоритм Дейкстры (1960):

кратчайшее расстояние

откуда ехать

7

E

8

E

Слайд 121

Кратчайший маршрут длины кратчайших маршрутов из A в другие вершины A

Кратчайший маршрут

длины кратчайших маршрутов из A в другие вершины

A → C

→ E → F
Слайд 122

Алгоритм Дейкстры const int N = 6; int W[N][N]; // весовая

Алгоритм Дейкстры

const int N = 6;
int W[N][N]; // весовая матрица
bool active[N];

// вершина не выбрана?
int R[N], P[N];
int i, j, min, kMin;

Данные:

Начальные значения (выбор начальной вершины):

for ( i = 0; i < N; i ++ ) {
active[i] = true; // все вершины не выбраны
R[i] = W[0][i]; // рёбра из вершины 0
P[i] = 0;
}
active[0] = false; // вершина уже выбрана
P[0] = -1; // это начальная вершина

Слайд 123

Алгоритм Дейкстры for ( i = 0; i minDist = 99999;

Алгоритм Дейкстры

for ( i = 0; i < N-1; i++ )

{
minDist = 99999;
for ( j = 0; j < N; j ++ )
if ( active[j] && R[j] < minDist) {
minDist = R[j];
kMin = j;
}
active[kMin] = false;
for ( j = 0; j < N; j ++ )
if ( R[kMin]+W[kMin][j] < R[j] ) {
R[j] = R[kMin] + W[kMin][j];
P[j] = kMin;
}
}

Основной цикл:

выбор следующей вершины, ближайшей к A

проверка маршрутов через вершину kMin

Слайд 124

Алгоритм Дейкстры i = N-1; while ( i != -1 )

Алгоритм Дейкстры

i = N-1;
while ( i != -1 )
{


printf ( "%d ", i );
i = P[i]; // к следующей вершине
}

Вывод результата (маршрут 0 → N-1):

для начальной вершины P[i]=-1

A → C → E → F

Слайд 125

Алгоритм Флойда for ( k = 0; k for ( i

Алгоритм Флойда

for ( k = 0; k < N; k++ )


for ( i = 0; i < N; i++ )
for ( j = 0; j < N; j++ )
if ( W[i][k]+W[k][j] < W[i][j] )
W[i][j] = W[i][k] + W[k][j];

Все кратчайшие пути (из любой вершины в любую):

Слайд 126

Алгоритм Флойда + маршруты for ( i = 0; i for

Алгоритм Флойда + маршруты

for ( i = 0; i < N;

i++ ) {
for ( j = 0; j < N; j++ )
P[i][j] = i;
P[i][i] = -1;
}

Дополнительная матрица:

for ( k = 0; k < N; k++ )
for ( i = 0; i < N; i++ )
for ( j = 0; j < N; j++ )
if ( W[i][k] + W[k][j] < W[i][j] ) {
W[i][j] = W[i][k] + W[k][j];
P[i][j] = P[k][j];
}

Кратчайшие длины путей и маршруты:

Слайд 127

Задача коммивояжера Коммивояжер (бродячий торговец) должен выйти из города 1 и,

Задача коммивояжера

Коммивояжер (бродячий торговец) должен выйти из города 1 и, посетив

по разу в неизвестном порядке города 2,3,...N, вернуться обратно в город 1. В каком порядке надо обходить города, чтобы путь коммивояжера был кратчайшим?

Точные методы:
простой перебор;
метод ветвей и границ;
метод Литтла;

Приближенные методы:
метод случайных перестановок (Matlab)
генетические алгоритмы
метод муравьиных колоний

большое время счета для больших N

O(N!)

не гарантируется оптимальное решение

Слайд 128

Некоторые задачи Задача на минимум суммы. Имеется N населенных пунктов, в

Некоторые задачи

Задача на минимум суммы. Имеется N населенных пунктов, в каждом

из которых живет pi школьников (i=1,...,N). Надо разместить школу в одном из них так, чтобы общее расстояние, проходимое всеми учениками по дороге в школу, было минимальным.
Задача о наибольшем потоке. Есть система труб, которые имеют соединения в N узлах. Один узел S является источником, еще один – стоком T. Известны пропускные способности каждой трубы. Надо найти наибольший поток от источника к стоку.
Задача о наибольшем паросочетании. Есть M мужчин и N женщин. Каждый мужчина указывает несколько (от 0 до N) женщин, на которых он согласен жениться. Каждая женщина указывает несколько мужчин (от 0 до M), за которых она согласна выйти замуж. Требуется заключить наибольшее количество моногамных браков.
Слайд 129

Алгоритмизация и программирование. Язык C § 44. Динамическое программирование

Алгоритмизация и программирование. Язык C

§ 44. Динамическое программирование

Слайд 130

Что такое динамическое программирование? Числа Фибоначчи: ; . F1 = F2

Что такое динамическое программирование?

Числа Фибоначчи:

;

.

F1 = F2 = 1
Fn =

Fn-1 + Fn-2, при n > 2

int Fib ( int N )
{
if ( N < 3 )
return 1;
else return Fib(N-1) + Fib(N-2);
}

повторное вычисление тех же значений

Слайд 131

Динамическое программирование Объявление массива: const int N = 10; int F[N+1];

Динамическое программирование

Объявление массива:

const int N = 10;
int F[N+1]; // чтобы начать

с 1

Заполнение массива:

F[1] = 1; F[2] = 1;
for ( i = 3; i <= N; i++ )
F[i] = F[i-1] + F[i-2];

F1 = F2 = 1
Fn = Fn-1 + Fn-2, при n > 2

нужны только два последних!

Слайд 132

Динамическое программирование Динамическое программирование – это способ решения сложных задач путем

Динамическое программирование

Динамическое программирование – это способ решения сложных задач путем сведения

их к более простым задачам того же типа.

1

2

5

ABE: 5 + 20 = 25

AСE: 2 + 30 = 32

ADE: 1 + 40 = 41

дополнительный расход памяти

увеличение скорости

Слайд 133

Количество вариантов Задача. Найти количество KN цепочек, состоящих из N нулей

Количество вариантов

Задача. Найти количество KN цепочек, состоящих из N нулей и

единиц, в которых нет двух стоящих подряд единиц.

Решение «в лоб»:

0/1

битовые цепочки

построить все возможные цепочки
проверить каждую на «правильность»

2N

Сложность алгоритма O(2N)

Слайд 134

Количество вариантов Задача. Найти количество KN цепочек, состоящих из N нулей

Количество вариантов

Задача. Найти количество KN цепочек, состоящих из N нулей и

единиц, в которых нет двух стоящих подряд единиц.

Простые случаи:

K1=2

N = 1:

0 1

K2=3

N = 2:

00 01 10

Общий случай:

KN-1 «правильных» цепочек начинаются с нуля!

KN-2 «правильных» цепочек начинаются с единицы!

KN-2

KN-1

KN = KN-1 + KN-2

= FN+2

Слайд 135

Оптимальное решение Задача. В цистерне N литров молока. Есть бидоны объемом

Оптимальное решение

Задача. В цистерне N литров молока. Есть бидоны объемом 1,

5 и 6 литров. Нужно разлить молоко в бидоны так, чтобы все бидоны были заполнены и количество используемых бидонов было минимальным.

Перебор?

при больших N – очень долго!

«Жадный алгоритм»?

N = 10:

10 = 6 + 1 + 1 + 1 + 1

10 = 5 + 5

K = 5

K = 2

Слайд 136

Оптимальное решение Сначала выбрали бидон… KN – минимальное число бидонов для

Оптимальное решение

Сначала выбрали бидон…

KN – минимальное число бидонов для N литров

KN

= 1 + KN-1

1 л:

KN = 1 + KN-5

5 л:

KN = 1 + KN-6

6 л:

min

KN = 1 + min (KN-1 , KN-5 , KN-6)

при N ≥ 6

KN = 1 + min (KN-1 , KN-5)

при N = 5

KN = 1 + KN-1

при N < 5

Рекуррентная формула:

Слайд 137

Оптимальное решение (бидоны) 1 1 2 1 3 1 4 1

Оптимальное решение (бидоны)

1

1

2

1

3

1

4

1

1

5

1

6

2

1

3

1

4

1

2

5

KN = 1 + min (KN-1 , KN-5 ,

KN-6)

2 бидона

5 + 5

Слайд 138

Задача о куче Задача. Из камней весом pi (i=1, …, N)

Задача о куче

Задача. Из камней весом pi (i=1, …, N) набрать

кучу весом ровно W или, если это невозможно, максимально близкую к W (но меньшую, чем W).

камень
взят

камень
не взят

2N

Сложность алгоритма O(2N)

Решение «в лоб»:

Слайд 139

Задача о куче Задача. Из камней весом pi (i=1, …, N)

Задача о куче

Задача. Из камней весом pi (i=1, …, N) набрать

кучу весом ровно W или, если это невозможно, максимально близкую к W (но меньшую, чем W).

Идея: сохранять в массиве решения всех более простых задач этого типа (при меньшем количестве камней N и меньшем весе W).

Пример: W = 8, камни 2, 4, 5 и 7

w

pi

базовые случаи

T[i][w] – оптимальный вес, полученный для кучи весом w из i первых по счёту камней.

i

Слайд 140

Задача о куче Добавляем камень с весом 4: для w 0

Задача о куче

Добавляем камень с весом 4:

для w < 4 ничего

не меняется!

0

2

2

для w ≥ 4:

если его не брать:

T[2][w] = T[1][w]

если его взять:

T[2][w] = 4 + T[1][w-4]

max

4

4

6

6

6

Слайд 141

Задача о куче Добавляем камень с весом 5: для w 0

Задача о куче

Добавляем камень с весом 5:

для w < 5 ничего

не меняется!

0

2

2

4

5

6

7

7

Слайд 142

Задача о куче Добавляем камень с весом 7: для w 0

Задача о куче

Добавляем камень с весом 7:

для w < 7 ничего

не меняется!

0

2

2

4

5

6

7

7

Слайд 143

Задача о куче Добавляем камень с весом pi: для w Рекуррентная формула:

Задача о куче

Добавляем камень с весом pi:

для w < pi ничего

не меняется!

Рекуррентная формула:

Слайд 144

Задача о куче Оптимальный вес 7 5 + 2

Задача о куче

Оптимальный вес 7

5 + 2

Слайд 145

Задача о куче Заполнение таблицы: W+1 N псевдополиномиальный

Задача о куче

Заполнение таблицы:

W+1

N

псевдополиномиальный

Слайд 146

Количество программ Задача. У исполнителя Утроитель есть команды: 1. прибавь 1

Количество программ

Задача. У исполнителя Утроитель есть команды:
1. прибавь 1
2.

умножь на 3
Сколько есть разных программ, с помощью которых можно из числа 1 получить число 20?
Слайд 147

Количество программ Как получить число N: N если делится на 3!

Количество программ

Как получить число N:

N

если делится на 3!

последняя команда

Рекуррентная формула:

KN =

KN-1 если N не делится на 3
KN = KN-1 + KN/3 если N делится на 3
Слайд 148

Количество программ Заполнение таблицы: Рекуррентная формула: KN = KN-1 если N

Количество программ

Заполнение таблицы:

Рекуррентная формула:

KN = KN-1 если N не делится на

3
KN = KN-1 + KN/3 если N делится на 3

1

2

2

2

3

3

3

5

5

5

7

7

7

9

9

9

12

12

12

K2+K1

K5+K2

K8+K3

одна пустая!

Слайд 149

Количество программ Только точки изменения: 12 20 Программа: K[1] = 1;

Количество программ

Только точки изменения:

12

20

Программа:

K[1] = 1;
for ( i = 2; i

<= N; i ++ )
{
K[i] = K[i-1];
if ( i % 3 == 0 )
K[i] = K[i] + K[i/3];
}
Слайд 150

Размен монет Задача. Сколькими различными способами можно выдать сдачу размером W

Размен монет

Задача. Сколькими различными способами можно выдать сдачу размером W рублей,

если есть монеты достоинством pi (i=1, …, N)? В наборе есть монета достоинством 1 рубль (p1 = 1).

Перебор?

при больших N и W – очень долго!

Динамическое программирование:

запоминаем решения всех задач меньшей размерности: для меньших значений W и меньшего числа монет N.

Слайд 151

Размен монет Пример: W = 10, монеты 1, 2, 5 и

Размен монет

Пример: W = 10, монеты 1, 2, 5 и 10

w

pi

базовые

случаи

T[i][w] – количество вариантов для суммы w с использованием i первых по счёту монет.

i

Рекуррентная формула (добавили монету pi):

при w < pi:

при w ≥ pi:

T[i][w] = T[i-1][w]

T[i][w] = T[i-1][w] + T[i][w-pi]

все варианты размена остатка

T[i-1][w]

без этой монеты

T[i][w-pi]

Слайд 152

Размен монет Пример: W = 10, монеты 1, 2, 5 и

Размен монет

Пример: W = 10, монеты 1, 2, 5 и 10

Рекуррентная

формула (добавили монету pi):
Слайд 153

Конец фильма ПОЛЯКОВ Константин Юрьевич д.т.н., учитель информатики ГБОУ СОШ №

Конец фильма

ПОЛЯКОВ Константин Юрьевич
д.т.н., учитель информатики
ГБОУ СОШ № 163, г. Санкт-Петербург
kpolyakov@mail.ru

ЕРЕМИН Евгений Александрович
к.ф.-м.н., доцент кафедры мультимедийной дидактики и ИТО ПГГПУ, г. Пермь
eremin@pspu.ac.ru