Приемы построения программ

Методы построения программы с одновременным доказательством ее правильности, сведения ее к более простым задачам. Спецификация программ, логические средства. Предусловие и постусловие, построение инварианта. Выделение последовательности из массива.

Рубрика Программирование, компьютеры и кибернетика
Вид методичка
Язык русский
Дата добавления 08.09.2015
Размер файла 68,3 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

МИНИСТЕРСТВО образованиЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ

Федеральное государственное автономное образовательное учреждение

высшего профессионального образования

"ЮЖНЫЙ ФЕДЕРАЛЬНЫЙ УНИВЕРСИТЕТ"

Методические указания

по курсу

"Информатика"

Приемы построения программ

для студентов дневного и вечернего отделений

факультета математики, механики и компьютерных наук

Невская Е.С.

Ростов-на-Дону 2011

Рецензент: ст. преподаватель кафедры АДМ Л.А. Мачулина.

Методические указания "Приемы построения программ. Часть 1" разработаны сотрудником кафедры прикладной математики и программирования старшим преподавателем Невской Е.С.

Печатается в соответствии с решением кафедры прикладной математики и программирования факультета математики, механики и компьютерных наук ЮФУ, протокол № 4 от 18 декабря 2008 г.

Содержание

  • Введение
  • 1. Основная терминология
  • 1.1 Спецификация программ
  • 1.2 Логические средства
  • 1.3 Предусловие и постусловие
  • 1.5 Способы построения инварианта
  • 2. Примеры построения программ
  • 2.1 Выделение последовательности из массива
  • 2.2 Поиск элемента в матрице
  • 2.3 Поиск элемента в текстовом файле
  • 2.4 Обработка строк
  • 2.5 Работа с массивами
  • Литература

Введение

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

1) метода построения программы с одновременным доказательством ее правильности,

2) метода сведения к более простым задачам.

Методические указания состоят из двух частей. Первая часть методических указаний посвящена первому методу. Вторая часть методических указаний посвящена второму методу.

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

Построение программы начинается с определения спецификации задачи - что дано и что нужно найти. Затем формулируются предусловие и постусловие.

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

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

Второй метод (сведение решения задач к решению более простых задач) иллюстрируется на задачах: перестановка сегментов в массиве, "Быстрая сортировка", удаление из последовательности повторяющихся элементов. Эти задачи рассматриваются во второй части методических указаний.

программа инвариант массив спецификация

1. Основная терминология

1.1 Спецификация программ

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

Спецификация задачи - это точное, однозначное описание задачи в терминах предметной области (задачи) ([1]). Определить спецификацию - значит прежде всего подобрать точные понятия, адекватные задаче. Здесь прослеживается связь спецификации с математическими понятиями. Главное - определить понятия, используемые в спецификациях. Нужные в спецификациях понятия не всегда можно найти в математике. В области прикладной математики, связанной с обработкой большого объема данных, постоянно создаются новые понятия.

Свойство спецификаций, отличающее их от алгоритмов (и от программ) определяется так: "Спецификация описывает, что надо сделать, а не как это делать". Это значит, что спецификация не должна содержать деталей реализации. Спецификация не должна быть процедурной, динамической, то есть не должна содержать последовательности действий. Спецификация должна быть декларативной, статической.

Основными свойствами спецификации являются полнота, точность и понятность.

Полнота означает, что ничто существенное из задачи в спецификации не упущено. Точность спецификации называют также формальностью или однозначностью, причем диапазон формализации очень широк: от полностью формализованного описания до слегка формализованного.

Степень и способ формализации должны облегчать написание спецификации и понимание ее человеком. Спецификация обращена прежде всего к человеку. Один из способов специфицировать программу - выразить спецификацию на естественном языке. Но естественный язык может быть двусмысленным, поэтому необходимо обратиться к более формальной нотации. Будем использовать средства дискретной математики, а именно, логику первого порядка. Литература по этому предмету обширная, будем ссылаться на книгу Д. Гриса "Наука программирования" ([2]), так как она непосредственно связана с программированием и построением программ.

1.2 Логические средства

Логические средства необходимы, чтобы преобразовывать и упрощать высказывания и предикаты при построении программ.

Вводятся: квантор (обозначающий "существует"), квантор (обозначающий "для всех") и квантор N (обозначающий "количество").

Квантор существования:

( i: m i n: Ei) - синтаксис предложения.

Семантика такова: существует по крайней мере одно (целое) i такое, что m i n, для которого выполнено Ei, то есть принимает значение истина (true).

Квантор всеобщности:

( i: m i n: Ei) - синтаксис предложения.

Семантика такова: для всех i из области m i n Ei принимает значение истина (true).

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

(N i: m i n: Ei) - синтаксис предложения.

Семантика такова: число различных значений i из области m i n, при которых Ei принимает значение истина (true). Квантор количества N наывается квантором счета.

Квантор суммирования:

(? i: m i n: Ei) - синтаксис предложения.

Семантика такова: сумма значений, определенных индексом i из области m i n, при которых Ei принимает значение истина (true).

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

Приведем примеры использования кванторов.

Определим спецификацию для задачи поиска значения x в одномерном массиве a [1: n] на языке предикатов:

( i: 1 i n: ai = x) or ( i: 1 i n: ai x)

