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

Информатика

.pdf
Скачиваний:
24
Добавлен:
03.05.2015
Размер:
3.59 Mб
Скачать

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. Таким

2 x
f(x-1) - f иначе
2

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 .