
Информатика
.pdf
31
В Mcd этот пример целесообразно решить в виде кода:
n ← 1 |
= 355 |
while |
|sin(n)| ≥ 0.0001 |
n ← n + 1
n
Здесь нет необходимости создавать функцию (для одноразового вычисления). Приведенный блок содержит в конце одну переменную, n, значение которой необходимо сообщить в результате вычисления (знак равенства после блока "высвечивает" значение n = 355), можно было бы использовать также оператор return n.
Пример 2. Пусть требуется вычислить значение непрерывной
дроби d = 2 + |
|
1 |
|
|
, образуемой двумя последовательностями |
|
|
|
2 |
|
|
||
3 + |
|
|
|
|||
4 + . |
9 |
|
||||
|
|
.. |
|
|||
|
|
|
|
+ |
|
|
|
|
|
|
11 |
чисел: {2, 3, 4, ..., 11} и {1, 2, 3, ..., 9}. Вычисление следует проводить "с хвоста": взять число 11, затем 9 поделить на 11 и прибавить 10, затем 8 поделить на результат и прибавить 9, и т.д. Таким образом, алгоритм вычисления выстраивается в следующий цикл пересчета в обратном порядке (т.е. с шагом -1):
Dim n As Integer, d As Single
d = 11
For n = 10 To 2 Step -1
d = n + (n - 1) / d
Next
В Mcd цикл пересчета вводится из меню (см. рис. 5) в виде
for ▪ ▪ , где в первой строке указаны места для счетчика и диапа-
▪
зона его изменения, нижний квадратик – для тела цикла (в это место можно вставить вертикальную черту –"Add Line"). Диапазон изменения счетчика можно указать явным перечислением его значений или с помощью операции "ранжирования", используя оператор ".." (клавиша ";" на латинской клавиатуре или меню матричных операций, см. рис 6 слева)
d ← 11 = 2.291
for n 10..2
d ← n + n - 1 d
d