Если первый дизъюнктивный член имеет значение true (в этом случае

i n и ai = x), то значение x есть в массиве и его номер i.

Если второй дизъюнктивный член имеет значение true (в этом случае i n и a [i] x), то значения x нет в массиве.

Определим спецификацию для задачи определения k количества нулевых элементов массива a [1: n]:

k = (N i: 1 i n: ai = 0).

1.3 Предусловие и постусловие

Введем обозначение

{ Q } S { R },

где Q, R предикаты, S программа (оператор или последовательность операторов).

Обозначение определяет следующий смысл: "Если выполнение S началось в состоянии, удовлетворяющем Q, то имеется гарантия, что оно завершится через конечное время в состоянии, удовлетворяющем R".

Программа S называется правильной (по отношению к данным предусловию Q и постусловию R), если имеет место {Q} S {R}.

Предикат Q называется предусловием (или входным утверждением) S, предикат R постусловием (ли выходным утверждением).

В предусловии Q нужно отражать тот факт, что входные данные получили начальные значения и удовлетворяют заданным ограничениям.

Постусловие R определяет то, что нужно установить, то есть R определяет значения выходных данных и связывающие их ограничения.

Таким образом, запись {Q} S {R} можно интерпретировать так: программа S работает правильно, если перед началом ее работы Q истинно, а после окончания работы истинно R. Конечно, все это верно, если S работает не бесконечно (проблема останова) и (что самое главное) Q правильно формирует начальное состояние данных задачи перед выполнением S, а R правильно формирует конечное состояние данных задачи после выполнения S.

Проблема останова формализуется с помощью ограничивающей функции (п.1.4), а правильность Q и R надо доказывать, что в сложных задачах не является простым. Это обстоятельство является главным аргументом противников такого метода построения программ, так как доказательство правильности программы заменяется доказательством правильности спецификаций (Q и R). Если же Q и R не правильны, то полученная в результате программа будет решать совсем другую задачу.

Отметим, что {Q} S {R} предикат, то есть предложение, которое либо истинно, либо ложно. Конечно, нам нужно, чтобы он был истинен. Наша цель при написании программы доказать истинность этого предиката.

Будем называть предикат, помещенный в программу, утверждением. Утверждается, что он истинен в соответствующий момент выполнения программы. Для построения правильной программы К. Хоар вводит так называемые правила верификации основных операторов алгоритмического языка ([3]). Подробно остановимся на операторе цикла, то есть S - это " while B do S".

1.4 Инвариант цикла и ограничивающая функция

Правило верификации для оператора цикла:

"Если {P and B} S {P}, то

{P} while B do S {P and not B}"

Правило вводит важное понятие инварианта цикла.

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

Построим следующее утверждение относительно цикла while:

{ P }

while P and B do S;

{ P and not B }

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

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

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

P and not B => R, где R - постусловие.

Следовательно, нужно построить сначала not B, а затем взять его отрицание и получить B. Это метод основывается на преобразованиях статических математических выражений.

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

Тело цикла следует строить таким образом, чтобы оно уменьшало ограничивающую функцию при сохранении истинности инварианта P.

Для того чтобы понять, правильно ли построен цикл, достаточно проверить следующий список условий:

1) P истинно перед выполнением цикла;

2) выполнение цикла завершится при истинном значении P;

3) в момент завершения цикла истинен искомый результат, то есть

P and not B => R;

4) t ограничена снизу (t > 0) до тех пор, пока цикл не завершится;

5) каждый шаг цикла приводит к уменьшению ограничивающей функции t.

1.5 Способы построения инварианта

Наша задача при данных предусловии Q и постусловии R перед определением цикла построить для него инвариант и ограничивающую функцию.

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

Рассмотрим семантику инварианта цикла P:

{Q} while P and B do S {R}.

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

Будем рассматривать три способа ослабления предиката R.

Способ 1. Устранение конъюнктивного члена.

В качестве примера рассмотрим задачу линейного поиска значения x в массиве a [1: n] при условии, что x содержится в массиве.

Сформулируем постусловие.

R: (1 i n) and ( j: 1 j < i: x ? a [j]) and x = a [i])

В R содержится три конъюнктивных члена. Труднее всего установить истинность третьего конъюнктивного члена, поэтому, удаляя его, получаем инвариант:

P: (1 i n) and ( j: 1 j < i: x ? a [j])

Способ 2. Замена константы переменной. В качестве примера рассматриваются задачи 1, 5.

Способ 3. Расширение области значений переменной. В качестве примера рассматривается задача 11.

2. Примеры построения программ

2.1 Выделение последовательности из массива

Задача 1. Пусть дан упорядоченный по не убыванию элементов массив b [1: n] (n>=1). Требуется определить максимальную длину p площадки массива. Задача взята из [2].

Площадка массива - это последовательность равных значений. Длина площадки - это количество элементов в последовательности.

Входные данные: nN, b [1: n] Z.

Выходные данные: p - максимальная площадка массива b [1: n].

Метод решения. Сформулируем предусловие

Q: ( i: 1 i < n: b [i] b [i+1]) and (n>=1).

Массив из одного элемента считается упорядоченным. Определим постусловие R: значение p будем считать длиной максимальной площадки, если существует в массиве последовательность из p равных значений и нет последовательности из p+1 равных значений. Заметим, что если сегмент b [j: j+p-1] является площадкой длины p, то, следовательно, b [j] = b [j+p-1]. Запишем это определение на языке предикатов:

