
Информатика
.pdf
41
Код в Mcd несущественно отличается от приведенного выше:
f(x) : = ε ← 0.000001
n ← 0 m ← 3 p ← x
sum ← p 3
r ← 0 q ← 1
while |q| > ε n ← n + 1
d ← n
k ← if(mod(trunc(d),2) > 0, -1, 1) p ← p∙d∙x2
m
r ← 3 + r
q ← k∙p
3 - r m ← 9∙m
sum ← sum + q sum
(здесь из-за невозможности использовать цикл с постусловием пришлось ввести фиктивное значение первого слагаемого q = 1 для запуска цикла). Заметим, что запись кода сильно растянута.
Циклы с условием успешно используются для организации ите-
рационных вычислительных процессов.
Пример 14. Пусть требуется найти с заданной точностью все корни кубического многочлена p(x) = a0x3 + a1x2 + a2x + a3 (a0 ≠ 0). Один действительный корень, во всяком случае, всегда существует. Поделим сначала все коэффициенты на а0, приведя многочлен к виду p(x) = x3 + a1x2 + a2x + a3. Тогда, если а3 > 0, то существует отрицательный корень, в противном случае корень больше или равен нулю. Так что можно с возрастающим шагом двигаться вдоль числовой оси в нужном направлении, пока многочлен не сменит знак, после чего шаг поиска уменьшить и сменить направление движения. Процесс продолжается до тех пор, пока шаг перемещения не будет достаточно мал. Найдя приближенное значение корня x = xˆ , можно поделить p(x) на x - x,ˆ получив в результате квадратный трехчлен, корни которого мы уже находить умеем (см. п. 1.5.2). Построим программу отыскания одного действительного корня кубического многочлена, заданного своими коэффициентами, результатом которой будет приближенное значение корня и коэффициенты "оставшегося" квадратного трехчлена.
В VBA это будет не функция, а программа (поскольку возвращается не одно значение, а четыре), в Mcd – это функция. VBA – код:
42
Sub Poly3( ByRef a0 As Single, ByRef a1 As Single, _ ByRef a2 As Single, ByRef a3 As Single)
Dim x As Double, h As Double, s As Double, r As Double Const eps As Double = 1E-10
a3 = a3 / a0: a2 = a2 / a0: a1 = a1 / a0: a0 = 1 x = 0: s = a3
If Abs(s) < eps Then Exit Sub h = Sgn(-s)
While Abs(h) > eps
x = x + h: r = ((x + a1)*x + a2)*x + a3 If r*s ≥ 0 Then
h = 1.2*h |
|
'***знак не меняется – увеличиваем шаг |
Else |
|
|
s = Sgn(r): h = -0.2*h |
'***смена знака – область корня |
|
End If |
|
|
Wend |
'***корень найден, делим многочлен на (х-корень) |
'***пересчитываем коэффициенты: a1 = a1 + x: a2 = a1*x + a2: a3 = x
End Sub .
Приведем Mcd-вариант программы (здесь для компактности в одной строке записано несколько операторов присваивания, для исполнения программу следует переписать по правилам, например, строка опера-
торов a3 ← a3 , a2 ← a2 , a1 ← a1 , a0 ← 1 |
|
a3 ← a3 |
|
|
|
|
|||||
|
|
|
|
|
|||||||
a0 |
a0 |
|
a0 |
|
a0 |
|
|
|
|
||
|
|
a2 ← a2 |
|
|
|
|
|||||
должна быть записана как блок: |
|
). |
|
|
|
||||||
|
a0 |
|
|
|
|||||||
|
|
|
|
|
|
|
a1 ← a1 |
|
|
|
|
|
|
|
|
|
|
|
a0 |
|
|
|
|
|
|
|
|
|
|
|
a0 ← 1 |
|
|
|
|
Poly3(a0, a1, a2, a3) : = |
|
ε ← 10-10 |
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|||||
|
|
|
a3 ← a3 , a2 ← a2 , a1 ← a1 |
, a0 ← 1 |
|
|
|||||
|
|
|
|
|
a0 |
|
a0 |
a0 |
|
|
|
|
|
|
x ← 0 , s ← a3 , (break) if |s| < ε, h ← sign(-s) |
|
|
||||||
|
|
|
while |h| > ε |
|
|
|
|
|
|
||
|
|
|
|
x ← x + h, r ← [(x ≠ a1)∙x + a2]∙x + a3 |
|
|
|||||
|
|
|
|
|
|
||||||
|
|
|
|
h ← 1.2∙h |
if r∙s ≥ 0 |
|
|
|
|
||
|
|
|
|
otherwise |
|
|
|
|
|
|
|
|
|
|
|
|
s ← sign(r), h ← -0.2∙h |
|
|
||||
|
|
|
|
|
|
|
|||||
|
|
|
a1 ← a1 + x, a2 ← a1∙x + a2, a3 ← x |
|
|
||||||
|
|
|
a0 |
|
|
|
|
|
|
||
|
|
|
a1 |
|
|
|
|
|
|
||
|
|
|
a2 |
|
|
|
|
|
|
||
|
|
|
a3 |
|
|
|
|
|
|
||
|
|
|
|
|
1 |
|
|
|
|
|
|
|
|
|
|
|
-1.5 |
|
|
3 |
|
||
Например, Poly3(2, -9, 10, -3) = 0.5 , т.е. для многочлена p(x) = 2x |
|
- |
|||||||||
|
|
|
|
|
3 |
|
|
|
|
|
|
9x2 + 10x – 3 найден корень х = 3 и "оставшийся" |
квадратный трехчлен |
||||||||||
x2 -1.5x + 0.5 |
(с корнями х1 = 1, х2 = 0.5). |
|
|
|
|

