Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Костюк - Основы программирования

.pdf
Скачиваний:
134
Добавлен:
30.05.2015
Размер:
1.3 Mб
Скачать

11

чаи их некорректности. Чтобы выявить, как программа справляется с такими ситуа­

циями, необходимо множество (I \ P)

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

Rk (I \ P) , и внутри каждой подобласти

Rk задать дополнительные тесты tk Rk .

Тогда все множество тестов, разработанных по методу черного ящика, будет множе­ ством {ti } {t j } {tk } I .

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

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

Пример 1.2. Рассмотрим программу вычисления площади треугольника по за­ данным на его входе длинам трех сторон a, b и c. Пусть алгоритм составлен таким образом, что он выдает площадь, равную нулю, если для заданных на входе значений невозможно построить треугольник.

Вначале выделим подобласти Rk :

1)a ≤ 0;

2)b ≤ 0, a > 0;

3)c ≤ 0, a > 0, b > 0;

4)a > 0, b > 0, c a + b;

5)a > 0, c > 0, b a + c;

6)b > 0, c > 0, a b + c.

Остальные подобласти принадлежат области входов, поэтому для них выполня­ ется общее соотношение: a > 0, b > 0, c > 0, c < a + b, b < a + c, a < b + c. Можно вы­ делить подобласти, разделяющие треугольники на различные типы. Равносторонние треугольники:

7) a = b = c.

Равнобедренные треугольники:

8)a = b c;

9)a b = c;

10)a = c b.

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

11)a2 = b2 + c2;

12)b2 = a2 + c2;

13)c2 = a2 + b2;

12

тупоугольные:

14)a2 > b2 + c2;

15)b2 > a2 + c2;

16)c2 > a2 + b2;

и один остроугольный треугольник: 17) a2 < b2 + c2, b2 < a2 + c2, c2 < a2 + b2.

Что касается выбора конкретных чисел для тестов, то будем подбирать такие числа, для которых легче всего вручную вычислить результат. Всего для этого при­ мера будет 23 теста: 6 внутренних точек подобластей Rk , 6 границ подобластей Rk , 11 внутренних точек подобластей Pi . Здесь нет необходимости задавать тесты на основе разделения области выходов Q на подобласти. В табл. 1.1 приведены полу­ ченные тесты (площадь треугольника обозначена через S):

Таблица 1.1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

 

теста

 

a

–3

1

1

0

1

1

1

1

3

1

1

2

1

5

6

5

5

4

3

4

2

3

6

b

 

1

–3

1

1

0

1

1

3

1

1

2

1

1

5

5

6

4

5

4

3

4

2

5

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

c

 

1

1

–3

1

1

0

3

1

1

2

1

1

1

6

5

5

3

3

5

2

3

4

4

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

S

 

0

0

0

0

0

0

0

0

0

0

0

0

)*

12

12

12

6

6

6

)**

)**

)**

)***

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

)* – число 3 / 4 0,433, )** – число ≈ 2,905, )*** – число ≈ 9,922.

Конец примера.

Пример 1.3. Рассмотрим программу упорядочения числового массива. На его вход подается количество элементов массива n и значения самих элементов масси­ ва X : X [1], X [2], …, X [n]. На выходе программа выдает значения тех же элементов массива X , но переставленные таким образом, чтобы они шли в порядке возраста­ ния (или, точнее, неубывания).

Предположим, что операция, используемая для определения того, какой из двух элементов массива должен иметь меньший номер номер на выходе – сравнение. В та­ ком случае сами конкретные значения элементов не важны, важно лишь соотноше­ ние между ними. Подобласть с недопустимыми значениями на входе программы – пустой массив, когда n ≤ 0. Остальные подобласти определяются при n ≥ 1. Следует также учесть ограниченность n, так как размер массива конечен. Должно быть зада­ но максимально возможное количество элементов в массиве nмакс, и тогда n nмакс . Тогда можно выделить nмакс + 2 подобласти для различных n:

1)n < 0;

2)n = 0;

13

3)n = 1;

4)n = 2;

. . .

nмакс + 2) n = nмакс .

Для каждой из подобластей с n ≥ 2 можно, в свою очередь, выделить следую­ щие подобласти Qj в области выходов:

1)значения всех элементов в массиве X равны между собой;

2)все значения массива X различны: нет ни одной пары одинаковых элементов;

3)количество разных значений массива X меньше n, т.е. элементы массива об­ разуют несколько групп с равными внутри группы значениями.