R: ( j: 1 j n- (p-1): b [j] = b [j+p-1]) and

( j: 1 j n-p: b [j] b [j+p])

Используя R, определим инвариант P.

Для этого заменим в R константу n переменной i и добавим границы изменения переменной i, тогда получим инвариант P:

(1 i n) and

( j: 1 j i- (p-1): b [j] = b [j+p-1]) and

( j: 1 j i-p: b [j] b [j+p]),

где p - длина максимальной площадки в массиве b [1: i].

Очевидно, что длина площадки в массиве b [1: 1] равна 1. Значит, для инициализации цикла необходимо выполнить оператор:

i: =1; p: =1;

Ограничивающая функция имеет вид t: n-i.

Охрана цикла

B: i < n.

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

i: = i+1; и i: = i+1; p: = p+1;

Определим для первого оператора условия, при выполнении которых сохраняется истинность инварианта. Для этого в логическом выражении P заменим i на i+1 и докажем истинность полученного выражения:

(1 i+1 n) and

( j: 1 j i+1- (p-1): b [j] = b [j+p-1]) and

( j: 1 j i+1-p: b [j] b [j+p])

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

Если p ? длина максимальной площадки в массиве b [1: i], то p ? длина максимальной площадки и в массиве b [1: i+1], но при условии

b [i+1-p] b [i+1].

Для второго оператора условие имеет вид:

b [i+1-p] = b [i+1].

Теперь можно сформулировать алгоритм:

i: =1; p: =1;

while i < n do

begin

if b [i+1-p] b [i+1] then i: = i+1;

if b [i+1-p] = b [i+1] then

begin i: = i+1; p: = p+1 end

end;

Опишем алгоритм в виде процедуры Plosh в следующей интерпретации: заменим два условных оператора в короткой форме одним условным оператором в короткой форме (выносим оператор i: = i+1; из условных операторов).

const n_max=20;

type T_El=integer;

vect=array [1. n_max] of T_El;

procedure Plosh (n: integer; const b: vect; var p: integer);

var i: integer;

begin

i: =1; p: =1;

while i < n do

begin

if b [i+1-p] = b [i+1] then p: = p+1;

i: = i+1

end

end;

Замечание. Тело цикла в последней интерпретации эффективней предыдущего варианта (короче и меньше операций сравнения). Но предыдущий вариант вытекает непосредственно из представленной теории и поэтому понятен.

Задача 2. Пусть дан массив b [1: n] (n>=1). Требуется выделить каждую площадку массива, определить количество площадок и максимальную длину p площадки массива.

Входные данные: n N, b [1: n] Z.

Выходные данные: площадки массива b [1: n],

k - количество площадок,

p - максимальная длина площадки.

Метод решения

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

( j: i j n-j: b [j] = b [i]),

где i - индекс очередного элемента массива.

Ограничивающая функция t: n-j.

Значит, для решения задачи имеем вложенные циклы. Сформулируем алгоритм определения только p (предыдущая задача) с помощью вложенных циклов:

i: =1; p: =0;

while i<=n do

begin

j: =i;

while (j<=n) and (b [i] =b [j]) do

j: =j+1;

p: =max (p,j-i);

i: =j

end;

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

Алгоритм решения задачи представим в виде процедуры Anal_Plosh:

procedure Anal_Plosh (n: integer; const b: vect;

var k,p: integer);

var i,j,t: integer;

begin

i: =1; p: =0; k: =0;

while i<=n do

begin

j: =i;

while (j<=n) and (b [i] =b [j]) do

begin write (b [j],' '); j: =j+1 end;

if j-i>0 then { определена площадка }

begin

if j-i>p then p: =j-i; { определение max (p,j-i); }

k: =k+1;

writeln { для вывода новой площадки }

end;

i: =j

end

end;

Задача 3. Выделить из массива последовательности, удовлетворяющие некоторому условию.

Входные данные: n N, b [1: n] Z.

логическое выражение, определяющее условие.

Выходные данные: последовательности (площадки) массива b [1: n],

k - количество последовательностей,

p - максимальная длина последовательности.

Метод решения

Для определенности будем считать, что необходимо выделить из массива неубывающие последовательности:

b [j-1] b [j] при j=i+1, i+2,…, n,

где i - индекс очередного элемента массива.

Если вести счет количества элементов последовательности с одновременной обработкой элемента (с выдачей), как это реализовано в предыдущей задаче:

j: =i+1;

while (j<=n) and (b [j-1] <= b [j]) do

begin write (b [j-1],' '); j: =j+1 end;

то последний элемент последовательности выдан не будет. Чтобы не усложнять алгоритм выдачи, оставим в цикле только счет количества элементов. Выдачу же элементов последовательности будем выполнять отдельным действием.

По окончании внутреннего циклического процесса нужно проверить, выделена ли последовательность (j-i>0). Если последовательность выделена, то выполняются следующие действия: определяются k, p и выдаются элементы последовательности.

Описание алгоритма

i: =1; p: =0; k: =0;

while i<=n do

begin

j: =i+1;

while (j<=n) and (b [j-1] <=b [j]) do

j: =j+1;

if j-i>0 then

begin k: =k+1; if j-i>p then p: =j-i;

