Главная / Программирование /
Программирование / Тест 69
Программирование - тест 69
Упражнение 1:
Номер 1
Рассмотрим следующий фрагмент программы:
утверждение: A(x)
цикл пока B(x)
| инвариант: A(x)
| x := T(x)
конец цикла
Здесь через A(x)
и B(x)
обозначены условия, зависящие от переменной x
.
Какое условие выполняется по окончании цикла?
Ответ:
 (1)
Условие (не A(x) и B(x))
.
 
 (2)
Условие (A(x) и B(x))
.
 
 (3)
Условие (A(x) и не B(x))
.
 
 (4)
Условие (не A(x) и не B(x))
.
 
Номер 2
Выполняется ли инвариант цикла в процессе выполнения
тела цикла?
Ответ:
 (1)
Да, выполняется.
 
 (2)
Не обязательно.
 
Номер 3
Укажите, в какие моменты работы программы выполняется
инвариант цикла.
Ответ:
 (1)
Перед первым выполнением тела цикла и после
последнего выполнения тела цикла.
 
 (2)
Перед каждым выполнением тела цикла.
 
 (3)
Перед каждым выполнением тела цикла и после
каждого выполнения тела цикла.
 
Упражнение 2:
Номер 1
Пусть целочисленная переменная n
содержит некоторое положительное целое число.
Указать, что вычисляет следующая функция f(n)
:
int f(int n) {
int s = 2; int k = 0;
while (s <= n) {
// Invariant: s == 2^(k+1)
s *= 2; ++k;
}
return k;
}
Ответ:
 (1)
Целую часть квадратного корня из n
.
 
 (2)
Целую часть от log2 n
.
 
 (3)
Целую часть кубического корня из n
.
 
 (4)
Целую часть от en
.
 
 (5)
Целую часть от 2n
.
 
Номер 2
Пусть целочисленная переменная n
содержит некоторое положительное целое число.
Указать, что вычисляет следующая функция f(n)
:
int f(int n) {
int x = 1; int y = 4;
while (y <= n) {
// Invariant: y == (x+1)^2
++x;
y += 2*x+1;
}
return x;
}
Ответ:
 (1)
Целую часть от n/2
;
 
 (2)
Целую часть квадратного корня из n
.
 
 (3)
Целую часть от log2 n
.
 
 (4)
Целую часть кубического корня из n
.
 
 (5)
Целую часть от en
.
 
 (6)
Целую часть от 2n
.
 
Номер 3
Пусть целочисленная переменная n
содержит некоторое положительное целое число.
Указать, что вычисляет следующая функция f(n)
:
int f(int n) {
int s = 10; int k = 0;
while (s <= n) {
// Invariant: s == 10*(k+1)
s += 10; ++k;
}
return k;
}
Ответ:
 (1)
Целую часть частного n/10
.
 
 (2)
Произведение 10*n
.
 
 (3)
Целую часть десятичного логарифма log10 n
.
 
 (4)
Целую часть кубического корня из n/10
.
 
 (5)
Целую часть от 210*n
.
 
Упражнение 3:
Номер 1
Рассмотрим следующую функцию, аргументами которой
являются два целых неотрицательных числа:
int f(int m, int n) {
int a = m, b = n;
int p = 0;
while (b != 0) {
if (b%2 == 0) { // b четное
b /= 2;
a *= 2;
} else { // b нечетное
--b;
p += a;
}
}
return p;
}
Какое условие является инвариантом цикла?
Ответ:
 (1)
Равенство ab p = mn
 
 (2)
Равенство a*b + p = m*n
 
 (3)
Равенство ba p = mn
 
 (4)
Равенство a/b + p = m*n
 
 (5)
Равенство a + b + p = m*n
 
Номер 2
Рассмотрим следующую функцию, аргументами которой
являются два целых неотрицательных числа:
int f(int m, int n) {
// дано: m >= 0 и n >= 0
int a = m; int b = n;
int c = 1;
while (a != 0 && b != 0) {
if (a%2 == 0 && b%2 == 0) {
// a и b четные
a /= 2; b /= 2;
c *= 2;
} else if (a%2 == 0) {
// a четное, b нечетное
a /= 2;
} else if (b%2 == 0) {
// a нечетное, b четное
b /= 2;
} else {
// a и b нечетные
if (a > b) {
a -= b;
} else {
b -= a;
}
}
} // end while
return c*(a + b);
}
Какое условие является инвариантом цикла?
(Через НОД и НОК обозначены наибольший общий делитель и
наименьшее общее кратное.)
Ответ:
 (1)
Равенство НОД(a, b) = НОД(m, n)*c
.
 
 (2)
Равенство НОК(m, n) = c*(a + b)
.
 
 (3)
Равенство НОД(a,b)*c = НОД(m, n)
.
 
 (4)
Равенство НОД(a,b) = НОД(m, n)*c
.
 