32
В циклах пересчета Mcd прямой и обратный ход записываются одинаково. Это таит в себе некоторую опасность: в Mcd цикл пересчета выполняется всегда.
Пример 3. Построим функцию, которая каждому натуральному n
ставит в соответствие min {ak = |
k - 1 |
(k-1)3 |
|
|
|
|
|
|
|||
k + 1 + sin |
k+1 , ak > 0}. Очевидно, |
||
1 ≤ k ≤ n |
|
|
|
все ak < 2, так что с самого начала значения чисел нужно сравнивать с числом 2. В VBA код будет иметь вид:
Function Choice(n As Integer) As Double
Dim min As Double, a As Double, r As Double, k As Integer min = 2
For k = 2 To n: r = (k – 1) / (k + 1) a = r + sin(r * (k – 1)^2)
If a > 0 And a < min Then min = a Next: Choice = min
End Function .
В Mcd:
Choice(n) : = min ← 2
for k 2 .. n
r ← k - 1 k + 1
a ← k + sin(r∙(k - 1)2)
min ← a if a > 0 a < min min
Циклы с условием позволяют организовать паузу заданной длительности, используя функцию Timer, возвращающую текущее время процессора в секундах. Например, паузу в пять секунд можно создать, вставив в программу код (цикл с пустым телом):
t = Timer + 5: While Timer <t: Wend.
Пример 4. Рассмотрим более сложную задачу. Известно, что значение тангенса угла х (угол дан в радианах) можно с восемью верными
(x2 - 105)∙x2 + 945
знаками вычислить по простой формуле: tg(x) = x∙ (15∙x2 - 420)∙x2 + 945 ,
если |x| < |
π |
. С другой стороны, угол всегда можно изменить на |
π |
, ис- |
|||
|
4 |
|
|
|
|
4 |
|
|
|
|
π |
1+ tg(x) |
|
|
|
пользуя равенство tg(x + |
4) = |
1- tg(x) |
. Построим алгоритм вычисле- |
ния тангенса любого угла, используя приведенные формулы. Очевидно, знак угла не играет роли. Нужно определить, сколько раз содер-
жится число π в заданном угле и найти оставшуюся часть. Если остав-
4
шаяся часть близка к нулю (с заданной точностью, будем называть это

33
"вырождением"), то tg(x) может принимать значения 0, ±1 или ∞, в противном случае можно воспользоваться приведенными формулами
столько раз, сколько раз 4π содержится в аргументе х. Алгоритм можно оформить в виде функции Tg(x). Окончательно имеем:
Sub Tg(x As Single) As Single
Dim p4 As Single, y As Single, z As Single, t As Single, k As Integer, n As Byte
Const pi As Double = 3.141592653586 |
'***ввод константы π |
|
Const eps As Double = 0.0000000001 |
'***константа ε |
|
p4 = pi / 4: y = Abs(x): k = Int(y / p4): t = y - k * p4 |
'***выделение остатка |
|
If Abs(t) < eps Then |
|
'***анализ на вырождение |
n = k Mod 4 |
|
|
Select Case n |
|
|
Case 0: t = 0 |
|
|
Case 2: t = 1E+35 |
|
|
Case Else: t = (-1) ^ n * Sgn(x) |
|
|
End Select |
|
|
Else |
'***невырожденный случай, используем формулы |
|
z = t ^ 2: t = t * ((z - 105) * z + 945) / ((15 * z - 420) * z + 945) |
||
For n = 1 To k |
|
|
t = (1 + t) / (1 - t) |
|
'***пересчет тангенса |
Next |
|
|
End If: Tg = t*Sgn(x) |
|
'***учет знака аргумента |
End Sub . |
|
|
В Mcd имеется встроенная константа π, есть также символ "∞", который обозначает наибольшее действительное число, используемое в вычислениях. Кроме того, конструкцию "Select Case ... End Select" можно реализовать многократным вложением функции if. Наконец, поскольку вырожденный и регулярный случай различаются по-сущес- тву, можно в первом случае просто прервать дальнейшие вычисления.
Tg(x) : = ε ← 10-10
p4 ← π4 y ← |x|
k ← trunc y
p4 t ← y – k∙p4
if |t| < ε
n ← mod(k, 4)
return if [n = 2, ∞, if [n = 0, 0, (-1)n∙sign(x)]]
break z ← t2
(z - 105)∙z + 945 t ← t (15∙z - 420)∙z + 945
for n 1 .. k
t ← 1 + t 1 - t
t∙sign(x)
34
(функция sign(∙) возвращает знак аргумента).
Пример 5. Рассмотрим алгоритм перевода десятичного числа в число системы счисления с основанием q. Если q > 10, то недостающие цифры заменяются буквами латинского алфавита (в 16-чной системе счисления цифры суть 0, 1, ..., 9, A, B, C, D, E, F). Так как латинский алфавит содержит 26 букв, то в нашем примере q ≤ 36. Если десятичное число N натуральное, то соответствующее его q-представле- ние получается из цепочки равенств
N= N1q + R1 (N1 – частное от деления N на q, R1 – остаток)
= (N2q + R2)q + R1 = ... + R3q2 + R2q + R1,
поэтому достаточно делить последовательно число N на q до нуля и выписывать все остатки. Если положительное число N < 1, то цифры Rk нового представления получаются как Rk = [Nk-1q], Nk = {Nk-1q} – целая и дробная часть произведения Nk-1q, N0 = N. Знак числа можно учесть при выводе результата. Построим функцию, возвращающую представление десятичного числа а в системе счисления с основанием q с числом знаков дробной части не более десяти. Полезно в функциях предусмотреть "нештатную" ситуацию. Будем считать выбор основания q "незаконным", если q < 2 или q > 36. В Mcd при отсутствии определения типов следует еще позаботиться о целочисленном q. В VBA такая необходимость отсутствует. Результат должен быть текстовым выражением.
Function Transform(ByVal a As Double, q As Byte) As String Dim u As Long, v As Double, z As integer, r A Byte, s As String
Const B As String = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" If q < 2 Or q > 36 Then
MsgBox "Illegal Basis", vbExclamation, "ERROR": Exit Function
End If |
|
If a = 0 Then |
|
Transform = "0": Exit Function |
|
End If |
|
z = Sgn(a): a = Abs(a): u = Int(a): v = a – u |
'***отделение целой и дробной частей |
While u > 0 |
'***преобразование целой части |
r = u Mod q |
'***получение остатка |
u = u \ q |
|
s = s & Mid(B, r+1, 1) |
'***добавление очередного знака |
Wend |
|
If v > 0 Then |
'***преобразование дробной части |
s = s & "." For u = 1 to 10
v = v*q: r = Int(v): v = v - r: s = s & Mid(B, r+1, 1) Next
End If
If z < 0 Then s = "-" & s Transform = s
End Function .