for t: =1 to j-i do write (b [i+t-1],' ');

writeln

end;

i: =j

end;

Замечание. В этом алгоритме элементы последовательности просматриваются дважды. Первый раз ведется счет элементов последовательности (j-i) с определением индексов начала и конца последовательности (i,j). Второй раз элементы последовательности выдаются на экран.

Алгоритм решения задачи представим в виде процедуры Sequence, входными данными которой являются массив b [1: n] и булевская функция B, которая анализирует условие выбора элементов последовательности. В данной задаче в качестве фактической функции будем использовать функцию Prov, которая проверяет, находятся ли значения x и y в отношении: x y.

const n_max=30;

type vect=array [1. n_max] of integer;

func=function (x,y: integer): Boolean;

function Prov (x,y: integer): Boolean;

begin

Prov: =x <= y

end;

procedure Sequence (n: integer; const a: vect;

B: func; var k,p: integer);

var i,j,t: integer;

begin

i: =1; p: =0; k: =0;

while i<=n do

begin

j: =i+1;

while (j<=n) and (B (b [j-1],b [j])) do

j: =j+1;

if j-i>0 then

begin k: =k+1; if j-i>p then p: =j-i;

for t: =1 to j-i do write (b [i+t-1],' ');

writeln

end;

i: =j

end

end;

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

Задача 4. Определить количество площадок массива b [1: n].

Входные данные: n N, b [1: n] Z.

Выходные данные: количество площадок.

Предусловие и постусловие те же, что и в задаче 1.

Определим инвариант и ограничивающую функцию.

P: 1 i n and q = количество площадок в b [1: i-1].

t: n-i.

Алгоритм представим в виде функции Count_S:

function Count_S (n: integer; const b: vect): integer;

var i,q: integer;

begin

i: =1; q: =1;

while i<n do

begin

if b [i] <>b [i+1] then q: =q+1;

i: =i+1

end;

Count_S: =q

end;

2.2 Поиск элемента в матрице

Задача 5. Найти элемент x в матрице a [1: n,1: m]. Условие и решение задачи взяты из [2].

Входные данные: n, m N; a [1: n,1: m], x Z.

Выходные данные: если x содержится в массиве, то есть x = a [i,j],

то i? номер cтроки, j ? номер столбца;

если x отсутствует в массиве, то i = n+1.

Метод решения 1

Определим предусловие Q и постусловие R.

Q: n > 0 and m > 0.

R: ( i,j: 1 i n and 1 j m: a [i,j] = x) or

( i,j: 1 i n and 1 j m: a [i,j] x) and (i=n+1).

Если i=n+1, то это значит, что x отсутствует в матрице a.

Инвариант P утверждает, что x не принадлежит i-1 строкам и j столбцам строки i:

P: i',j': ( (1 ? i' ? i-1) and (1 ? j' ? m)) or

( (i'=i) and (1?j'<j)): (x a [i',j']) and (1?i?n+1) and (1?j?m)

Ограничивающая функция t ? число значений в не проверенной еще части массива:

t: (n - i) *m + (m - j)

Теперь определим охрану цикла B, которая должна удовлетворять условию:

P and not B => R

Для того, чтобы был истинным первый дизъюнктивный член постусловия R, достаточно

(i n) and (x =a [i,j]),

чтобы был истинным второй дизъюнктивный член R, достаточно

i = n+1.

Следовательно,

not B: (i = n+1) or (i < n+1) and (x=a [i,j]).

Предполагается, что если i = n+1, то не будет продолжено вычисление выражения not B и не будет выбираться неопределенный элемент a [i,j].

Найдем теперь B, то есть not not B, используя закон де Моргана,

B: (i n+1) and (i >= n+1 or x a [i,j]) =>(i n+1) and (x a [i,j]).

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

P and B, то есть при i < n+1, j < m+1 и x a [i,j].

Для того, чтобы перейти к проверке следующего элемента, необходимо увеличить j на 1:

j: = j+1

Этот оператор сохраняет значение истинности P при условии

j < m+1

Определим, что нужно сделать после того, когда проверится последний элемент строки, то есть при j = m. В этом случае для перехода к следующему элементу следует начать с новой строки, то есть выполнить операторы:

i: = i+1; j: = 1;

Таким образом, тело цикла имеет вид:

if j < m+1 then j: = j+1;

if j = m+1 then

begin i: = i+1; j: = 1 end;

Тело цикла может быть представлено в следующем виде:

j: = j+1;

if j = m+1 then

begin i: = i+1; j: = 1 end;

Опишем алгоритм в виде процедуры Poisk_Matr:

const nmax=10;

type T_El= integer;

matr=array [1. nmax,1. nmax] of T_El;

procedure Poisk_Matr (n,m: integer; const a: matr;

x: T_el; var i,j: integer);

begin

i: = 1; j: = 1;

while (i <> n+1) and (x <> a [i,j]) do

begin

j: = j+1;

if j = m+1 then

begin i: = i+1; j: = 1 end

end

end;

Метод решения 2

При построении алгоритма естественно воспользоваться вложенными циклами: внешний цикл по строкам (переменная i) и внутренний цикл по столбцам (переменная j). В заголовке каждого цикла необходимо проверять x a [i,j].

Описание алгоритма

i: = 1; j: =1;

while (i <= n) and (x a [i,j]) do

begin