43
|
y(x - 1) = 1, |
|
||
|
|
|
|
|
Пример 15. Система уравнений |
|
|
|
имеет два действи- |
|
2 |
2 |
||
|
x |
= y + 1 |
|
тельных решения: в первом и третьем квадранте координатной плоскости. Полагая, например, х0 = 2, рассмотрим итерационный процесс
|
|
|
|
||
yn = |
|
xn2 - 1, |
|||
|
|
|
1 (n = 0, 1, 2, ...). Через 20 итераций (можно показать, что при |
||
|
|
|
|||
xn+1 |
= 1 + |
|
|
||
yn |
|||||
|
|
|
выбранном начальном значении итерации сходятся к точному решению) три десятичных знака дробной части перестают меняться, процесс можно остановить: х ≈ 1.7166, у ≈ 1.2953. Если из уравнений ис-
ключить у, то получим |
(х3 + 1)(х - 2) + х = 0. Для этого уравнения ите- |
||
рации xn+1 = 2 - |
xn |
, |
x0 = 2, сходятся значительно быстрее – уже на |
xn3 + 1 |
шестой итерации получается тот же результат. Приведем Mcd-код программы, решающей последнее уравнение и возвращающей приближенное решение и число итераций (напишите соответствующий VBA- код самостоятельно).
ε ← 0.0001, n ← 0, x ← 2 =
y ← x + 1 while |x - y| ≥ ε
n ← n + 1, y ← x
x ← 2 - 3x x + 1
xn
1.716
6
Циклы удобно использовать для вычисления различных пределов. Если аргумент f(x) стремится к некоторому конечному значению α, то достаточно взять какое-нибудь допустимое значение х = а и полагать
xn = α + a2-nα , n = 0, 1, ... , точнее, можно выбрать шаг h = a - α и реализовать цикл
VBA: |
Mcd: |
x = a: h = a - alf: y = f(x)
Do: z = y: h = h/2: y = f(alf + h) Loop Until Abs(y - z) < eps
x ← a, h ← a - α, y ← f(x), z ← y + 1 while |y - z| > ε
z ← y, h ← 0.5∙h, y ← f(α + h)
y
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 - x |
|
|
Пример 16. Вычислим lim |
1 + x |
1 - x |
. Обозначим функцию |
||||||
|
|
|
|
||||||
|
|
|
|
x → 1 |
2 + x |
|
|
|
|
|
|
1 - x |
|
|
|
|
|
|
|
1 |
+ x |
1 - x |
, положим х = 0, h = -1, ε = 0.00001 и, воспользовав- |
||||||
f(x) = |
|
|
|
||||||
2 |
+ x |
|
|
|
|
|
|
|
|
шись приведенным алгоритмом, получим 0.816 (≈ 23 ).
44
Пределы в бесконечности, в принципе, считаются аналогично с той лишь разницей, что выбранный шаг нужно не дробить (деля на два, например), а увеличивать, умножая на какое-то число, например, на два или больше.
|
|
|
|
|
|
|
|
х2 |
- 1 |
х2 + 1 |
|||
|
|
|
|
|
|
|
x |
|
|||||
|
|
|
Пример 17. Вычислим lim |
|
|
|
|
|
|
. Обозначим функцию f(x) = |
|||
|
|
|
|
2 |
|
|
|||||||
|
|
|
|
|
|
x → ∞ x |
|
+ 1 |
|
|
|||
|
х2 |
- 1 |
х2 + 1 |
|
|
|
|
|
|
|
|||
x |
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
, положим х = 2, h = 10 и в цикле будем удваивать h: |
|||||||
|
2 |
|
|
||||||||||
x |
|
+ 1 |
|
|
|
|
|
|
|
|
|
Do: z = y: h = 2*h: y = f(h)
Loop Until Abs(y - z) < eps.
В результате получим единицу, как и следовало ожидать. Упражнение 3. Вычислите все "замечательные" пределы. Пример 18. Реализация этого примера существенно различается в
VBA и Mcd. Требуется найти наименьшее четырехзначное число n, для которого n = m5, где m – количество единиц в двоичном представлении числа n. Здесь достаточно было бы для каждого n выяснять, есть ли единица в соответствующем разряде, сравнивая его с соответствующей степенью двойки (с помощью связки And). Код решения задачи в VBA выглядит достаточно прозрачно:
Dim n As Integer, m As Integer, k As Integer, i As Integer
For n = 1000 To 9999
m = 0 |
'***счетчик единиц |
For i = 1 To 15 |
|
If i = 1 Then k = 1 Else k = 2*k |
'***сдвиг разряда влево |
If n And k Then m = m + 1 |
'***совпадение разрядов |
Next |
|
If m^5 = n Then |
|
MsgBox "n = " & Format(n) & ", m = " & Format(m)
Exit For
End If
Next .
В результате работы программы получено число 7776 с шестью единицами в двоичном представлении. Заметим, что в подсчете двоичных единиц условие записано упрощенно: If n And k Then m = m + 1. Как уже отмечено выше, логическое значение ячейки есть False, если она пуста, в противном случае ее логическое значение есть True. Логические связки Or, And, Xor и отрицание Not действуют поразрядно, поэтому выражение n And k истинно (= True), если хоть в одном разряде у n и k одновременно стоят единицы, в противном случае значение ложно.