35
Соответствующий Mcd-вариант функции имеет вид:
Transform(a, q) : = B ←"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
if q < 2 q > 36 q ≠ trunc(q) return "Illegal arguments" break
return "0" if a = 0 otherwise
z ← sign(a) a ← |a|
u ← trunc(a) v ← a – u
s ← "" while u > 0
r ← mod(u, q)
u ← trunc u
q
s ← concat(substr(B, r, 1), s) if v > 0
s ← concat(s, ".")
for u 0 .. 9 v ← v∙q
r ← trunc(v) v ← v – r
s ← concat(s, substr(B, r, 1)) s ← concat("-", s) if z < 0
s
Циклы, как и условные операторы, легко вкладываются друг в друга, причем вложенные циклы должны различаться параметрами и во внутреннем цикле (равно и в теле цикла) не следует менять параметры (внешнего) цикла.
Пример 6. Есть следующе "простое" правило сокращения простых дробей – зачеркивание одинаковых цифр числителя и знамена-
теля, например, |
20 |
= 2, |
64 |
= |
4 |
= 4 (все правильно!). Найти все дроби с |
10 |
16 |
1 |
двузначными числителями и знаменателями, для которых это правило применимо (исключая равные числители и знаменатели, а также сокращение на ноль). Задачу легко решить с помощью VBA-кода:
Dim m As Byte, n As Byte, a As Byte, b As Byte, c As Byte, d As Byte, q As Single For m = 10 To 99
For n = m + 1 To 99
a = m \ 10: b = m Mod 10
c = n \ 10: d = n Mod 10 If b <> 0 Then
If a = c Then q = d / b
ElseIf a = d Then q = c / b
36
ElseIf b = c Then
q = d / a
ElseIf b = d Then
q = c / a
Else
q = 0
End If
End If
If q = n / m Then MsgBox Format(n) & "/" & Format(m)
Next
Next .
В результате будут выданы дроби: |
64 |
, |
95 |
, |
65 |
и |
98 |
. Обсуждение этой |
16 |
19 |
26 |
49 |
программы в Mcd отложим до будущего.
Упражнение 1. Какими цифрами следует заменить буквы в идеограмме СУК × СУК = БАРСУК, чтобы результат был верен?
Пример 7. Найти все трехзначные числа (числа Армстронга), которые равны сумме кубов своих цифр. Здесь придется вложить друг в друга три цикла:
Dim i As Integer, j As Integer, k As Integer
For i = 1 To 9
For j = 1 To 9
For k = 1 To 9
If i^3 + j^3 + k^3 = (10 * i + j) * 10 + k Then _
MsgBox Format((10 * i + j) * 10 + k)
Next k
Next j
Next i
(бывает полезно для контроля после "Next" через пробел указывать имя счетчика цикла, как выше; необходимости, однако, в этом нет).
В вычислительной практике циклы весьма успешно применяются при анализе текстов, при нахождении значений бесконечных сумм, произведений, предельных результатов итерационных и рекурсивных алгоритмов, и т.д.
Пример 8. Построим функцию для вычисления биномиальных
|
|
|
0, если m < n |
|
|||
коэффициентов Cn |
|
|
|
|
|
|
|
= |
m |
= 1 при m = n или n = 0, |
Конечно, можно бы |
||||
m |
|
n |
|
|
m! |
в ост. сл. |
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
n!(m - n)! |
|
|
в циклах пересчета вычислять факториалы. Но легко заметить, что факториалы можно определить рекурсивно (через самих себя) по форму-
1 при m = 0,
ле m! = (m-1)!∙m иначе. Это уже подразумевает наличие пересчета. Аналогично можно поступить и с биномиальными коэффициентами,
37
|
|
|
0, если m < n |
||
определив их следующим образом: Cn |
|
|
|
||
= |
m |
= 1 при m = n или n = 0, |
|||
m |
|
n |
|
m |
Cmn -1 в ост. сл. |
|
|
|
|
||
|
|
|
m-n |
Таким образом, функцию можно оформить кодом:
Function C(m As Integer, n As Integer) As Integer
Dim i As Integer, r As Integer
Select Case m
Case Is < n: r = 0
Case n: r = 1
Case Else: r = 1
For i = n + 1 To m: r = r * i / (i - n): Next
End Select: C = r
End Function .
VBA и Mcd позволяют решить задачу еще проще – в обоих системах допускается рекурсивное определение функции и подпрограммы "через себя" – внутри тела функция или подпрограмма вызывает себя с другими параметрами (глубина рекурсии определяется допустимой памятью стека). В VBA соответствующий код примет вид:
Function C(m As Integer, n As Integer) As Integer
Dim i As Integer, r As Integer
Select Case m |
|
Case Is < n: r = 0 |
|
Case n: r = 1 |
|
Case Else: r = С(m – 1, n) * m / (m – n) |
'***рекурсия |
End Select: C = r |
|
End Function . |
|
В Mcd определение функции выглядит еще проще:
|
|
m |
|
C(m, n) : = if m < n, 0, if m = n, 1, C(m - 1, n)∙ |
m - n |
. |
Пример 9. Рассмотрим алгоритм Эйлера для нахождения наибольшего общего делителя GCD двух натуральных чисел m и n: если n делится на m, то DCD = m, иначе следует получить остаток r от деления m на n и искать наибольший общий делитель чисел n и r. Приведем соответствующие коды функций в VBA и Mcd:
VBA: Function GCD(m As Integer, n As Integer) As Integer
If n Mod m = 0 Then GCD = m Else GCD = GCD(n Mod m, m) End Function,
Mcd: GCD(m,n) : = if(mod(n,m) = 0, m,GCD(mod(n,m), m)).
При достаточно глубокой рекурсии внутренние переменные определяются многократно, поэтому для определения этих переменных целесообразно использовать инструкцию Static вместо Dim. Таким