while (j <= m) and (x a [i,j]) do

j: = j+1;

if j > m then

begin i: = i+1; j: =1 end

end;

Анализ алгоритма

Этот алгоритм менее эффективен, так как во внутреннем цикле дополнительно выполняется операция (x ? a [i,j]).

Задача 6. Найти элемент x в матрице a [1: n,1: m]. Известно, что каждый столбец и каждая строка матрицы упорядочены по не убыванию значений элементов и что x есть среди элементов матрицы.

Входные данные: n, m N; x a [1: n,1: m] Z.

Выходные данные: найти x такое, что x = a [i,j], где i? номер cтроки, j ? номер столбца.

Метод решения

Известно, что x a [1: n,1: m], то есть a [1,1] <= x <=a [n,m],

так как строки и столбцы упорядочены.

Сформулируем предусловие и постусловие.

Q: n > 0 and m > 0 and

( i: 1 i < n: ( j: 1 j m: a [i,j] a [i+1,j]) and

( j: 1 j < m: ( i: 1 i n: a [i,j] a [i,j+1])

R: ( i,j: 1 i n and 1 j m: a [i,j] = x).

Начальная установка: i: =1; j: =m; если x > a [i,j], то i: = i + 1; если x < a [i,j], то j: = j - 1; опишем алгоритм в виде процедуры Poisk_Sort, procedure Poisk_Sort (n,m: integer; const a: matr;

x: T_El; var i,j: integer);

begin

i: = 1; j: = m;

while x <> a [i,j] do

begin

if x> a [i,j] then i: = i + 1;

if x< a [i,j] then j: = j - 1

end;

end;

Замечание. В условии задачи сказано, что x есть среди элементов матрицы. Если же предположить, что x отсутствует в матрице, то логическое выражение в заголовке цикла в этом случае нужно сформулировать следующим образом:

(j > 0) and (i <= n) and (x ? a [i,j]).

В противном случае выполнение процедуры Poisk_Sort приведет к аварийной ситуации: "выход за границы диапазона изменения индекса".

2.3 Поиск элемента в текстовом файле

Задача 7. Поиск целочисленного значения x в текстовом файле.

Текстовый файл представлен в виде последовательности строк. Каждая строка состоит из последовательности целых чисел, разделенных пробелами.

Входные данные: Txt. txt - текстовый файл, x - заданное значение.

Выходные данные: i - номер строки, j - номер в строке.

Метод решения

Задача аналогична задаче поиска элемента в матрице.

Сформулируем предусловие и постусловие.

Q: файл не пуст.

R: ( i,j: 1 i n and 1 j m: x файлу) or

( i,j: 1 i n and 1 j m: x not файлу) and (i = n+1).

Если i=n+1, то это значит, что x отсутствует в файле.

Здесь n - количество строк в файле, m - количество чисел в строке.

Но n и m явно не определены при работе с текстовым файлом. Конец файла определяет стандартная функция eof (t), конец строки определяет стандартная функция eoln (t), где t - файловая переменная, связывающая файл данных Txt. txt с программой.

Опишем алгоритм в виде процедуры Search_Fl.

procedure Search_Fl (name: string; x: integer;

var i,j: integer);

var t: text;

y: integer;

begin

assign (t,name);

reset (t);

read (t,y);

i: =1; j: =1;

while not eof (t) and (x<>y) do

begin

if not eoln (t) then

begin j: =j+1; read (t,y) end

else

begin readln (t); j: =0; i: =i+1; end

end;

close (t)

end;

2.4 Обработка строк

Задача 8. Определить количество слов в строке. Слова в строке разделяются пробелами.

Входные данные: s - строка.

Выходные данные: k - количество слов.

Промежуточные переменные: n - длина строки,

i - индекс очередного символа строки.

Метод решения

Сформулируем предусловие.

Q: s ? '' and s n= ' ', где n = Length (s),

то есть строка не может быть пустой и последний символ строки должен быть пробелом.

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

Например, s='aa rrrr bbb '.

Строка содержит 3 слова.

Сформулируем постусловие.

R: k= (i: 1 i < n: si ' ' and si+1=' '),

где обозначает квантор количества.

Определим инвариант и ограничивающую функцию.

P: 1 i < n and q = число слов в s [1: i-1].

t: n-i.

Задача аналогична задаче 4. Если нужно определять количество слов без анализа слов, то алгоритм, представленный в виде функции Count_Word, является кратким и понятным.

function Count_Word (s: string): integer;

var i, k: integer;

begin

s: =s+' '; { строка должна заканчиваться пробелом }

k: =0; i: =1;

while i < Length (s) do

begin

if (s [i] <>' ') and (s [i+1] =' ') then k: =k+1;

i: =i+1

end;

Count_Word: =k

end;

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

Задача 9. Определить массив SL из слов строки. Слова в строке разделяются пробелами.

Входные данные: s - строка.

Выходные данные: k - количество слов, SL [1: k] - массив слов.

Метод решения

Предусловие то же самое, что и в задаче 6:

Q: s ? '' and s [Length (s)] = ' ',

то есть строка не может быть пустой и последний символ строки должен быть пробелом.

Слово - это последовательность символов, отличных от пробела. Пробел является признаком конца слова. Значит, слово определяется так:

R: Slovo = (? i: m i r: si) and (m r) and (r n),

то есть Slovo = Slovo + si при этом i из области m i r,

где si ? ' ', m - позиция начала слова, r - позиция конца слова.

Алгоритм опишем в виде процедуры Arr_Slov.

const n_max=20;

type T_El=string;

vect=array [1. n_max] of T_El;

procedure Arr_Slov (s: string; var k: integer; var SL: vect);

var i, n: integer;

Slovo: string;

begin

s: =s+' ';

n: =Length (s);

k: =0;

i: =1;

while i <= n do

begin

Slovo: ='';

while s [i] <> ' ' do

begin

Slovo: = Slovo+s [i]; i: =i+1

end;

if Slovo<>'' then

begin

k: =k+1; SL [k]: = Slovo

end

end

end;

Замечание. Этот алгоритм эффективней по сравнению с алгоритмом задачи 2 (3) выделения последовательности из массива, так как вложенный цикл содержит простое условие выхода из цикла s [i] <> ' ', в задаче 2 имеем составное условие: (j<=n) and (b [i] =b [j]). Такой эффект достигается благодаря тому, что последнее слово так же, как и все предыдущие слова, имеет признак конца слова - символ пробел.

Задача 10. Определить количество вхождений в строку заданной подстроки.

Входные данные: s - строка, w - подстрока.

Выходные данные: k - количество вхождений.

Метод решения

Сформулируем предусловие и постусловие.

Q: s ? '' and w ? ''

то есть строка и подстрока не могут быть пустыми.

R: k= (i: 1 i< Length (s): (j: 1 j Length (w): wj = si+j-1)).

Введем операцию Index, с помощью которой будем определять индекс Ind вхождения подстроки w в строку s, просматривая строку s с позиции i:

function Index (w,s: string; i: integer): integer;

Если w не принадлежит s, то значение функции Index равно 0.

Описание алгоритма

k: =0; n: =Length (w);

Ind: =Index (w,s,1);

while Ind<>0 do

begin

k: =k+1;

Ind: =Index (w,s, Ind+n)

end;

Решение подзадачи задачи 10 - операция Index

Сформулируем предусловие и постусловие.

Q: s ? '' and w ? ''

R: (i: 1 i < Length (s): (j: 1 j Length (w): wj = si+j-1))

Or (i: 1 i < Length (s): (j: 1 j Length (w): wj ? si+j-1))

По аналогии с задачей поиска элемента в матрице (задача 5) опишем алгоритм в виде функции Index.

function Index (w,s: string; i: integer): integer;

var j: integer;

L,K: integer; {длины s и w}

begin

L: =Length (s); K: =Length (w);

j: =1;

while (i<L) and (j<=K) do

if (s [i+j-1] =w [j]) then j: =j+1

else begin i: =i+1; j: =1 end;

if j>K then Index: =i else Index: =0

end;

Возможен и такой вариант циклического процесса (по аналогии с алгоритмом Poisk_Matr):

while (i<L) and (j<=K) do

begin

if s [i+j-1] ? w [j] then

begin j: =1; i: =i+1 end;

if s [i+j-1] = w [j] thenj: =j+1

end;

2.5 Работа с массивами

Задача 11. Пусть даны три упорядоченных по возрастанию элементов массива. Известно, что, по крайней мере, один элемент есть во всех трех массивах. Определить первый такой элемент. (Условие задачи и ее решение взяты из книги Д. Гриса [2]).

Входные данные: f [1: nf], g [1: ng], h [1: nh] Z, nf, ng, nh N.

Выходные данные: in, jn, kn - индексы наименьшего значения, удовлетворяющего условию:

f [in] = g [jn] = h [kn].

Промежуточные данные: i, j, k - текущие индексы соответственно в массивах f, g, h.

Метод решения

Предусловие Q - это условие упорядоченности каждого массива.

Постусловие представим в следующем виде:

R: (i = in) and (j = jn) and (k = kn),

что равносильно условию:

f [in] = g [jn] = h [kn].

Для решения задачи используем линейный поиск, который предполагает просмотр массивов, начиная с нижней границы. Расширение области значений переменных i, j, k приводит к инварианту:

P: (1 i in) and (1 j jn) and (1 k kn).

Ограничивающая функция имеет вид:

t: in-i + jn-j + kn-k

Исходя из P, инициализация цикла осуществляется операторами:

i: = 1; j: =1; k: =1;

Для уменьшения ограничивающей функции необходимы операторы:

i: = i + 1, j: = j + 1, k: = k + 1

Для каждого оператора требуется построить подходящую охрану.

Построим охрану для первого оператора. Для этого в инварианте P заменим i на i + 1, получим

(1 i+1 in) and (1 j jn) and (1 k kn)

Из инварианта следует истинность отношения 1 i + 1 и последних двух конъюнктивных членов. Но охраной не может служить отношение

i + 1 in,

так как переменная in обозначает конкретный искомый результат и в алгоритме не будет использоваться.

Но это отношение и инвариант P означают, что f [i] не является искомым элементом, что верно при условии

f [i] < g [j].

Последнее отношение может использоваться как охрана. Ослабим охрану, добавляя отношение f [i] < h [k].

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

B1: (f [i] < g [j]) or (f [i] < h [k]).

Аналогично сформулируем охраны для двух следующих операторов:

B2: (g [j] < h [k]) or (g [j] < f [i]).

B3: (h [k] < f [i]) or (h [k] < g [j]).

Сформулируем алгоритм. Описание алгоритма

i: = 1; j: =1; k: =1;

while not ( (f [i] = g [j]) and (g [j] = h [k])) do

begin

if (f [i] < g [j]) or (f [i] < h [k]) then i: = i+1;

if (g [j] < h [k]) or (g [j] < f [i]) then j: = j+1;

if (h [k] < f [i]) or (h [k] < g [j]) then k: = k+1

end;

Покажем, что в момент завершения цикла истинным является значение R, т.е. выполняется

P and not BB => R, где BB = B1 or B2 or B3.

Вычислим not BB. Для этого вычислим отрицание первых дизъюнктивных членов каждой охраны:

not BB ? (f [i] g [j]) and (g [j] h [k]) and (h [k] f [i]) ?

? f [i] g [j] h [k] f [i] ? f [i] = g [j] = h [k] => R.

Заметим, что для доказательства завершения цикла нужны только первые дизъюнктивные члены охран, значит, вторые дизъюнктивные члены можно опустить. Окончательный алгоритм опишем в виде процедуры Value:

const n_max=20;

type T_El=integer;

vect=array [1. n_max] of T_El;

function Value (const f,g,h: vect): T_El;

var i,j,k: integer;

begin

i: = 1; j: =1; k: =1;

while not ( (f [i] = g [j]) and (g [j] = h [k])) do

begin

if (f [i] < g [j]) then i: = i+1;

if (g [j] < h [k]) then j: = j+1;

if (h [k] < f [i]) then k: = k+1

end;

Value: =f [i]

end;

Таким образом, получили алгоритм короче и эффективней. Но, в общем, надо иметь в виду: чем слабее охраны, тем легче построить правильный алгоритм.

Замечания.

1) Алгоритм не изменится, если массивы представляют последовательность символов или строк, или вещественных чисел. Изменить требуется только тип элемента T_El.

2) В задаче предполагается, что значение обязательно присутствует в трех массивах (верхние границы при этом не участвуют в алгоритме). Если мы хотим обезопасить алгоритм от зацикливания, то необходимо усложнить логическое выражение в заголовке цикла, то есть добавить выражение:

