- •Ответы на экзаменационные вопросы
- •1. Функциональность программного средства (functionality)
- •Оценивание качества разработки программ на основе метрик Холстеда. Измеримые свойства алгоритмов
- •Длина программы
- •Объем программы
- •Потенциальный объем V*
- •Значение уровня языка
- •{Ri (ej)} e -event.
- •Модель Джелинского-Моранды.
- •Модель Шика-Уолвертона.
- •Модель Нельсона. Применение последовательного анализа Вальда для снижения количества прогонов программы.
Объем программы
Важной характеристикой программы является ее размер. При переводе алгоритма с одного языка на другой размер программы будет меняться. Изучение этих изменений количественными методами требует, чтобы размер был измеримой величиной.
Кроме того, метрическая характеристика размера должна быть применима к широкому кругу языков без потери общности и объективности и, следовательно, не должна зависеть от набора символов, требуемых для представления алгоритма, то есть символов, практически используемых для записи операторов или имен операндов.
Решение этой проблемы связано с тем, что можно определить абсолютный минимум длины представления самого длинного оператора или имени операнда. Минимальная длина зависит только от числа элементов в словаре . В общем случае log2 есть минимальная длина (в битах) всей программы. Под битом здесь понимается логическая единица информации – символ, оператор, операнд – то, чем оперирует программист при создании программы.
Соответствующая метрическая характеристика размера любой реализации какого-либо алгоритма, называемая объемом V, может быть определена как
V = N log2 (2.2)
где N = N1 + N2 -длина реализации; а = 1 + 2 - ее словарь.
Очевидно, что если данный алгоритм переводится с одного языка на другой, то его объем меняется. Например, при переводе алгоритма с Паскаля в машинный код какой-либо конкретной машины его объем увеличится. С другой стороны, выражение алгоритма на более развитом языке, нежели тот, на котором он исходно написан, приведет к уменьшению его объема. Последняя возможность чрезвычайно важна и заслуживает специального рассмотрения.
Потенциальный объем V*
Выражение алгоритма в наиболее сжатой форме предполагает существование языка, в котором требуемая операция уже определена или реализована, возможно, в виде процедуры или подпрограммы. Для реализации алгоритма в таком языке требуются лишь имена операндов для его аргументов и результатов.
Обозначив соответствующие программные параметры возможно кратчайшей или наиболее сжатой формы алгоритма звездочками, из уравнения (2.1) получим, что минимальный (или потенциальный ) объем равен
V* = (N1* + N2*) log2(1* + 2*) (2.3)
Но в минимальной форме ни операторы, ни операнды не требуют повторений, поэтому
N1* = 1* ; N2* = 2*
что дает нам
V* = (1* + 2*) log2(1* + 2*)
Кроме того, известно минимально возможное число операторов 1* для любого алгоритма. Каждый алгоритм должен включать один оператор для имени функции или процедуры и один в качестве символа присваивания или группировки, т.е. минимальное значение 1*=2.
Уравнение теперь примет вид:
V* = (2+ 2*) log2( 2+ 2*) , (2.4)
где 2* должно представлять собой число различных входных и выходных параметров.
Из уравнения (2.4) следует, что потенциальный объем V* любого алгоритма не должен зависеть от языка, на котором он может быть выражен. Если 2* расценивается как число единых по смыслу (неизбыточных) операндов, то V* оказывается наиболее полезной мерой содержания алгоритма.
Вернемся к примеру программы для алгоритма Евклида и определим его объем для программ на Паскале и СИ.
Паскаль: V =N * log2 = 61* log2 18 = 254.4 бита.
СИ: V =N * log2 = 55* log2 18 = 224.8 бита.
Чтобы найти потенциальный объем, нам нужно подсчитать число требуемых входных и выходных параметров. В данном случае это А, В и GCD, так что 2*=3. Следовательно, потенциальный объем:
V* = (2 +2*) log2( 2 + 2*) = (2+3) log2(2+3) = 11,6 бита
Как уже упоминалось выше, при переводе алгоритма с одного языка на другой его потенциальный объем V* не меняется, но действительный объем V увеличивается или уменьшается в зависимости от развитости рассматриваемых языков. Однако легко заметить, что не может быть гладкого перехода от выражения на потенциальном языке, для которого V = V* , к любому менее развитому языку, для которого V V*. Такой резкий переход обусловлен тем, что для потенциального языка N*= * , в то время как для всех других языков применяется уравнение длины и N .
Понятие уровня программы. Вывод уравнения уровня. Интеллектуальное содержание программы. Несовершенства в программах.
Уровень программы
Понятие уровня программы известно с тех пор как первые «языки высокого уровня» получили свое название Однако, прежде чем это понятие сможет применяться в науке оно должно быть выражено количественно или приведено к измеримому виду так как в противном случае невозможно определить изменения уровня разных выражений какого либо алгоритма
Прежде, чем вводить метрическую характеристику уровня программы необходимо заметить что уровень языка и уровень программы являются разными понятиями хотя и тесно связанными Функциональное соотношение между ними и способы измерения уровня языка будут рассмотрены позднее Сейчас ограничимся измерением уровня отдельных программ Ранее выведенные выражения для определения объема V программы и потенциального объема V* подсказывают простой метод количественного выражения данного понятия Если записать уровень L программы являющейся реализацией какого либо алгоритма как
(3.1)
то лишь наиболее сжатое выражение какое только возможно для алгоритма будет иметь уровень равный единице
Более объемные реализации будут иметь меньшие значения уровня так что L1
Перестроив выражение (3.1) и выделив независящий от реализации член получим
V* = LV (3.2)
при увеличении объема уровень программы уменьшается и наоборот
Обычно легче написать вызов процедуры чем саму процедуру С этой точки зрения проще всего было бы использовать потенциальный язык (L = 1). Однако из самого определения потенциального языка следует что любая процедура которая когда либо могла бы понадобиться в нем уже присутствует Так как число таких процедур бесконечно процесс простого ознакомления с их списком тоже бесконечен Поэтому реализация и использование потенциальных языков невозможны для выполнения реальных заданий
Однако уровень программы играет двойственную роль в определении легкости или трудности ее понимания Специалист которому известны все используемые термины сможет уловить идею тем быстрее чем выше уровень ее представления С другой стороны чтобы сообщить подобную идею лицу менее сведущему в данной конкретной области требуется больший объем сведений и меньший уровень
Вывод уравнения уровня программы
Нередко требуется определить уровень программы непосредственно из ее реализации не зная ее потенциального объема V* и не ссылаясь на возможное обращение к ней в виде вызова процедуры Это можно сделать рассмотрев отдельно влияние операторов и операндов на уровень программы Разумно предположить что чем больше число уникальных операторов используемых в реализации тем ниже ее уровень Но наименьшее возможное число уникальных операторов равно двум и эти два оператора суть символ функции и оператор присваивания или группировки те 1*= 2 С другой стороны увеличение числа уникальных операторов беспредельно поскольку на него нет ограничений ни в каком языке в котором разрешено определение новых вложенных процедур подпрограмм или переходов к помеченным участкам Отсюда получаем следующее соотношение операторов: L (33)
Операнды же не дают однозначного минимума по всем возможным алгоритмам поэтому к ним требуется иной подход В этом случае достаточно заметить что всякое повторение имени операнда является указанием на более низкий уровень реализации Этот эффект можно измерить взяв отношение числа простых операндов к общему числу операндов
L (34)
Объединяя уравнения (33) и (34), заметим что коэффициент пропорциональности являющийся константой должен равняться единице, поскольку в случае потенциального языка 1=1* 2=N2 и L=1. Тогда приходим к уравнению уровня программы
(35)
в котором символ ^ указывает на то что уровень определяемый этим выражением служит аппроксимацией уравнения (31) На самом деле его можно было бы взять в качестве альтернативного определения уровня программы
Возвращаясь к примерам программ для алгоритма Евклида, определим их уровень и его оценку для представлений на Паскале и СИ.
Паскаль: L = V* / V =11.6 / 254.4 = 0.0456; = 2*2 /(1* N2) = 2*6/(12*21) =0.0476
СИ: L = V* / V =11.6 / 224.8 = 0.0516; = 2*2 /(1* N2) = 2*6/(11*18) =0.0606 .
Определение интеллектуального содержания программ
Соотношения для объема и уровня программы рассмотренные ранее приводят к интересному выводу Если произведение объема на уровень любого алгоритма действительно не изменяется при выражении его на различных языках то это свойство можно считать фундаментальным И хотя интуитивно ясно что в машинном коде требуется большая степень детализации для того чтобы «сказать то же» что и на языке Паскаль пока еще отсутствует способ позволяющий измерить «сколько» же было сказано в каждом случае Но если постулировать что любые две эквивалентные программы в самом деле «говорят одно и то же» мы сможем ответить на вопрос «сколько?»
Меру того сколько было сказано в программе логичнее было бы назвать информационным содержанием программы но теория информации Шеннона раньше других завладела смыслом этого термина правда для указания лишь объема сообщения без учета его уровня
Поэтому «информационое содержание» программы мы заменим понятием интеллектуального содержания и определим как
I = V (36)
С помощью уравнений (21) и (35) это выражение можно представить в виде
(N1+N2) log2(1+2) , (37)
где все члены в правой части доступны непосредственному измерению, исходя из любого выражения алгоритма Интеллектуальное содержание вычисленное с помощью уравнения (3.6) приближенно равно потенциальному объему V* полученному из уравнения (34). Следовательно поскольку V* не зависит от языка на котором выражен алгоритм интеллектуальное содержание также не должно от него зависеть
Эту инвариантность можно продемонстрировать оценив интеллектуальное содержание программ, реализующих алгоритм Евклида на разных языках Выше мы рассмотрели метрики алгоритма Евклида, реализованного на Паскале и СИ (приведены в таблицах 2 и 3). Для этих программ значения интеллектуального содержания будут равны:
Паскаль: I = V = 0.0476 * 254.4 = 12.11;
СИ: I = V = 0.0606 * 224.8 = 13.62;
Теперь рассмотрим существенно отличный от этих двух вариант реализации алгоритма поиска наибольшего общего делителя (НОД), основанный на выборе значений НОД из заранее подготовленной таблицы значений. Пусть в качестве операнда «TABLE» в программу включен массив из 1000 строк и 1000 столбцов содержащий заранее вычисленные значения НОД Тогда алгоритм вычисления сводится к выбору значения НОД из двумерного массива по индексам, равным аргументам функции НОД и может быть представлен в виде:
TABLE 0,1,.........................
0,1,.........................
..............................
GCD := TABLE [A,B]
Подсчет частот для алгоритма вычисления НОД в виде «просмотра таблицы» представлен в табл. 4.
Таблица 4
J |
Оператор |
f1,j |
J |
Операнд |
f2,j |
1 |
|
1 000 001 |
1 |
TABLE |
1 000 001 |
2 |
|
1 |
2 |
A |
1 |
3 |
:= |
1 |
3 |
B |
1 |
4 |
[ ] |
1 |
4 |
CGD |
1 |
1=4 |
|
N1=1 000 004 |
2=4 |
|
N2=1 000 004 |
Соответственно, значение интеллектуального содержания такой программы будет равно
(N1+N2) log2(1+2) = 2/4 * 4/1000004 *2*1000004* log2(8) = 4*3 =12
В потенциальном языке этот алгоритм будет записан в виде одного оператора
GCD(A, B, GCD) для которого 1=N1=2 2=N2=3
И значение интеллектуального содержания программы на потенциальном языке будет
(N1+N2) log2(1+2) = 2/2 * 3/3 *5* log2(5) = 11.6
В табл. 5 указаны данные для всех четырех версий алгоритма Евклида а также вычисленное с помощью уравнения (37) интеллектуальное содержание I .
Таблица 5
-
Язык
1
2
N1
N
I
Паскаль
10
6
21
52
12.11
СИ
9
6
21
52
13.62
Потенциальный
2
3
3
5
11.6
Просмотр таблицы
4
4
1 000 004
2 000 0088
12
Как и следовало ожидать с уменьшением уровня используемого в реализации алгоритма пропорционально возрастает требуемый объем поэтому интеллектуальное содержание колеблется в пределах приблизительно 10% от его среднего значения { }
Поскольку данное свойство остается неизменным при изменении языка и лишь усиливается с увеличением сложности задачи оно определяет не зависящую от языка меру содержания внутренне присущего программе.
Пока параметры I и V* используются только как вспомогательные величины они являются весьма полезными метрическими характеристиками которые задают меру того «сколько» было сказано но ничего не говорят о важности сказанного То есть программные параметры показывают насколько хорошо написана программа но не определяют нужно ли было вообще ее писать
Работа в программировании. Уравнение работы. Расчет времени программирования.
Работа в программировании
Если мы ограничим понятие работы в программировании умственной деятельностью затрачиваемой на превращение заранее разработанного алгоритма в реализацию на языке которым исполнитель свободно владеет то метрические характеристики и понятия введенные выше дадут нам возможность проникнуть в суть процесса программирования и образуют исходную систему для его количественной оценки Простое соотношение между этими метрическими характеристиками и работой выполняемой программистом может быть получено с помощью шести шагов описанных ниже в общих чертах
Вывод уравнения работы
1. Как и ранее допустим что любая реализация какого-либо алгоритма заключается в N-кратном выборе из словаря состоящего из элементов
2. Предположим далее что каждый выбор из словаря неслучаен Исследования методов сортировки показали что за исключением хеширования самым быстрым способом поиска в упорядоченном списке является двоичный поиск при котором список многократно делится пополам до тех пор пока не будет найден нужный элемент Полученное в результате число сравнений равно двоичному логарифму числа элементов в списке Следовательно эффективный процесс эквивалентный двоичному поиску требует log2 сравнений для нахождения элемента
3. На основании шагов 1 и 2 можно заключить что программа порождается выполнением N log2 мысленных сравнений
4. Поскольку объем программы V определяется как
V = N log2 (41)
из шага 3 следует что он равен числу мысленных сравнений затрачиваемых на порождение программы
5. Каждое мысленное сравнение содержит ряд элементарных мысленных различений число которых является мерой сложности задачи Из предыдущих результатов вытекает что именно уровень программы L является величиной обратной ее сложности
6. Так как объем V равен числу мысленных сравнений а величина обратная уровню программы те 1/L, есть среднее число элементарных мысленных различений входящих в каждое мысленное сравнение общее число элементарных мысленных различений Е требуемых для порождения программы должно задаваться выражением
Е = (42)
Можно выявить более глубокий смысл уравнения работы если вспомнить уравнение (31)
L =
и подставить его в уравнение (42)
E = (43)
Уравнение (43) показывает что мысленная работа по реализации любого алгоритма с данным потенциальным объемом в каждом языке пропорциональна квадрату объема программы Как будет детально показано далее из уравнения (43) следует что так как «квадрат суммы больше суммы двух квадратов» правильное разбиение на модули может уменьшить работу по программированию реализаций разбитых на отдельные части Теперь перейдем от подсчета элементарных мысленных различений к измерению времени
Расчет времени, необходимого для программирования.
Рассмотрим понятие введенное психологом Джоном Страудом в работе «Тонкая структура психологического времени» ДжСтрауд определил «момент» как время требуемое человеческому мозгу для выполнения наиболее элементарного различения Он обнаружил что в течение времени бодрствования человек воспринимает эти «моменты» со скоростью «от пяти до двадцати раз» в секунду Следует отметить что в диапазон приведенных Страудом цифр попадает и число кадров в секунду превращающее кинофильм из последовательности отдельных снимков в непрерывное изображение Обозначая через S число страудовских «моментов» в секунду мы можем записать
5 S 20 в сек
В дальнейшем S называется числом Страуда Естественно что любой человек занимающийся реализацией алгоритма может в зависимости от степени своей сосредото-ченности отвлечь какую-то часть мысленных различений на посторонние предметы Пользуясь терминологией вычислительной техники можно сказать что если он находится «в режиме разделения времени» S представляет собой лишь верхнюю границу С другой стороны если программист выполняет эквивалент машинной операции «запретить все прерывания» и сосредоточивает внимание на программировании то применимо действительное значение S
Для того чтобы перевести в единицы времени уравнение (42) имеющее размерность двоичных разрядов или различений разделим обе его части на число различений в единицу времени В результате получим
T^ = = = (44)
Символ ^ здесь указывает на то что с помощью этого уравнения вычисляется приближенное а не наблюдаемое время программированияУравнение (44) можно выразить через основные параметры если подставить в него вместо V правую часть уравнения (21) а вместо L - правую часть уравнения (35)
T = (45)
При этом естественно =2
В предыдущем выводе подразумевалось что все программы совершенны т е не имеют несовершенств Хотя это допущение более или менее обосновано для опубли-кованных программ оно необязательно Поэтому откажемся от него по крайней мере в первом приближении подставив вместоN в уравнение (45) Если при этом задается уравнением длины приходим к выражению
(46)
где за исключением числа Страуда S все параметры в правой части доступны непосред-ственному измерению для любой реализации алгоритма
Уровень языка программирования. Прогнозирование числа ошибок в программах.
Уровень языка.
Материал, изложенный в лекции 3, выявил полезное соотношение между уровнем программы L и ее объемом V. Для любого алгоритма, который переводится с одного языка на другой, с увеличением объема уровень уменьшается в той же пропорции. В результате произведение L на V равняется потенциальному объему V* данного алгоритма. С другой стороны, если язык реализации остается одним и тем же, а разрешено менять сам алгоритм, имеется другое, но похожее соотношение. В этом случае с увеличением потенциального объема V* уровень программы L уменьшится в том же отношении. Следовательно, произведение L на V* остается неизменным для любого языка. Это произведение, называемое уровнем языка, обозначается через и записывается в виде:
= LV* (4.7)
Изменения уровня от языка к языку
Следует ожидатьчто чем более универсальным будет язык общего назначения тем больше проявиться способов его использования для данной цели Поскольку многие из этих способов находятся на различных уровнях с ростом среднего значения увеличатся и отклонения от него Кроме этой гипотезы вводится еще одна более существенная согласно которой среднее значение полученное на ряде программ свидетельствует о количественном росте при увеличении развитости языка Для проверки второй гипотезы необходимо иметь интуитивно упорядоченный список языков Так, принято считать что Фортран выше языка ассемблера ПЛ-1 выше Фортрана а английский язык еще выше чем ПЛ-1 Результаты, полученные рядом исследователей, рассматривавших реализации одной группы алгоритмов на различных языках, включая английский, сведены в табл.6.
Таблица 6
Язык |
|
Отклонения |
Английский |
216 |
0,74 |
ПЛ-1 |
1,53 |
0,92 |
Алгол |
1,21 |
0,74 |
Фортран |
1,14 |
0,81 |
Ассемблер |
0,88 |
0,42 |