
Основы программирования. Борисенко
.pdfРасширенный алгоритм Евклида |
61 |
Начальные значения этих переменных обеспечивают выполнение ин¬ варианта:
а = m , |
b = n , |
|||
u1 |
= |
1 , |
v1 = |
0, |
u2 |
= |
0, |
v2 = |
1. |
Условием завершения цикла, как и в обычном алгоритме Евклида, является равенство нулю переменной b:
<3(а, b, u 1 , v1, u2, v2) : b = 0.
Осталось написать тело цикла, сохраняющее инвариант и уменьша¬ ющее абсолютную величину переменной b. Это нетрудно сделать, исходя из инварианта цикла. В обычном алгоритме Евклида пара (а, b) заменяется на (b, r) , где r — остаток от деления а на b.
а = qb + r, |r| < |b|.
Здесь q равняется целой части частного от деления а на b. Заме¬ тим, что в программировании, в отличие от школьной математики, операция взятия целой части перестановочна с операцией изменения знака:
целая |
часть (—ж) = —целая часть(ж). |
|
|
||||
Например, целая |
часть(—3.7) |
= —3. Это позволяет работать |
с отри¬ |
||||
цательными числами |
так ж е , как и с положительными, |
т.е. вообще |
|||||
не следить за знаком! Отметим также, что в большинстве |
языков |
||||||
программирования считается, что результат любой операции |
с целы¬ |
||||||
ми числами является целым числом, например, 8/3 = 2. |
|
|
|||||
Переменная |
q вычисляется |
как целая часть |
частного |
от |
деления |
||
а на b: |
|
|
|
|
|
|
|
|
|
q = целая |
часть(а/Ь). |
|
|
|
|
Выразим остаток r в виде линейной |
комбинации |
а и b: |
|
|
|||
|
|
r |
= а — qb. |
|
|
|
Используя инвариант цикла, можно выразить r через исходные числа m и n:
r = а — qb = ( u 1 m + v 1 n ) — q ( u 2 m + v 2 n ) = = (u1 — qu2)m + (v1 — qv2)n.
62 |
|
|
|
|
|
|
|
|
|
|
|
1.5.2. Инвариант цикла |
||
Через |
u 1, v1, u'2 , v2 обозначаются новые |
значения переменных u 1 , |
||||||||||||
v 1 , |
u 2 , |
v 2 . При замене |
(а, b) |
|
(b, r) они |
вычисляются |
следующим |
|||||||
образом: |
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
u'2 = u1 — qu2, v2 = v1 — qv2 |
|
|||||||||
|
По |
завершению цикла ответ будет находиться в переменных а |
||||||||||||
( Н О Д |
исходных |
чисел |
m |
и n), |
u 1 , v 1 |
(коэффициенты |
выражения |
|||||||
Н О Д через m |
и n). |
|
|
|
|
|
|
|
|
|
||||
|
Выпишем |
алгоритм: |
|
|
|
|
|
|
|
|
||||
алгоритм Расширенный |
алгоритм Евклида( |
|
|
|||||||||||
|
вх: цел m, |
цел n, |
цел v |
|
|
|
|
|
||||||
) |
вых: цел |
d, цел |
и, |
|
|
|
|
|
||||||
дано: целые числа т, n, хотя бы одно отлично от нуля; |
||||||||||||||
| |
||||||||||||||
| |
надо: вычислить |
d |
= НОД(т, |
и) и |
найти и, v такие, что |
|||||||||
| |
|
|
|
|
d = u * m + v * n ; |
|
|
|
||||||
начало алгоритма |
|
|
v l , |
и2, v2; |
|
|
|
|
||||||
| |
цел |
a, b, |
q, |
r, и1, |
переменная |
|
||||||||
| |
цел t ; |
|
// вспомогательная |
|
||||||||||
| |
// |
инициализация |
|
|
|
|
|
|
|
|
||||
| |
a := m; b := |
n; |
0; |
|
|
|
|
|
|
|
|
|||
| |
и1 |
:= 1; v l |
:= |
|
|
|
|
|
|
|
|
|||
| |
и2 := 0; v2 := 1; |
|
b) == |
НОД(т, |
и) |
и |
|
|||||||
| |
утверждение: НОД(а, |
|
||||||||||||
| |
|
|
|
а == и1 * т + v1 * n |
|
и |
|
|||||||
| |
|
|
|
b |
== |
и2 |
* т |
+ |
v2 * |
n; |
|
|
|
|
| |
цикл пока |
b |
!= |
0 |
|
b) == |
НОД(т, |
n) |
и |
|
||||
| |
| инвариант: НОД(а, |
|
||||||||||||
| | |
|
|
а == и1 * т + v1 * n |
|
и |
|
||||||||
| |
| |
|
|
b |
== |
и2 |
* т + v2 * |
n; |
|
от деления а на b |
||||
| |
| q |
:= а / b; // целая часть частного |
||||||||||||
| |
| r |
:= а % b; // остаток |
от деления а на b |
(b, r) |
||||||||||
| |
| а |
:= b; |
b |
:= |
r; |
|
// |
заменяем |
пару |
(а, b) на |
| | // Вычисляем новые значения переменных и1, и2
Нахождение корня |
функции |
|
|
|
63 |
||||
|
|
t |
:= и2; |
|
|
// запоминаем старое значение и2 |
|||
|
|
и2 |
:= и1 - q * и2; // вычисляем новое |
значение |
и2 |
||||
|
|
и1 |
:= t ; |
|
|
// новое значение и1 := старое |
|||
|
|
|
|
|
|
// |
|
значение |
и2 |
|
|
// Аналогично |
находим |
новые значения переменных |
v1, v2 |
||||
|
|
t |
:= v2; |
|
|
|
|
|
|
|
|
v2 |
:= v1 |
q |
* v2; |
|
|
|
|
|
|
v1 |
:= t ; |
|
|
|
|
|
|
| |
конец цикла |
|
|
|
|
|
|
||
| |
утверждение: b |
== 0 |
b) |
и |
|
|
|||
|
|
|
|
НОД(а, |
НОД(т, n) и |
|
|
||
| |
|
|
|
а |
и1 |
* т + v1 * n; |
|
|
|
// Выдаем ответ |
|
|
|
|
|||||
| |
d |
:= а; |
|
|
|
|
|
|
|
| и |
:= и1; v |
:= v1; |
|
|
|
|
|||
конец |
алгоритма |
|
|
|
|
|
|||
Н а х о ж д е н и е корня ф у н к ц и и |
методом |
|
|
||||||
деления отрезка пополам |
|
|
|
||||||
|
Рассмотрим |
еще один |
пример использования |
схемы построения |
цикла с помощью инварианта, часто встречающийся в реальных про¬ |
||
граммах. Пусть y = /(ж) — непрерывная |
функция |
от вещественного |
аргумента, принимающая вещественные |
значения. |
Пусть известно, |
что |
на заданном отрезке [а, b] она принимает значения разных зна¬ |
||
ков. |
Из непрерывности |
функции / следует, что она имеет по край |
|
ней |
мере |
один корень |
на этом отрезке. Требуется вычислить корень |
функции |
/ с заданной |
точностью е. |
Идея алгоритма состоит в том, чтобы поделить отрезок пополам и выбрать ту половину отрезка, на которой функция принимает значе¬ ния разных знаков. Эта операция повторяется до тех пор, пока длина отрезка не станет меньше, чем е.
Пусть концы текущего отрезка хранятся в переменных ж0 , ж1 . Инвариантом цикла является утверждение о том, что функция при¬ нимает значения разных знаков в точках ж0 , ж1 :
/(жо,ж1) : /(жо) • /(ж1) < 0.
64 |
1.5.2. Инвариант цикла |
Начальные значения: |
|
ж0 = а, |
ж1 = b. |
Условием завершения цикла является утверждение о том, что длина отрезка меньше е:
<3(ж0, ж1 ) : |ж1 — ж0 | < е
(знак модуля используется потому, что в условии задачи не требуется
выполнения |
неравенства а |
< |
b). |
|
|
|
|||||
|
Выпишем алгоритм вычисления корня функции с заданной точ¬ |
||||||||||
ностью: |
|
|
|
|
|
|
|
|
|
||
вещ корень функции на отрезке(вх: вещ |
а, вещ |
b, вещ eps) |
|||||||||
| |
дано: 1(а) * f(b) <= |
0, |
|
|
|
||||||
| |
|
|
eps > 0 -- очень маленькое число; |
|
|||||||
| |
надо: вычислить |
корень |
функции |
f на |
отрезке [а, b] с |
||||||
| |
|
|
точностью |
eps; |
|
|
|
|
|
||
начало алгоритма |
|
|
|
|
|
|
|||||
| вещ x0, x1, c; |
|
|
|
|
|
|
|||||
| |
// |
инициализация |
|
|
|
|
|
||||
| |
x0 |
:= |
а; |
x1 |
:= b; |
f |
(x1) <= |
0; |
|
|
|
| |
утверждение: f(x0) * |
|
|
||||||||
| |
цикл пока |x1 - x0| |
>= |
eps |
0; |
|
|
|||||
| |
| инвариант: f(x0) * |
f |
(x1) < |
|
[x0, x1] |
||||||
| |
| c := |
(x0 + |
x1) |
/ 2 ; |
// Середина отрезка |
||||||
| |
| если |
f(x0) * f ( c ) <= |
0 |
|
|
|
|||||
| |
| |
| то |
x1 |
:= |
c |
|
|
|
|
|
|
| |
| |
| |
|
|
|
|
|
|
|||
| |
| | иначе |
|
|
|
|
|
0 |
|
|||
| |
| | |
утверждение: f(c) * f(x1) <= |
|
||||||||
| |
| |
| |
x0 |
:= |
c |
|
|
|
|
|
|
| |
| конец |
если |
|
|
|
|
|
|
|
||
| |
конец |
цикла |
|
|
|
|
|
|
|
||
| |
утверждение: |
|x1 |
- x0| |
< eps |
и |
|
|
||||
| |
|
|
|
|
f(x0) * |
f |
(x1) <= |
0; |
|
|
Задачи по теме «Инвариант цикла» |
65 |
| ответ := (x0 + x1) / 2; конец алгоритма
З а д а чи по теме «Построение цикла с помощью инварианта»
1.По аналогии с алгоритмом быстрого возведения в степень, со¬ ставить алгоритм быстрого вычисления произведения двух це¬ лых чисел, использующий лишь операции сложения, вычита¬ ния, удвоения и деления пополам. Пусть надо вычислить про¬ изведение чисел а и n. В качестве инварианта цикла использо¬ вать условие
b • k + p = а • n = const,
где b, k, p — целочисленные переменные, которые меняются в теле цикла.
2.Определить, что вычисляется в результате данного фрагмента программы:
дано: целые числа а >= 0, b > 0
цел q, r, e, т; |
|
:= |
1; |
т |
:= b |
||||
q |
:= 0; r := а; e |
||||||||
утверждение: а |
- q*b == |
r |
и |
||||||
|
|
|
|
т |
== |
e*b |
|
|
|
цикл пока r >= b |
|
|
r |
и |
|||||
| |
инвариант: а |
- q*b == |
|||||||
| |
если |
2*т |
т |
== |
e*b |
|
|
||
| |
<= r |
:= т*2; |
|
||||||
| |
| то |
e |
:= |
e*2; т |
|
||||
| |
иначе если т > r |
|
|
|
|||||
| |
| то |
e |
:= |
e/2; т |
:= т/2; |
|
|||
| |
иначе |
|
|
|
<= |
r |
и |
r < 2*т |
|
| |
| утверждение: т |
||||||||
| |
| q |
:= |
q |
+ e; r |
:= |
r |
- т; |
||
| |
конец |
если |
|
|
|
|
|
конец цикла
66 |
1 5 2 Инвариант цикла |
ответ := q;
Глава 2
Устройство компьютера
Компьютер — это универсальный исполнитель, который умеет управлять другими исполнителями и обладает собственной внутрен¬ ней памятью. Запись алгоритма для компьютера называется про¬ граммой. Все современные компьютеры построены по так называе¬ мой фон-Неймановской архитектуре: программа хранится в памяти компьютера, так ж е как и данные.
Компьютер построен из следующих составных частей:
п р о ц е сс ор — это основа любого компьютера, его мозг. Процессор производит все вычисления и отдает команды всем остальным компонентам компьютера;
оперативная память также является обязательной составной ча¬ стью любого компьютера. Оперативная память ( R A M — Random Access Memory) хранит как программу, так и данные (т.е. значения переменных). Часть памяти может быть защище¬ на от записи и хранится в специальной микросхеме (ПЗУ — постоянное запоминающее устройство или R O M - Read Only Memory). Обычно в П З У л е ж и т программа первоначальной за¬ грузки и базовая система ввода-вывода (BIOS);
шина — это канал передачи команд и данных между всеми со¬ ставными частями компьютера. В компьютере могут быть одна или несколько шин. Все устройства подключаются к шине па¬ раллельно, т.е. порядок подключения не важен, а количество
68 |
2.1. Оперативная память |
|
проводов не зависит от количества |
подключенных |
устройств. |
Порядок передачи команд и данных |
определяется |
протоколом |
работы шины, т.е. четко описанным набором соглашений, при¬ нятым, как правило, в виде международного стандарта. Каждое устройство подключается к шине с помощью контроллера, ко¬
торый осуществляет перевод с языка |
сигналов, передаваемых |
по шине, на язык команд конкретного |
устройства; |
внешние устройства подключаются к шине компьютера. Наиболее распространенные внешние устройства — это жесткий диск, клавиатура, монитор, сетевая карта, модем и т.п. Н и одно из них не является обязательным, как показывает пример копьютера, управляющего автомобильным двигателем со впрыском топлива. Но какие-то внешние устройства всегда присутству¬ ют, поскольку через них осуществляется связь компьютера с внешним миром.
Рассмотрим к а ж д у ю из составляющих частей компьютера более подробно.
2.1.Оперативная память
Элементарной единицей памяти всех современных компьютеров является байт, состоящий из восьми двоичных разрядов. Каждый байт имеет свой адрес. В наиболее распространенной 32-разрядной архитектуре адреса байтов изменяются от 0 до 2 3 2 — 1 с шагом 1. Память, с логической точки зрения, можно рассматривать как мас¬ сив байтов: можно прочесть или записать байт с заданным адресом. Содержимое байта трактуется либо как неотрицательное целое чис ло в диапазоне от 0 до 255, либо как число со знаком в диапазоне от —128 до 127. (На самом деле байт — это элемент кольца вычетов по модулю 256, см. раздел 1.4.1.)
Однако, физически при работе с памятью по шине передаются не отдельные байты, а машинные слова. В 32-разрядной архитектуре машинное слово — это четыре подряд идущих байта, при этом ад¬ рес младшего байта кратен четырем. (В 64-разрядной архитектуре машинное слово состоит из восьми байтов.) М а ш и н н о е слово — это наиболее естественный элемент данных для процессора. М а ш и н н о е
2.1. Оперативная память |
69 |
слово содержит целое число, которое можно рассматривать либо как
беззнаковое в диапазоне от 0 до |
2 3 2 |
— 1, |
либо |
как знаковое |
в |
диа¬ |
|||||
пазоне от |
—23 1 до |
2 3 1 — 1. |
Адрес |
памяти |
также |
представляет |
|
собой |
|||
машинное |
слово. |
|
|
|
|
|
|
|
|
|
|
|
Принято нумеровать биты внутри машинного слова (как |
и |
вну¬ |
||||||||
три |
байта) |
справа |
налево, |
начиная |
с нуля и кончая 31. М л а д ш и й |
||||||
бит |
имеет |
нулевой |
номер, |
старший, |
или |
знаковый, |
бит — номер 31 |
||||
(см. раздел1.4.1). М л а д ш и е биты |
числа находятся |
в младших |
|
битах |
|||||||
машинного |
слова. |
|
|
|
|
|
|
|
|
|
Существуют два способа нумеровать байты внутри машинного слова. В соответствии с этим все процессоры разделяются на два
типа: |
|
|
|
|
|
|
|
|
|
|
Big Endian |
— байты |
внутри |
машинного |
слова |
нумеруются |
слева |
||||
направо. Таковы процессоры |
Motorola, |
Power P C . Байты |
в ар |
|||||||
хитектуре |
B i g Endian удобно |
представлять |
записанными |
слева |
||||||
направо. |
При этом старшие |
биты целого числа располагаются |
||||||||
в байте |
с младшим |
адресом. |
|
|
|
|
|
|||
Little Endian |
|
— байты |
внутри машинного |
слова |
нумеруются |
спра |
||||
ва налево. Таковы процессоры Intel 80x86, |
Alpha, V A X и др. |
|||||||||
Байты в архитектуре Little Endian следует представлять запи¬ |
||||||||||
санными справа налево. |
При этом старшие |
биты целого |
числа |
|||||||
располагаются |
в байте со старшим адресом. |
|
||||||||
Архитектура |
B i g |
Endian |
была |
популярна |
в |
середине X X ве¬ |
ка. К концу 70-х годов программисты осознали, что Little Endianархитектура гораздо удобнее. Например, один из аргументов в пользу Little Endian заключается в том, что целое число, занимающее ма¬ шинное слово с адресом n, и байт с тем ж е адресом содержат одно и то ж е значение (конечно, если оно не превышает 255). В случае B i g Endian это не так: например, если целое число с адресом n содержит число 17, то байт с адресом n содержит 0; или если целое число со¬ держит отрицательное значение —77, то байт с адресом n содержит отрицательное значение —1. При небрежном программировании это порождает массу ошибок. Поэтому большинство современных про¬ цессоров построены по архитектуре Little Endian.
Тем не менее многие компьютерные протоколы ориентируются на Big Endian, поскольку они были приняты достаточно давно. Напри-
70 |
|
|
|
|
|
2.2. Процессор |
|
мер, все протоколы |
сети Internet передают данные в формате B i g |
||||||
Endian, |
т.к. они были |
разработаны |
в 70-х годах X X века. |
На ма¬ |
|||
шинах |
с архитектурой |
Little Endian |
приходится |
переставлять |
байты |
||
внутри |
слова |
перед |
отправкой IP-пакета в сеть |
или при получении |
|||
IP-пакета из |
сети. |
|
|
|
|
|
2.2.Процессор
Процессор является основой любого компьютера. Это большая микросхема, содержащая внутри себя сотни тысяч или д а ж е милли¬ оны элементов. Современные процессоры чрезвычайно сложны и мо¬ гут содержать несколько уровней построения и описания. Так, можно различать внешние команды процессора в том виде, в котором они
используются в |
программах |
и записываются в оперативной памя¬ |
ти, и внутренний |
микрокод, |
применяемый дл я реализации внешних |
команд. Процессор может содержать внутри себя устройства, пред¬ назначенные дл я ускорения работы, — конвейер команд, устройство опережающей выборки из памяти, кеш-память и т.п.
Рассмотрим лишь самые общие принципы построения и работы процессора, которые одинаковы как для примитивных, так и для са¬ мых современных процессоров.
Любой процессор имеет устройство, выполняющее команды, и собственную внутреннюю память, реализованную внутри микросхе¬ мы процессора. Она называется регистрами процессора. Имеется 3 типа регистров:
о б щ ие регистры хранят целые числа или адреса. Размер общего ре¬ гистра совпадает с размером машинного слова и в 32-разрядной архитектуре равен четырем байтам. Число общих регистров и их назначение зависит от конкретного процессора. В большин¬ стве Ассемблеров к ним можно обращаться по именам R0, R1, R2, . . . Среди общих регистров имеются регистры специального
назначения: указатель |
стека S P (Stack Pointer), счетчик команд |
P C (Program Counter) |
и др.; |
регистр флагов содержит биты, которые устанавливаются в едини¬ цу или в ноль в зависимости от результата выполнения по¬ следней команды. Так, бит Z устанавливается в единицу, если