38
образом определенные переменные сохраняются до конца работы функции (подпрограммы) и, следовательно, отпадает необходимость их переопределения. Рекурсии, несмотря на их внешнюю привлекательность и безусловное удобство, содержат скрытые опасности (поскольку определяющие их циклы не выписаны явно). Одна из них – слишком глубокая (бесконечная) рекурсия, полностью "ломающая" программу. Тем не менее, встречаются ситуации, в которых без рекурсий обойтись трудно и нет нужды пренебрегать ими без особых на то причин.
Упражнение 2. Записать определение функции
x2 при x ≤ 0.1,
f(x) = x + с помощью рекурсии и без нее (цик-
лом с условием).
Пример 10. Текст (строковое выражение) содержит целое число, цифры которого перемешаны с другими символами. Найти квадрат этого числа. Задача решается VBA-кодом:
Function AnalTxt(txt As String) As Variant |
|
|
Dim s As String*1, i as integer, m As Integer, n As Long |
|
|
m = 1: n = 0 |
|
|
For i = 1 To Len(txt): s = Mid(txt, i, 1) |
|
|
Select Case s |
|
|
Case "0" To "9": n = 10*n + VAL(s) |
'***формирование числа |
|
Case "-": If (m > 0) And (n = 0) Then m = -1 |
'***знак числа |
|
End Select |
|
|
Next
If n = 0 Then AnalTxt = "Чисел нет." Else AnalTxt = n*n
End Function,
или Mcd-кодом:
AnalTxt(txt) : = m ← 1 n ← 0
for i 1 .. strlen(txt)
t ← substr(txt,i – 1, 1)
n ← 10∙n + str2num(t) if search("0123456789",t,0) ≥ 0
m ← -1 if t = "-" m = 1 n = 0 othetwise if(n = 0, "no numbers", n2)
Так, например, при txt = "ква 7+ числа-5-то" функция возвратит результат 5625.
В этом примере впервые встретилось использование в VBA типа Variant. Объекты этого типа могут содержать информацию произвольного базового типа. В данном случае этот тип позволяет функции возвращать как текстовую, так и числовую информацию. В Mcd вид

39
возвращаемого результата определяется самой задачей и специально не оговаривается.
Если бы потребовалось из текстовой информации извлечь последовательность целых чисел, разделенных произвольными символами, и просуммировать эти числа, то код программы был бы несколько иной. Постройте работающий код в качестве упражнения.
Как уже отмечалось, с помощью циклов удобно вычислять всевозможные суммы и произведения, особенно когда вычисления следует проводить с заданной точностью. При этом полезно сначала провести предварительный анализ, выяснить алгоритм изменения слагаемых (сомножителей), затем использовать этот алгоритм при программировании решения конкретного задания.
Пример 11. Вычислить приближенное значение cos(x) с помощью
степенного ряда cos(x) = ∞ |
|
(-1)n |
x2n |
с погрешностью не более |
||||||
|
|
|||||||||
|
|
n = 0 |
|
(2n)! |
|
|
|
|
||
|
|
|
|
|
|
|
|
|
||
= 0.00001. Здесь первые несколько слагаемых ряда n∞= 0 qn |
можно |
|||||||||
записать в виде: 1 - x2 |
+ x4 |
- x6 |
+ … Очевидно, qn = (-1)n |
x2n |
и |
qn-1 раз- |
||||
(2n)! |
||||||||||
2 |
4! |
6! |
|
|
|
|
|
|
- x2
личаются лишь множителем (2n - 1) 2n , поэтому каждое новое слага-
емое получается из предыдущего умножением на дробь указанного вида. Задачу, таким образом, можно решить кодом в VBA:
Function Cos_x(x As Double) As Double
Din n As Integer , sum As Double, p As Double, q As Double Const eps As Double = 0.0000001
sum = 1 : p = - x * x: q = 1
'***задано первое слагаемое и начальное значение суммы
Do
n = n + 2 : q = p * q / ( n * ( n - 1)) : sum = sum + q Loop Until Abs(q) < eps
Cos_x = sum End Function.
И кодом Mcd:
Cos_x(x) : = sum ← 1
ε ← 0.0000001 q ← 1
n ← 0 while |q| > ε
n ← n + 2
q ← -x2∙q n∙(n - 1)
sum ← sum + q sum