(i<=nf) and (j<=ng) and (k<=nh),

где nf, ng, nh - верхние границы массивов f,g,h.

3) По условию задачи требуется найти значение Value, присутствующее в трех массивах. Если необходимо найти индекс значения в каждом из массивов, то i,j,k станут выходными параметрами.

Опишем соответствующий алгоритм в виде процедуры Ind_Value.

const n_max=20;

type T_El=integer;

vect=array [1. n_max] of T_El;

procedure Ind_Value (nf,ng,nh: integer; const f,g,h: vect;

var i,j,k: integer);

begin

i: = 1; j: =1; k: =1;

while (i<=nf) and (j<=ng) and (k<=nh) and

not ( (f [i] = g [j]) and (g [j] =h [k])) do

begin

if (f [i] < g [j]) then i: = i+1

else

if (g [j] < h [k]) then j: = j+1

{if (h [k] < f [i]) then }

else k: = k+1

end;

if not ( (i<=nf) and (j<=ng) and (k<=nh)) then

begin i: = 0; j: =0; k: =0 end

end;

Задача 12. Даны два массива, упорядоченных по возрастанию элементов: a и b из n и m элементов соответственно. Определить количество значений, встречающихся как в массиве a, так и в массиве b. (Условие задачи и ее решение взяты из [2]).