Наконец, для 2-й и 3-й подобластей Qj можно выделить по нескольку подобла­ стей входов Pi . Но таких подобластей огромное количество, только для 2-й подобласти Qj (для всех n различных значений массива X) число вариантов разме­ щения равно n! = 1∙2∙…∙n . Поэтому приходится существенно ограничивать число выбираемых вариантов даже при небольших n.

Таким образом, тестирование по методу черного ящика может давать столь много вариантов тестов, что их невозможно перебрать даже автоматически. Действи­ тельно, пусть n = 100, что совсем немного (на практике нередко приходится упоря­ дочивать массивы длиной в 106 элементов и более). Нетрудно подсчитать, что 100! ≈ 10158. Если на компьютере перебирать по 109 тестов в секунду, то на это по­ требуется более 10140 лет, что в 10130 раз дольше, чем время существования Вселен­ ной!

Поэтому ограничимся всеми возможными тестами только при очень малых n (n = 1, 0, 1, 2), небольшим числом тестов при каком-либо не слишком большом n,

например, при n = 6, а также одним тестом при n = nмакс . Тесты (кроме последнего) представлены в табл. 1.2.

Таблица 1.2

Номер теста

n

 

Массив X на входе

 

Массив X на выходе

1

–1

 

 

2

0

 

 

3

1

1

 

1

 

4

2

1

1

1

1

5

2

1

2

1

2

6

2

2

1

1

2

7

6

1 1 1 1 1 1

1 1 1 1 1 1

8

6

1 2 3 4 5 6

1 2 3 4 5 6

9

6

6 5 4 3 2 1

1 2 3 4 5 6

10

6

5 1 2 6 4 3

1 2 3 4 5 6

11

6

1 1 2 2 2 3

1 1 2 2 2 3

12

6

3 2 2 2 1 1

1 1 2 2 2 3

14

13

6

1 3 2 2 1 2

1 1 2 2 2 3

Тест для n = nмакс вручную ввести практически невозможно, поэтому его придет­ ся задавать автоматически, специальной тестирующей программой. Такая программа может, например, генерировать случайные значения из некоторого заданного диапа­ зона и присваивать их элементам массива. При этом проверка правильности ре­ зультата также должна выполняться автоматически, специальной проверочной про­ граммой.

В целом алгоритм тестирования для n = nмакс может быть следующим:

1)генерация nмакс случайных значений и запись их в массив X;

2)копирование элементов массива X в массив Y;

3)упорядочение элементов массива X проверяемой программой;

4)проверка массива X на упорядоченность;

5)сравнение элементов массива X с элементами массива Y: для каждого значе­ ния, имеющегося в X, в массивах X и Y должно быть одинаковое количество эле­ ментов.

Конец примера.

1.3Тестирование методом белого ящика

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

1)последовательной;

2)ветвящейся;

3)циклической.

15

Последовательный алгоритм состоит из выполняемых друг за другом элементов, например A и B на рис. 1.3. На языке Паскаль последовательный алгоритм можно представить последовательно выполняющимися операторами A и B, возможно, взя­ тыми в операторные скобки:

 

begin A; B end

(1.1)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 1.3

Ветвящийся алгоритм представлен на рис. 1.4. Первое выполняемое в нем дей­ ствие – проверка некоторого условия E (логического выражения). Если значение условия E оказалось ложным, то далее выполняется элемент A, а если истинным, то B.

Рис. 1.4 На языке Паскаль ветвящийся алгоритм можно представить условным операто­

ром, внутри которого, в свою очередь, имеются операторы A и B:

if E then A else B

(1.2)

Циклический алгоритм представлен на рис. 1.5. Здесь, так же как в ветвящемся алгоритме, первое выполняемое действие – проверка условия E. При ложном E происходит выход из алгоритма, а при истинном E выполняется элемент A, после чего вновь проверяется условие E. В зависимости от того, как в процессе выполне­ ния алгоритма изменяется значение условия E, элемент A может выполняться 0, 1, 2 и более раз. Если значение условия E всегда остается истинным, процесс выпол­ нения циклического алгоритма никогда не прекратится. Тогда говорят, что алгоритм зациклил. Конечно, необходимо не допускать такой ситуации, так как компьютер, ис­ полняющий зацикленный алгоритм, не может его завершить самостоятельно.

16

Рис. 1.5