40
Пример 12. Тот же косинус может быть представлен произведе-
∞ |
1 - |
4x2 |
|
. При отсутствии сомножителей произ- |
нием cos(x) = П |
|
|||
2 |
2 |
|||
n = 1 |
|
π (2n - 1) |
ведение по умолчанию считается равным единице. Далее, как и выше, следует вычислять в цикле каждый сомножитель и домножать на него предыдущий результат до тех пор, пока этот сомножитель не будет отличаться от единицы меньше допустимой погрешности. Приведем без пояснений коды соответствующих функций в VBA и Mcd.
Function CosProd(x As Double) As Double |
CosProd(x) : = |
|
n ← 0 |
||||||||||||
|
|||||||||||||||
Dim prod As Double, r As Double |
|
|
|
|
prod ← 1 |
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4∙x2 |
Dim n As Integer, q As Double |
|
|
|
|
r ← π2 |
||||||||||
Const pi As Double = 3.141592653589 |
|
|
|
|
q ← 0 |
||||||||||
prod = 1: r = 4*x^2 / pi^2 |
|
|
|
|
while |q – 1| > 10-6 |
||||||||||
Do: n = n + 1: q = 1 - r / (2*n - 1)^2 |
|
|
|
|
|
n ← n + 1 |
|||||||||
prod = prod * q |
|
|
|
|
|
|
|
|
|
q ← 1 - r∙(2∙n - 1)-2 |
|||||
Loop Until Abs(q – 1) < 0.000001 |
|
|
|
|
|
prod ← prod∙q |
|||||||||
CosProd = prod |
|
|
|
|
|
|
|
|
|
|
|
prod |
|||
End Function |
|
|
|
|
|
|
|
|
|
|
|
|
|
||
Пример 13. Рассмотрим более сложный случай. Требуется вычи- |
|||||||||||||||
слять значения функции, представленной бесконечным рядом |
|||||||||||||||
|
∞ |
|
|
[ n] |
|
|
2n+1 |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
||||||
f(x) = ∑ |
|
(-1) |
|
n! x |
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
, |
где [ ∙ ] обозначает целую часть чи- |
||||
n = 0 3n |
2 |
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
||||||||||
|
|
3 - |
3 + ... + 3 |
|
|
|
|
сла, в знаменателе – |
ровно n вложенных (слагаемых) корней. Здесь |
|||||||||||||||
|
|
|
|
|
|
|
|
|
|
pn |
x2n+1 |
n! |
|
|
||
каждое слагаемое |
qn |
есть дробь вида |
|
|
|
, где pn = |
|
|
, а |
rn = |
||||||
|
|
|
3n2 |
|
||||||||||||
3 - rn |
|
|||||||||||||||
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
|||||||||
3 + |
3 + ... 3 |
(n вложенных корней, r0 = 0). Можно заметить, |
что |
|||||||||||||
|
|
x2 |
n |
|
|
|
|
|
|
|
|
|
|
|
||
pn = pn-1 |
mn , где m1 = 3 и mn = 9∙mn-1. На каждом этапе, конечно же, ну- |
жно вычислять dn =n и ее целую часть для определения знака слагаемого. Теперь все готово. Код функции в VBA примет следующий вид.
Function f(x As Double) As Double
Dim n As Integer, p As Double, q As Double, r As Double Dim m As Long, sum As Double, d As Double, k As Integer Const eps As Double = 0.000001
m = 3: p = x: sum = x / Sqr(3) Do: n = n + 1: d = Sqr(n)
If (Int(d) Mod 2) > 0 Then k = -1 Else k = 1
p = p*d*x*x / m: r = Sqr(3 + r): q = k*p / Sqr(3 - r) m = 9*m: sum = sum + q
Loop Until Abs(q) < eps End Function .