45
Mcd, в общем, не приспособлен для простого анализа разрядов чисел (в нем логические условия обрабатываются иначе). Здесь потребуется некоторая изворотливость с использованием операций над текстом. Во-первых, полезно будет создать функцию, возвращающую двоичное представление десятичного целого числа (в виде текстового выражения):
B(n) : = s ← ""
while n > 0
d ← mod(n,2), n ← trunc(0.5∙n) s ← concat(if(d = 0,"0","1"), s)
s
Во-вторых, полезно создать функцию, возвращающую число единиц в строке двоичного представления числа:
Num(n) : = s ← B(n), m ← 0
for i 0 .. strlen(s) – 1
m ← m + 1 if substr(s, i, 1) = "1"
m
Ну, а теперь сама программа будет выглядеть уже просто:
|
for n 1000 .. 9999 |
7.776×103 |
|||||
|
= |
6 |
|
||||
|
|
s ← B(n), m ← Num(n) |
|
|
|
||
|
|
if n = m5 |
|
|
|
|
|
|
|
|
return |
n |
|
|
|
|
|
|
|
|
|
||
|
|
|
m |
|
|
|
|
|
|
|
break |
|
|
|
|
|
"nothing" |
|
|
|
|
||
|
|
|
|
|
Напомним еще раз, что в Mcd-блоках несколько операторов, разделенных запятыми, в одной строке записаны лишь для компактно-
сти, в тексте рабочего листа каждый оператор должен записывать-
ся в отдельной строке.
1.7. МАССИВЫ
Массивы - группы однотипных данных с произвольным доступом в оперативной памяти (во внешней памяти соответствующие группы данных называются файлами). По сути это таблицы с одним или несколькими входами (одно или многомерные массивы) в соответствии с указанными индексами (т.е. с заданной нумерацией). В VBA массивы определяются оператором Dim с указанием количества и диапазона изменения индексов (индексация по умолчанию начинается с нуля). При этом используются инструкции:
Dim ‹имя массива1›(n1) As ‹тип›, ‹имя массива2›(m2, n2) As ‹тип›
46
для одномерного и двумерного массивов: в первом массиве индексы (т.е. номера элементов) изменяются от 0 до n1, второй массив двухмерный, первый индекс меняется в пределах 0 - m2, второй 0 – n2. Одномерный массив – это последовательность однотипных данных. Двухмерный массив – плоская таблица, первый индекс – номер строки, второй – номер столбца элемента в таблице. Индексы указываются в круглых скобках. По желанию индексацию можно начинать с произвольного натурального числа, тогда инструкция определения массива выглядит иначе:
Dim ‹имя массива›(m To n) As ‹тип›, ...
Здесь индекс может меняться в пределах от m до n.
Если размер массива заранее не известен и будет определен в программе в дальнейшем, то массив определяется вначале формально, без указания размеров:
Dim ‹имя массива›() As ‹тип›,
затем перед его фактическим использованием можно указать его реальный размер инструкцией Redim ‹имя массива›(размер) (этот оператор позволяет в процессе вычислений обнулять существующий массив). В программе определение массива всегда должно располагаться до его фактического использования. Выборка элемента массива осуществляется указанием его имени и индексов (в круглых скобках): первый индекс – номер строки, второй – номер столбца, третий (если есть) – номер "слоя", и т.д.. VBA позволяет использовать размерность массивов до шестнадцати, хотя трудно представить себе реальную размерность больше трех.
В Excel рабочий лист представляет собой плоскую таблицу с более чем 2500000 ячеек с индексацией, по умолчанию начинающейся с единицы. Эта стандартная таблица-массив имеет стандартное же имя Cells(), ячейка в левом верхнем углу – Cells(1, 1), соседняя справа – Cells(1, 2), и т.д. Второй ряд начинается с ячейки Cells(2, 1), далее вправо Cells(2, 2), затем Cells(2, 3), ... . По умолчанию ячейки рабочего листа имеют тип Variant, т.е. они могут содержать как числовую, так и текстовую информацию. Поскольку ячейки – видимая часть рабочего листа, в них можно вводить информацию непосредственно с клавиатуры, а также выводить в них информацию в процессе работы программы просто с помощью оператора присваивания. Например, работая с конкретным рабочим листом (активный лист) оператор присваивания Cells(1, 3) = "Вывод результата:" поместит в указанную ячейку приведенную фразу. А поскольку ячейки листа имеют много полезных свойств, таких, например, как тип шрифта, размер символов, цвет, и т.д., то такая таблица является удобным

