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

Основы программирования. Борисенко

.pdf
Скачиваний:
1610
Добавлен:
09.04.2015
Размер:
9.31 Mб
Скачать

Расширенный алгоритм Евклида

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 устанавливается в единицу, если