На языке Паскаль циклический алгоритм можно представить оператором цикла, внутри которого, в свою очередь, имеется оператор A:

while E do A

(1.3)

Во всех трех видах алгоритмов элементы A и B сами являются алгоритмами, так как в каждом из них имеется только один вход и только один выход. Однако условие E не есть алгоритм, так как у него два выхода. Поэтому дальнейшей де­

композиции можно подвергать, при необходимости, A и B, но не E.

 

Пусть P0 – вход, Q0 – выход всего алгоритма; PA – вход, QA

– выход

A; PB

вход, QB – выход B. Тогда для последовательного алгоритма:

 

 

PA = P0 ; PB = QA ; QB = Q0 .

 

(1.1)

Для ветвящегося алгоритма:

 

 

PA = P0 , E = истина;

 

 

PB = P0 , E = ложь;

 

(1.2)

Q0 = (QA или QB) .

 

 

Пусть для циклического алгоритма PA(i) – вход, QA(i) – выход

A, E(i)

– условие

перед i–м исполнением A. Тогда:

 

 

PA(1) = P0 , E(1) = истина;

 

 

Q0 = P0 , E(1) = ложь;

 

 

PA(i+1) = QA(i), E(i+1) = истина;

 

(1.3)

Q0 = QA(i), E(i+1) = ложь; при i ≥ 1.

 

 

Тестирование методом белого ящика состоит в разработке такого набора тестов, чтобы каждое условие в программе проверялось не меньше, чем двумя тестами, причем один тест должен давать истинное условие, а другой – ложное, чтобы каждый элемент алгоритма исполнялся при выполнении хотя бы одного теста. Если же неко­ торый элемент алгоритма может исполняться многократно, то необходимо, чтобы было не менее, чем по одному тесту для каждой последовательности многократных исполнений этого элемента.

17

Из этого следует, что для тестирования последовательного алгоритма достаточно одного теста, ветвящегося алгоритма – двух тестов, а циклического алгоритма – столько тестов, сколько раз может исполняться в нем элемент A, т.е. сколько раз мо­ жет повторяться цикл. Это количество тестов достаточно, если элементы A и B ранее проверены, и их не требуется дополнительно тестировать. На практике отдельно те­ стировать каждый элемент алгоритма не всегда удобно, обычно рассматривают сразу несколько вложенных элементов. Но тогда количество тестов может существенно увеличиться. Рассмотрим пример вложенности ветвящихся алгоритмов (1.4).

if E1 then begin

 

if

E2 then A1

else A2

 

end

 

 

(1.4)

else

begin

 

 

 

if

E3 then B1

else B2

 

end

 

 

 

Здесь каждый из элементов A и B, в свою очередь, является ветвящимся алго­ ритмом. Для этого примера необходимо 4 теста, в которых должно выполняться:

1)E1 = истина, E2 = истина;

2)E1 = истина, E2 = ложь;

3)E1 = ложь, E3 = истина;

4)E1 = ложь, E3 = ложь.

В алгоритме (1.5) внутри циклического алгоритма содержится ветвящийся алго­ ритм. В нем для различных n выполнений цикла потребуется по 2n тестов с различ­ ными последовательностями значений для условия E2 :

while E1 do

 

 

begin

 

(1.5)

if E2 then A

else B

 

end

 

 

Алгоритм (1.5) показывает, что полное тестирование методом белого ящика на практике невозможно, поэтому приходится проверять не все варианты исполнения совокупности элементов алгоритма. Однако в любом случае для каждого элемента алгоритма, который может при одних условиях исполняться, а при других – нет, необходимо по два таких теста, чтобы один тест задавал условия исполнения этого элемента, а другой тест – условия неисполнения.

Пример 1.4. Как в примере 1.2, рассмотрим задачу вычисления площади тре­ угольника по заданным на его входе длинам трех сторон a, b и c. Пусть алгоритм, в котором используется формула Герона, записан следующим образом:

S:=0;

18

if a>0 then if b>0 then

if c>0 then

begin p:=(a+b+c)/2;

if p>a then (1.6) if p>b then

if p>c then S:=sqrt(p*(p-a)*(p-b)*(p-c))

end

Из записи алгоритма следует набор тестов, представленный в табл. 1.3.

Таблица 1.3

Номер теста

1

2

3

4

5

6

7

a

0

1

1

2

1

1

1

b

1

0

1

1

2

1

1

c

1

1

0