47
инструментом ввода-вывода. Другие свойства рабочего листа и его ячеек будут обсуждаться позднее по мере необходимости.
В Mcd массивы определяются указанием индексов (нижняя индексация) без скобок. Присваивание имени массива с максимальным индексом некоторого значения (например, нуля) уже является определением этого массива. Например, запись A2,5 := 0 определит дву-
мерный массив с тремя строками и шестью столбцами. Эта же запись в определенном ранее массиве обнулит соответствующее значение. Массивы фиксированного (не очень большого размера) можно вводить, щелкнув мышкой по иконке "матрица" (см. рис. 6). Кроме того, Mcd допускает использование ранжированных переменных (см. п. 1.6) – одномерных массивов заданного типа, в которых содержимое меняется по типу арифметической прогрессии с фиксированным шагом. Например, запись i := 2, 2.5 .. 12 создаст переменную-массив
из 21 числа: 2, 2.5, 3, 3.5, 4, 4.5, ..., 12. Если переменную "ранжируем" только первым и последним (целым) числом, то получим массив значений арифметической прогрессии с единичным шагом. Это позволяет определять регулярные массивы произвольных размеров. Например, определим квадратную матрицу (двумерный массив) размером 1000×1000, диагональные элементы которой равны четырем, соседние элементы – единицы, остальные нули:
m : = 1000 |
n : = 1000 |
i : = 0 .. m - 1 |
j : = 0 .. n - 1 Ai, j : = if(i = j, 4, if(|i - j| = 1, 1, 0)). |
Для того, чтобы посмотреть созданную матрицу, достаточно набрать ее имя и поставить после него знак равенства (рис. 7):
Рис. 7