Входные данные: n,m N, a [1: n], b [1: m] Z.

Выходные данные: k - количество встречающихся значений в обоих массивах.

Метод решения

Сформулируем предусловие

Q: ( i: 1 i < n: a [i] < a [i+1]) and (n>=1) and

( i: 1 j < m: b [j] < b [j+1]) and (m>=1).

Массив из одного элемента считается упорядоченным.

Сформулируем постусловие

R: k= (i,j: 1 i n and 1 j m: a [i] = b [j]).

Построим инвариант P, расширяя область значений переменных i,j и пользуясь тем, что массивы упорядочены:

P: 1 p n and 1 q m and

(k= (i,j: 1 i p and 1 j q: a [i] = b [j])) and

b [1. j-1] <a [i] and a [1. i-1] <b [j].

Ограничивающая функция t: m-p+n-q.

Алгоритм решения задачи представим в виде функции Count.

const n_max=20;

type T_El=integer;

vect=array [1. n_max] of T_El;

function Count (n,m: integer; const a,b: vect): integer;

var p,q,k: integer;

begin

p: =1; q: =1; k: =0;

while (p<=n) and (q<=m) do

begin

if a [p] <b [q] then p: =p+1;

if a [p] =b [q] then

begin p: =p+1; q: =q+1; k: =k+1 end;

if a [p] >b [q] then q: =q+1

end;

Count: =k

end;

Замечание. Тело циклического процесса может быть представлено одним условным оператором, что менее понятно, но более эффективно:

if a [p] <b [q] then p: =p+1

else if a [p] =b [q] then

begin p: =p+1; q: =q+1; k: =k+1 end

else q: =q+1;

Литература

1. Агафонов В.Н. Спецификация программ. - Новосибирск: Наука, 1990. - 222 с.