1

1

2

1

S

0

0

0

0

0

0

 

 

 

3

/ 4 0,433

Если сравнить табл. 1.1 и табл. 1.3, то можно заметить, что метод белого ящика более полно проверяет конкретный алгоритм, но совершенно не анализирует саму исходную задачу. Предположим, что алгоритм решения этой задачи иной:

p:=(a+b+c)/2;

(1.7)

S:=sqrt(p*(p-a)*(p-b)*(p-c))

 

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

Конец примера.

Пример 1.5. Как в примере 1.3, рассмотрим задачу упорядочения числового мас­

сива X из n элементов. Пусть алгоритм решения этой задачи следующий:

 

 

 

 

 

 

i:=1;

 

 

 

while i<n do

 

 

 

if X[i]<=X[i+1]

 

 

 

then i:=i+1

 

(1.8)

 

else begin

 

 

 

 

 

z:=X[i];X[i]:=X[i+1]; X[i+1]:=z

 

 

 

i:=1

 

 

 

end

 

 

Этот алгоритм из-за его простоты называют простейшим алгоритмом сортиров­ ки. Для его проверки можно предложить следующие тесты (табл. 1.4).

19

Таблица 1.4

Номер теста

N

Массив X на входе

 

Массив X на выходе

1

1

1

 

1

 

2

2

1

1

1

1

3

2

2

1

1

2

4

6

1 1 1 1 1 1

1 1 1 1 1 1

5

6

6 5 4 3 2 1

1 2 3 4 5 6

Тест № 1 сразу задает ложное условие в заголовке цикла, и тот ни разу не испол­ няется. В тесте № 2 цикл исполняется (1 раз), условие внутри цикла будет истинно. В тесте № 3 цикл исполняется, но условие внутри цикла вначале будет ложно. Тесты

4 и № 5 задают многократное исполнение цикла, при этом условие внутри цикла

4 всегда будет истинно, а внутри цикла № 5 вначале будет ложно.

Предположим, что алгоритм (1.8) записан с ошибкой: в третьей строчке вместо операции ”<=” записана операция ”<”. Для такого ошибочного алгоритма по мето­

ду белого ящика вместо теста № 2 будет тест со следующим массивом X на входе: (1, 2), а вместо теста № 4 тест с массивом X на входе: (1, 2, 3, 4, 5, 6). Алгоритм на этих тестах исполнится без ошибок, но если бы ему был задан тест № 2 из табл. 1.4, алгоритм бы зациклил, и ошибка была бы обнаружена.

Конец примера.

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

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

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

значений. Например, 16-ти битный тип integer позволяет представлять числа в пределах от –215 = –32768 до 215 – 1 = 32767, а 32-х битный тип longint в Turbo

Pascal – от –231 = –2147483648 до 231 – 1 = 2147483647. Поэтому важно знать, для ка­ ких значений входных данных во время вычислений промежуточные значения не выйдут за допустимые пределы. На практике положение осложняется тем, что, как правило, выход целочисленных значений за допустимые пределы (переполнение) ни­ как не фиксируется, и вычислительный процесс продолжается с неверными значени­ ями. Из-за этого операция, которая первой вычислила неправильное значение, оста­ нется неизвестной. При этом довольно сложно подобрать тесты, выявляющие пре­ дельные значения входных данных, при которых не будет переполнения.

20

Хотя при выполнении операций над вещественными числами переполнение по­

лучить труднее (для 32-х битных вещественных типа single в Turbo Pascal значе­ ние находится в пределах ± 3.4∙1038, а для 80-ти битных типа extended в Turbo

Pascal – в пределах ± 1.1∙104932), но иногда оно также происходит. Гораздо больше проблем вызывает ограниченная точность вычислений, так как вещественные числа представляются приближенно, с округлением. Например, при вычислении по фор­ муле x:=1/3*3 никогда не получится результат x=1. При написании алгорит­ мов это обязательно надо учитывать, например, сравнение двух значений x и y, необходимо производить не операцией “=”, а с помощью выражения abs(x-y)<e, где величина e должна быть достаточно малой. Кроме того, следует учитывать накопление ошибок округления. Полный анализ величин ошибок вычис­

лений весьма сложен, для этого необходимо учитывать относительную точность представления вещественных чисел (для типа single около 7, а для типа ex­

tended около 19 верных десятичных цифр).

Конец замечания.

1.4 Отладка программ

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

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

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