48
Остальную часть массива можно увидеть, воспользовавшись прокруткой, которая появляется после щелчка мышкой по правому нижнему углу выданной таблицы.
Mcd особенно приспособлен для работы с векторами и матрицами, снабжен для этого богатым набором встроенных операций и функций, таких, например, как транспонирование матриц, матричное и векторное умножение, определение количества строк и столбцов матриц (функции rows и cols), определение последнего индекса вектора (функция last), вычисление обратных (и псевдообратных – функция geninv) матриц, выделение подматрицы (функция submatrix), вычисление определителя (обычнй знак модуля), упорядочение массива по возрастанию содержимого (функция sort), склеивания соответствующих массивов горизонтально или вертикально (функции augment и stack), решение линейных систем уравнений (функция lsolve), и много других. Индексация массивов по умолчанию начинается с нуля, но Mcd имеет специальную встроенную переменную ORIGIN, которая позволяет изменить начало индексации, присвоив этой переменной необходимое значение. В связи со всем этим значительная часть программирования обработки массивов будет иллюстрироваться в VBA с параллельными ссылками на сходные конструкции в Mcd.
Пример 1. Переписать содержимое одномерного массива размером 1 : m∙n в двумерный m×n массив. Код VBA:
Sub A_To_B(A() As Single, B() As Single, m As integer, n As Integer) Dim k As Integer, i As Integer, j As Integer
k = 0
For i = 1 To m For j = 1 TO n
k = k + 1: B(i, j) = A(k)
Next Next
End Sub .
Код Mcd (индексация начинается с нуля, для переписывания в Mcd предыдущего кода "один к одному" следует положить ORIGIN : = 1):
A_To_B(A, m, n) : = k ← 0
for i 0 .. m – 1 for j 0 .. n – 1
Bi, j ← Ak
k ← k + 1
B
Пример 2. Составить программу умножения двух матриц. Пусть матрицы заданы массивами A(1 To m, 1 To k), B(1 To k, 1 To n), C(1 To m, 1 To n) и требуется вычислить C = A∙B.