Номер 3
Рассмотрим следующий фрагмент программы, вычисляющей
частное q
и остаток r
от деления
целых чисел a
, b
:
// дано: целые числа a >= 0, b > 0
int a, b;
. . .
int q = 0, r = a;
int e = 1, m = b;
while (r >= b) {
if (2*m <= r) {
e *= 2; m *= 2;
} else if (m > r) {
e /= 2; m /= 2;
} else {
// утверждение: m <= r && r < 2*m
q += e; r -= m;
}
}
// q и r - частное и остаток от деления a на b
Какое условие является инвариантом цикла?
Ответ:
 (1)
Число e
является степенью двойки, а также
выполняются равенства
a - q*b == r
и
m == e*b
.
 
 (2)
Число e
является степенью двойки, а также
выполняются равенства
a - q*m == r
и
m == e*b
.
 
 (3)
Число e
является степенью двойки, а также
выполняются равенства
a + q*b == r
и
m == e*b
.
 
 (4)
Число e
является степенью двойки, а также
выполняются равенства
a - q*m == r
и
m == e+b
.
 
Упражнение 4:
Номер 1
Какое утверждение является инвариантом для следующего
фрагмента программы (т.е. из справедливости утверждения
до выполнения фрагмента программы вытекает справедливость утверждения
после выполнения)? Предполагается, что значение переменной
n
неотрицательно.
double r, x; int n;
. . .
r *= x*x;
r /= ((n+1)*(n+2));
n += 2;
Ответ:
 (1)
Утверждение r == xn/n!
,
где восклицательным знаком обозначен факториал числа n
.
 
 (2)
Утверждение
r == xn/((n+1)*(n+2))
.
 
 (3)
Утверждение r == (x+n)/n!
,
где восклицательным знаком обозначен факториал числа n
.
 
 (4)
Утверждение
r == xn/(n+1)!
.
 
Номер 2
Какое утверждение является инвариантом для следующего
фрагмента программы (т.е. из справедливости утверждения
до выполнения фрагмента программы вытекает справедливость утверждения
после выполнения)? Предполагается, что n > 0
.
double r, x; int n;
. . .
r *= -x;
r *= n/(n+1);
++n;
Ответ:
 (1)
Утверждение
r == (-1)n*xn/n!
,
где восклицательным знаком обозначен факториал числа n
.
 
 (2)
Утверждение
r == (-1)n-1*xn/n
.
 
 (3)
Утверждение
r == (-1)n+1*xn/n!
,
где восклицательным знаком обозначен факториал числа n
.
 
 (4)
Утверждение
r == (-1)n+1*xn/n
.
 
Номер 3
Какое утверждение является инвариантом для следующего
фрагмента программы (т.е. из справедливости утверждения
до выполнения фрагмента программы вытекает справедливость утверждения
после выполнения)? Предполагается, что
n
не меньше k
.
Восклицательным знаком обозначается операция вычисления факториала.
int n, k, c;
. . .
c *= (n+1);
c /= (n+1-k);
++n;
Ответ:
 (1)
Утверждение
c = n! / (k! * (n-k)!)
.
 
 (2)
Утверждение
c = (n+k)! / (n! * (n-k)!)
.
 
 (3)
Утверждение
c = (n-k)! / (k! * (n+k)!)
.
 
 (4)
Утверждение
c = (n+k)! / (k! * (n-k)!)
.
 
Упражнение 5:
Номер 1
Оценить сверху время работы (т.е. количество
выполнений тела цикла) алгоритма Евклида
вычисления наибольшего общего делителя двух целых чисел:
int gcd(int m, int n) {
// дано: целые числа m, n, хотя бы одно отлично от нуля
// надо: вычислить НОД пары (m, n)
int a = m, b = n;
while (b != 0) {
// Invariant: НОД(a, b) == НОД(m, n)
int r := a % b; // находим остаток от деления a на b
a = b; b = r; // заменяем пару (a, b) на (b, r)
}
return a; // ответ = a
}
Ответ:
 (1)
Время работы не больше, чем
C*log2max(m, n)
,
где C
- некоторая константа
(т.е. время пропорционально количеству цифр в двоичной или
десятичной записи максимального из чисел m
, n
).
 
 (2)
Время работы не больше, чем
C*r
, где C
- некоторая константа,
r
-
квадратный корень из max(m, n)
(т.е. время пропорционально квадратному корню из максимального
числа).
 
 (3)
Время работы не больше, чем
C*max(m, n)
,
где C
- некоторая константа
(т.е. время пропорционально максимальному из чисел
m
, n
).
 
Номер 2
Оценить сверху время работы (т.е. количество
выполнений тела цикла) алгоритма быстрого возведения в степень:
int fastPow(double a, int n) {
// дано: основание a и показатель степени n >= 0
// надо: вычислить a в степени n
double b = a, p = 1.0; int k = n;
while (k > 0) {
// Invariant: b^k * p == a^n
if (k%2 == 0) {
// k четное
k /= 2;
b *= b;
} else {
// k нечетное
--k;
p *= b;
}
}
return p;
}
Ответ:
 (1)
Время работы не больше, чем
C*n
,
где C
- некоторая константа
(т.е. время пропорционально числу n
).
 
 (2)