2. Грис Д. Наука программирования. - М.: Мир, 1984. - 416 с.

3. Минакова Н.И. Методы программирования. Учебное пособие / Н.И. Минакова, Е.С. Невская, Г.А. Угольницкий, А.А. Чекулаева, М.И. Чердынцева. Методы программирования. - М.: Вузовская книга, 2000. - 280 с. - ISBN 5-89522-038-X.

4. Невская Е.С., Чекулаева А.А., Чердынцева М.И. Искусство программирования / Под ред. проф. Г.А. Угольницкого. - М.: Вузовская книга, 2002. - 240 с. - ISBN 5-9502-0003-9.

Размещено на Allbest.ru

...

Подобные документы

  • Информационные технологии в создании обучающих программ. Принципы построения тестирующих программ. Программы по высшей математике: ODE; Формула; "Математика". Методы решения дифференциальных уравнений в символьном виде. Модульность программного средства.

    дипломная работа [488,2 K], добавлен 08.06.2011

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

    курсовая работа [505,3 K], добавлен 19.11.2013

  • Средства интегрированной среды Microsoft Visual Studio, предоставляемые программисту для реализации программ на языке С++. Особенности стиля написания программ. Типовые приемы и методы создания и отладки программ. Листинги программ и их тестирование.

    лабораторная работа [814,3 K], добавлен 26.05.2013

  • Среда Borland Delphi и ее графические средства для построения фрактальных множеств. Разработка программы для построения изображения листа папоротника при помощи вероятностных распределений с использованием средств для отображения графической информации.

    курсовая работа [1,3 M], добавлен 29.07.2013

  • Проектирование программ в среде Рascal с интерфейсом типа "Меню". Разработка и отладка программы сортировки массива данных. Освоение методов проектирования Pascal-программ с использованием графических процедур и функций из стандартного модуля Graph.

    контрольная работа [581,1 K], добавлен 16.01.2015

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

    статья [19,8 K], добавлен 08.12.2016

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

    презентация [976,8 K], добавлен 21.05.2019

  • Основные направления развития параллелизма, модели параллельного программирования. Автоматические средства разработки параллельного ПО, анализ последовательной программы. Разработка системы автоматического распараллеливания программ на языке Fortran77.

    дипломная работа [57,7 K], добавлен 14.10.2010

  • Методика разработки внешних спецификаций программ, основанных на использовании HIPO-технологии проектирования программ. Приобретение практических навыков определения и оформления внешних спецификаций программ. Схема состава разложения и IPO-диаграммы.

    лабораторная работа [45,6 K], добавлен 15.03.2009

  • Эволюция вирусных систем. Проблема вредоносных программ - рекламных и шпионских. Компьютерный вирус как разновидность компьютерных программ. Шпионское программное обеспечение. Признаки заражения компьютера вирусом. Методы защиты от вредоносных программ.

    реферат [35,9 K], добавлен 19.10.2010

  • Защищенность информационной среды. Драйвера, их интеграция и идеология построения. Обеспечение безопасности компьютеров. Контроль работоспособности драйверов устройств. Метод обнаружения странного поведения программ. Работа антивирусных программ.

    отчет по практике [665,4 K], добавлен 10.12.2011

  • Первый прототип вируса. Идея создания самовоспроизводящихся программ. Разработка вирусоподобных программ. Основные признаки проявления вирусов. Классификация компьютерных вирусов. Рынок антивирусных программ. Основные виды антивирусных программ.

    презентация [1,8 M], добавлен 25.10.2012

  • Особенности разработки программ для ЭВМ. Этапы планирования программы. Понятие и особенности алгоритмов. Средства, используемые для создания программ. Виды и классификация языков программирования. Структурное и объектно-ориентированное программирование.

    реферат [59,7 K], добавлен 19.08.2010

  • История развития вирусов и антивирусов. Классификация антивирусных программ. Методы работы антивирусных программ. Другие методы работы антивирусных программ. Сравнение антивирусов: SymantecNortonAntivirus 2005; антивирус Касперского Personal; DoctorWeb.

    реферат [28,8 K], добавлен 22.06.2019

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

    курсовая работа [462,8 K], добавлен 05.04.2014

  • Схема разбора арифметического и логического выражения. Внешняя спецификация конвертора и алгоритм перевода программ на языке Паскаль в текст на языке Си. Назначение подпрограмм, особенности констант и переменных. Код программы и ее тестирование.

    курсовая работа [567,5 K], добавлен 03.07.2011

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

    курсовая работа [1,4 M], добавлен 25.04.2012

  • Выбор языка манипулирования данными. Построение концептуальной модели предметной области и проектирование концептуальной схемы БД. Методы построения форм и отчетов на примере построения программы ведения электронной документации учебного заведения.

    курсовая работа [446,3 K], добавлен 21.09.2013

  • Особенности разработки программ на языке Turbo Pascal на примере программы обработки массива данных с построением диаграммы. Функции программы и основные требования к ней. Состав входных и выходных данных. Использование предметной области "Садовод".

    курсовая работа [789,1 K], добавлен 13.03.2013

  • Классификация групп стандартов. Основные виды программ и программных документов: спецификация, ведомость держателей подлинников, текст и описание программы, методика испытаний. Содержание эксплуатационных документов. Руководство системного программиста.

    презентация [97,7 K], добавлен 27.12.2013

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.