49
Sub MProd(A() As Single, m As Integer, k As Integer, B() As Single, _ n As Integer, C() As Single)
Dim i As Integer, j As Integer, t As Integer, p As Single For i = 1 To m
For j = 1 To n : p = 0
For t = 1 To k
p = p + A(i, t) * B(t, j) '***вычисление суммы сомножителей
Next: C(i, j) = p Next
Next End Sub .
В Mcd это просто C : = A∙B.
Заметим, что в Excel в меню f(x) "вставка функций" имеются
функции МОПРЕД и МУМНОЖ, вычисляющие определитель матрицы, записанной в ячейках рабочего листа, и произведение матриц.
Пример 3. Иногда бывает необходима специальная нумерация элементов массива. Пусть, например, требуется перенумеровать элементы квадратного массива A(1 To n, 1 To n) параллельно побочной диагонали, т. е. заполнить массив последовательно натуральными чис-
лами в порядке: A(1, 1) = 1, A(2, 1) = 2, A(1, 2) = 3, A(3, 1) = 4, A(2, 2) = 5 и т. д. Заметим, что сумма индексов параллельно побочной диагонали остается постоянной. Этим и воспользуемся в приведенном ниже VBA-коде:
Sub SpecNum(A() As Integer, n As Integer)
Dim k As Integer, u As Integer, v As Integer, i As Integer, j As Integer, t As Integer
For k = 1 To 2 * n – 1 |
|
If k < n Then u = k Else u = n |
'***u = min(k, n) |
If k < n Then v = 1 Else v = k – n + 1 |
'***v = max(1, k – n + 1) |
For i = u To v Step -1 |
|
t = t + 1: j = k + 1 – i: A(i, j) = t |
|
Next |
|
Next |
|
End Sub . |
|
Для n = 5 массив будет заполнен следующим образом:
|
|
|
|
Таблица 6 |
1 |
3 |
6 |
10 |
15 |
2 |
5 |
9 |
14 |
19 |
4 |
8 |
13 |
18 |
22 |
7 |
12 |
17 |
21 |
24 |
11 |
16 |
20 |
23 |
25 |
Соответствующий код в Mcd имеет вид (напомним: индексация начинается с нуля, если не используется встроенная перемнная
ORIGIN):

50
SpecNum(n) : = t ← 0
for k 0 .. 2∙n - 2 u ← min(k, n - 1)
v ← max(0, k - n + 1)
for i u .. v t ← t + 1
Ai, k – i ← t
A
Пример 4. Вернемся к примеру 6 из п. 1.6 (правило сокращения дробей). Теперь мы можем определить массив результата, который будет формироваться по мере вычислений и возвратится по окончании расчетов. Код в Mcd выглядит следующим образом:
k ← 0
for m 10 .. 00
for n m + 1 .. 100
a ← trunc(0.1∙m), b ← mod(m, 10)
c ← trunc(0.1∙n), d ← mod(n, 10), q ← 0 if b ≠ 0
q ← db if a = c q ← bc if a = d
q ← da if b = c q ← ac if b = d
if q = mn
resk, 0 ← n, resk, 1 ← m k ← k + 1
res
6416
=95 19
6526
9849
(здесь массив res формируется в процессе получения результата).
Пример 5. |
Для антисимметричной матрицы A с элементами |
|||||
2, i = j, |
|
|
|
|||
Ai,j = |
1 |
|
, i ≠ j |
найти сумму всех скалярных произведений столб- |
||
|
|
|
|
|
|
|
i - j |
|
|
|
|
||
цов по формуле S = n - 1 |
n |
A‹ i › A‹ j ›, где n количество строк |
||||
|
|
|
|
i =1 |
j = i + 1 |
|
(столбцов) матрицы А, верхний индекс указывает номер столбца матрицы. В VBA следующая программа решает задачу.
Function Scalar(n As Integer) As Single
Dim A() As Single, i As Integer, j As Integer, s As Single, k As Integer Redim A(1 To n, 1 To n) '***формирование матрицы