Время работы не больше, чем
C*log2 n
,
где C
- некоторая константа
(т.е. время пропорционально количеству цифр в двоичной или
десятичной записи числа n
).
 
 (3)
Время работы не больше, чем
C*r
, где C
- некоторая константа,
r
- квадратный корень из числа n
(т.е. время пропорционально квадратному корню из n
).
 
Номер 3
Оценить сверху время работы (т.е. количество
выполнений тела цикла) алгоритма
приблизительного вычисления логарифма:
double myLog(double x, double a, double eps) {
// дано: x > 0, a > 1, eps > 0
// надо: вычислить log_a x с точностью eps
double y = 0.0, z = x, t = 1.0;
while (
fabs(t) > eps ||
x <= 1.0/a ||
z >= a
) {
// Invariant: a^y * z^t == x
if (z >= a) {
z /= a; y += t;
} else if (z <= 1.0/a) {
z *= a; y -= t;
} else {
z *= z; t /= 2.0;
}
}
return y;
}
Ответ:
 (1)
Время работы не больше, чем
C*log2 [x+1]
,
где C
- некоторая константа,
[x+1]
- целая часть числа x+1
(т.е. время пропорционально количеству цифр
в двоичной или десятичной записи
целой части числа x
).
 
 (2)
Время работы не больше, чем
C*log2[x+1]*log2(1/eps)
,
где C
- некоторая константа, а квадратные скобки
обозначают целую часть.
Иначе говоря, время пропорционально количеству цифр
в двоичной или десятичной записи
целой части x
, умноженному на требуемое количество значащих
цифр в дробной части результата.
 
 (3)
Время работы не больше, чем
C*log2[x+1] + log2(1/eps)
,
где C
- некоторая константа, а квадратные скобки
обозначают целую часть. Иначе говоря,
время пропорционально сумме количества цифр
в двоичной или десятичной записи
целой части x
и требуемого количества значащих
цифр в дробной части результата.
 
Упражнение 6:
Номер 1
Пусть f(x)
- целочисленная функция от целочисленного
аргумента. Определить,
содержит ли следующий фрагмент программы ошибку
(т.е. действительно ли тело цикла сохраняет инвариант):
// Программа корень функции
int a, b, c;
. . .
// утверждение: a < b && f(a)*f(b) <= 0
// Значения функции на концах отрезка [a,b] разных знаков
while (b - a > 1) {
// Invariant: f(a)*f(b) <= 0
// Делим отрезок [a, b] пополам
c = (a + b)/2; // c - целая часть (a+b)/2
if (f(a) * f(c) < 0) {
b = c; // выбираем левую половину отрезка
} else {
a = c; // выбираем правую половину
}
}
// утверждение: a == b-1 &&
// f(a)*f(b) <= 0
Ответ:
 (1)
Ошибки нет, фрагмент программы корректный.
 
 (2)
Фрагмент программы содержит ошибку.
 
Номер 2
Пусть a
- целочисленный массив размера n
(индекс элементов меняется от 0 до n-1
),
элементы которого строго возрастают:
a[0] < a[1] <... < a[n-1]
.
Определить, содержит ли следующий фрагмент программы ошибку
(т.е. действительно ли тело цикла сохраняет инвариант):
// Программа Поиск
int n; int *a;
. . .
// дано: целое n;
// целочисленный массив a[n],
// элементы которого строго возрастают
// a[0] < a[1] < ... < a[n-1]
// надо: найти элемент x в массиве
int x; // искомый элемент
. . . // рассматриваются исключительные случаи
// общий случай
// утверждение: a[0] < x && x <= a[n-1];
int b = 0; int e = n - 1;
while (e - b > 1) {
Invariant: a[b] < x && x <= a[e]
int c := (a + b)/2; // c - целая часть (a+b)/2
if (x < a[c]) {
e = c; // выбираем левую половину отрезка
} else {
b = c; // выбираем правую половину
}
}
// утверждение: b == e - 1 &&
// a[b] < x && x <= a[e]
Ответ:
 (1)
Ошибки нет, фрагмент программы корректный.
 
 (2)
Фрагмент программы содержит ошибку.
 
Номер 3
Пусть f(x)
- вещественная функция функция
от вещественного
аргумента. Определить,
содержит ли следующий фрагмент программы ошибку
(т.е. действительно ли тело цикла сохраняет инвариант):
// Программа корень функции
double a, b, c;
double eps = 0.000001;
. . .
// утверждение: a < b && f(a)*f(b) <= 0.0
// Значения функции на концах отрезка [a, b] разных знаков
while (b - a > eps) {
// Invariant: f(a)*f(b) <= 0.0
// Делим отрезок [a, b] пополам
c = (a + b)/2.0; // c - середина отрезка [a, b]
if (f(a) * f(c) < 0.0) {
b = c; // выбираем левую половину отрезка
} else {
a = c; // выбираем правую половину
}
}
// утверждение: b - a <= eps &&
// f(a)*f(b) <= 0.0
Ответ:
 (1)
Ошибки нет, фрагмент программы корректный.
 
 (2)
Фрагмент программы содержит ошибку.