1.1.1.
Даны две целые переменные a, b. Составить фрагмент
программы, после исполнения которого значения переменных
поменялись бы местами (новое значение a равно
старому значению b и наоборот).
Решение. Введем дополнительную целую переменную t.
t := a; a := b; b := t;
Попытка обойтись без дополнительной переменной, написав
a := b; b := a;
не приводит к цели (безвозвратно утрачивается начальное
a ).
1.1.2.
Решить предыдущую задачу, не используя дополнительных
переменных (и предполагая, что значениями целых переменных
могут быть произвольные
Решение.Начальные значения a и b обозначим a0, b0.
a := a + b; {a = a0 + b0, b = b0}
b := a - b; {a = a0 + b0, b = a0}
a := a - b; {a = b0, b = a0}
1.1.3.
Дано а и натуральное (целое
неотрицательное) число n. Вычислить $$a^n$$.
Другими словами, необходимо составить программу, при
исполнении которой значения переменных а и n не
меняются, а значение некоторой другой переменной
(например, b ) становится равным $$a^n$$.
(При этом разрешается использовать и другие переменные.)
Решение. Введем целую переменную k, которая меняется
от 0 до n, причем поддерживается такое свойство: $$b = a^k$$ ).
k := 0; b := 1;
{b = a в степени k}
while k <> n do begin
| k := k + 1;
| b := b * a;
end;
Другое решение той же задачи:
k := n; b := 1;
{a в степени n = b * (a в степени k)}
while k <> 0 do begin
| k := k - 1;
| b := b * a;
end;
1.1.4.Решить предыдущую задачу, если требуется, чтобы число
действий (выполняемых
Решение. Внесем некоторые изменения во второе из предложенных решений предыдущей задачи:
k := n; b := 1; c:=a;
{a в степени n = b * (c в степени k)}
while k <> 0 do begin
| if k mod 2 = 0 then begin
| | k:= k div 2;
| | c:= c*c;
| end else begin
| | k := k - 1;
| | b := b * c;
| end;
end;
Каждый второй раз (не реже) будет выполняться первый
вариант k нечетно, то после
k уменьшается по крайней мере вдвое.
1.1.5.Даны а, b. Вычислить +, -, =, <>.
Решение.
k := 0; c := 0;
{инвариант: c = a * k}
while k <> b do begin
| k := k + 1;
| c := c + a;
end;
{c = a * k и k = b, следовательно, c = a * b}
1.1.6.
Даны натуральные числа а и b. Вычислить их сумму $$а+b$$. Использовать
Решение.
...
{инвариант: c = a + k}
...
1.1.7.
Дано натуральное (целое неотрицательное) число а
и целое положительное число d. Вычислить частное q и r при а на d, не используя
операций div и .
Решение. Согласно определению, $$a=q\cdot d+r$$, $$0 \le r <d$$.
{a >= 0; d > 0}
r := a; q := 0;
{инвариант: a = q * d + r, 0 <= r}
while not (r < d) do begin
| {r >= d}
| r := r - d; {r >= 0}
| q := q + 1;
end;
1.1.8.Дано натуральное $$n$$, вычислить $$n$$! ( $$0!=1$$, $$n! =n\cdot (n-1)$$!).
1.1.9.Последовательность Фибоначчи определяется так: $$a_0=0$$, $$a_1=1$$, $$a_k= a_{k-1} +a_{k-2}$$ при $$k\ge 2$$. Дано $$n$$, вычислить $$a_n$$.
1.1.10. Та же задача, если требуется, чтобы число операций было пропорционально $$log n$$. (Переменные должны быть целочисленными.)
Указание.
Пара соседних чисел Фибоначчи получается из предыдущей
- так что задача сводится к возведению
1.1.11. Дано натуральное n, вычислить
$$\frac{1}{0!} + \frac{1}{1!}+ \ldots+\frac{1}{n!}.$$1.1.12.
То же, если требуется, чтобы количество операций
(выполненных команд
Решение.
1.1.13.Даны два a и b, не равные нулю
одновременно. Вычислить НОД(a,b) - наибольший общий а и b.
Решение. Вариант 1.
if a > b then begin
| k := a;
end else begin
| k := b;
end;
{k = max (a,b)}
{инвариант: никакое число, большее k, не является
общим делителем}
while not ((a mod k = 0) and (b mod k = 0)) do begin
| k := k - 1;
end;
{k - общий делитель, большие - нет}
Вариант 2 (НОД(0,0)=0. Тогда НОД(a,b) =НОД(a-b,b) = НОД(a,b-a) ; НОД(a,0) =НОД(0,a) = a для всех $$a,b\ge 0$$.
m := a; n := b;
{инвариант: НОД (a,b) = НОД (m,n); m,n >= 0 }
while not ((m=0) or (n=0)) do begin
| if m >= n then begin
| | m := m - n;
| end else begin
| | n := n - m;
| end;
end;
{m = 0 или n = 0}
if m = 0 then begin
| k := n;
end else begin {n = 0}
| k := m;
end;
1.1.14.Написать модифицированный вариант алгоритма Евклида,
использующий соотношения НОД(a,b) = НОД(a
при $$a\ge b$$, НОД(a,b) = НОД(a, b при $$b\ge a$$.
1.1.15.
Даны натуральные a и b, не равные 0
одновременно. Найти d = НОД(a,b) и такие
целые x и y, что $$d = a\cdot x + b\cdot y$$.
Решение. Добавим в p, q, r, s и впишем в m = p*a+q*b ; n = r*a+s*b.
m:=a; n:=b; p := 1; q := 0; r := 0; s := 1;
{инвариант: НОД (a,b) = НОД (m,n); m,n >= 0
m = p*a + q*b; n = r*a + s*b.}
while not ((m=0) or (n=0)) do begin
| if m >= n then begin
| | m := m - n; p := p - r; q := q - s;
| end else begin
| | n := n - m; r := r - p; s := s - q;
| end;
end;
if m = 0 then begin
| k :=n; x := r; y := s;
end else begin
| k := m; x := p; y := q;
end;
1.1.16.Решить предыдущую задачу, используя в
1.1.17.
(Э. Дейкстра) Добавим в u, v, z:
m := a; n := b; u := b; v := a;
{инвариант: НОД (a,b) = НОД (m,n); m,n >= 0 }
while not ((m=0) or (n=0)) do begin
| if m >= n then begin
| | m := m - n; v := v + u;
| end else begin
| | n := n - m; u := u + v;
| end;
end;
if m = 0 then begin
| z:= v;
end else begin {n=0}
| z:= u;
end;
Доказать, что после исполнения алгоритма значение z
равно удвоенному наименьшему общему кратному
чисел a, b: $$z = 2\cdot НОК(a,b)$$.
Решение. Заметим, что величина $$m\cdot u + n\cdot v$$ не меняется в ходе выполнения алгоритма. Остается воспользоваться тем, что вначале она равна $$2ab$$ и что $$НОД (a,b) \cdot НОК (a, b) = ab$$.
1.1.18. Написать вариант алгоритма Евклида, использующий соотношения
$$\begin{multiple} НОД}(2}a}, 2}b})=2}\cdotНОД}(a},b}), \\ НОД}(2}a},b})= НОД}(a},b}) \quad \text {при нечетном b}}, \end{multiple}}$$не включающий 2 и проверку k.)
Решение.
m:= a; n:=b; d:=1;
{НОД(a,b) = d * НОД(m,n)}
while not ((m=0) or (n=0)) do begin
| if (m mod 2 = 0) and (n mod 2 = 0) then begin
| | d:= d*2; m:= m div 2; n:= n div 2;
| end else if (m mod 2 = 0) and (n mod 2 = 1) then begin
| | m:= m div 2;
| end else if(m mod 2 = 1) and (n mod 2 = 0) then begin
| | n:= n div 2;
| end else if (m mod 2=1) and (n mod 2=1) and (m>=n) then begin
| | m:= m-n;
| end else if (m mod 2=1) and (n mod 2=1) and (m<=n) then begin
| | n:= n-m;
| end;
end;
{m=0 => ответ=d*n; n=0 => ответ=d*m}
Оценка числа действий: каждое второе действие делит хотя бы
одно из чисел m и n пополам.
1.1.19.
Дополнить алгоритм предыдущей задачи поиском x и y, для которых $$ax+by=НОД(a,b)$$.
Решение. (Идея сообщена Д. Звонкиным.)
Прежде всего заметим, что одновременное a
и b пополам не меняет искомых x и y. Поэтому можно считать, что с самого начала одно из чисел a
и b нечетно. (Это свойство будет сохраняться и далее.)
Теперь попытаемся, как и раньше, хранить такие числа $$p,q,r,s$$, что$$\begin{align*}
m =ap + bq,\\
n =ar + bs.
\end{align*}$$
Проблема в том, что при m на 2 надо разделить p и q на 2, и они перестанут быть целыми (а станут двоично-рациональными). Двоично-d в виде
комбинации a и b с двоично-рациональными
коэффициентами. Иными словами, мы имеем$$2^{i}d = ax + by$$
для некоторых целых $$x,y$$ и натурального $$i$$.
Что делать, если $$i > 1$$? Если x и y четны, то на 2 можно сократить. Если это не так, положение
можно исправить преобразованием$$\begin{align*}
x\mathbin:=x + b, \\
y\mathbin:=y - a
\end{align*}$$
(оно не меняет $$ax+by$$ ). Убедимся в этом.
Напомним, что мы считаем, что одно из чисел a и b
нечетно. Пусть это будет a. Если при этом y четно,
то и x должно быть четным (иначе $$ax+by$$ будет нечетным). А при
нечетном y a
делает y четным.
1.1.20.Составить программу, печатающую квадраты всех натуральных чисел от 0 до заданного натурального n.
Решение.
k:=0;
writeln (k*k);
{инвариант: k<=n, напечатаны все
квадраты до k включительно}
while not (k=n) do begin
| k:=k+1;
| writeln (k*k);
end;
1.1.21.
Та же задача, но разрешается использовать из арифметических
операций лишь n.
Решение. Введем переменную k_square (k соотношением $${k{\_}square}= k^2$$:
k := 0; k_square := 0;
writeln (k_square);
while not (k = n) do begin
| k := k + 1;
| {k_square = (k-1) * (k-1) = k*k - 2*k + 1}
| k_square := k_square + k + k - 1;
| writeln (k_square);
end;
Замечание. Можно обойтись без
while not (k = n) do begin
| k_square := k_square + k;
| {k_square = k*k + k}
| k := k + 1;
| {k_square = (k-1)*(k-1)+(k-1)=k*k-k}
| k_square := k_square + k;
end;
1.1.22.
Составить программу, печатающую разложение на простые
множители заданного n ;
если $$n = 1$$, печатать ничего не надо).
Решение. Вариант 1.
k := n;
{инвариант: произведение напечатанных чисел и k равно
n, напечатаны только простые числа}
while not (k = 1) do begin
| l := 2;
| {инвариант: k не имеет делителей в интервале (1,l)}
| while k mod l <> 0 do begin
| | l := l + 1;
| end;
| {l - наименьший делитель k, больший 1, следовательно,
| простой}
| writeln (l);
| k:=k div l;
end;
Вариант 2.
k := n; l := 2;
{произведение k и напечатанных чисел равно n; напечатанные
числа просты; k не имеет делителей, меньших l}
while not (k = 1) do begin
| if k mod l = 0 then begin
| | {k делится на l и не имеет делителей,
| | меньших l, значит, l просто}
| | k := k div l;
| | writeln (l);
| end else begin
| | { k не делится на l }
| | l := l+1;
| end;
end;
1.1.23.
Составить программу решения предыдущей задачи, использующую
тот факт, что составное число имеет
Решение. Во втором варианте решения вместо l:=l+1 можно написать
if l*l > k then begin | l:=k; end else begin | l:=l+1; end;
1.1.24. Проверить, является ли заданное натуральное число $$n > 1$$ простым.
1.1.25.
(Для знакомых с основами
(a) Проверить, является ли оно простым (в $$\mathbb{Z}[i]$$ ).
(б) Напечатать его разложение на простые (в $$\mathbb{Z}[i]$$ ) множители.
1.1.26.Разрешим применять команды лишь при $$i=0,1,\allowbreak2,\ldots,9$$. Составить
программу, печатающую десятичную запись заданного
Решение.
base:=1;
{base - степень 10, не превосходящая n}
while 10 * base <= n do begin
| base:= base * 10;
end;
{base - максимальная степень 10, не превосходящая n}
k:=n;
{инвариант: осталось напечатать k с тем же числом
знаков, что в base; base = 100..00}
while base <> 1 do begin
| write(k div base);
| k:= k mod base;
| base:= base div 10;
end;
{base=1; осталось напечатать однозначное число k}
write(k);
Типичная ошибка при решении этой задачи: неправильно
обрабатываются числа с нулями посередине. Приведенный
1.1.27.
То же самое, но надо напечатать десятичную запись
в обратном порядке. (Для $$n=173$$ надо напечатать 371.)
Решение.
k:= n;
{инвариант: осталось напечатать k в обратном порядке}
while k <> 0 do begin
| write (k mod 10);
| k:= k div 10;
end;
1.1.28.
Дано натуральное n. Подсчитать количество решений
Решение.
k := 0; s := 0;
{инвариант: s = количество решений неравенства
x*x + y*y < n c x < k}
while k*k < n do begin
| ...
| {t = число решений неравенства k*k + y*y < n
| с y>=0 (при данном k) }
| k := k + 1;
| s := s + t;
end;
{k*k >= n, поэтому s = количество всех решений
неравенства}
Здесь ... - пока еще не написанный кусок программы,
который будет таким:
l := 0; t := 0;
{инвариант: t = число решений
неравенства k*k + y*y < n c 0<=y<l }
while k*k + l*l < n do begin
| l := l + 1;
| t := t + 1;
end;
{k*k + l*l >= n, поэтому t = число
всех решений неравенства k*k + y*y < n}
1.1.29.
Та же задача, но количество операций должно быть порядка $$\sqrt{n}$$. (В предыдущем решении, как можно
подсчитать, порядка n операций.)
Решение. Нас интересуют точки решетки (с целыми
X ) состоит из
Идея решения состоит в том, чтобы "двигаться вдоль его
границы", спускаясь по верхнему его краю, как по
лестнице. Координаты движущейся точки обозначим <k,l>.
Введем еще одну переменную s и будем поддерживать
истинность такого условия:$$\begin{quote}
\rule{0pt}{0pt}<k,l> находится сразу над k-ым столбцом; \\
s - число точек в предыдущих столбцах.
\end{quote}$$
Формально:
<k,l> не принадлежит X ;X.Обозначим эти условия через (И).
k := 0; l := 0;
while <0,l> принадлежит X do begin
| l := l + 1;
end;
{k = 0, l - минимальное среди тех l >= 0,
для которых <k,l> не принадлежит X}
s := 0;
{инвариант: И}
while not (l = 0) do begin
| s := s + l;
| {s - число точек в столбцах до k-го включительно}
| k := k + 1;
| {точка <k,l> лежит вне X, но, возможно, ее надо сдвинуть
| вниз, чтобы восстановить И}
| while (l <> 0) and (<k, l-1> не принадлежит X) do begin
| | l := l - 1;
| end;
end;
{И, l = 0, поэтому k-ый столбец и все следующие пусты, а
s равно искомому числу}
Оценка числа действий очевидна: сначала мы движемся вверх не более чем на $$\sqrt{n}$$ шагов, а затем вниз и вправо - в каждую сторону не более чем на $$\sqrt{n}$$ шагов.
1.1.30.
Даны n и k, $$n>1$$.
Напечатать k десятичных знаков числа $$1/n$$.
(При наличии двух десятичных разложений выбирается то из
них, которое не содержит девятки в периоде.) Программа
должна использовать только целые переменные.
Решение. Сдвинув в десятичной записи числа $$1/n$$
запятую на k мест вправо, получим число $$10^k/n}$$. Нам надо напечатать его целую часть,
то есть разделить $$10^k$$ на n нацело.
Стандартный способ требует использования больших по
величине чисел, которые могут выйти за границы r:
l := 0; r := 1;
{инв.: напечатано l разрядов 1/n, осталось напечатать
k - l разрядов дроби r/n}
while l <> k do begin
| write ( (10 * r) div n);
| r := (10 * r) mod n;
| l := l + 1;
end;
1.1.31.
Дано
Решение. Период дроби равен периоду в последовательности
остатков (докажите это; в частности, надо доказать, что он
не может быть меньше). Кроме того, в этой
последовательности все периодически повторяющиеся члены
различны, а предпериод имеет длину не более n. Поэтому
достаточно найти $$(n+1)$$ -ый член
последовательности остатков и затем минимальное k, при
котором $$(n+1+k)$$ -ый член совпадает
с $$(n+1)$$ -ым.
l := 0; r := 1;
{инвариант: r/n = результат отбрасывания l знаков в 1/n}
while l <> n+1 do begin
| r := (10 * r) mod n;
| l := l + 1;
end;
c := r;
{c = (n+1)-ый член последовательности остатков}
r := (10 * r) mod n;
k := 1;
{r = (n+k+1)-ый член последовательности остатков}
while r <> c do begin
| r := (10 * r) mod n;
| k := k + 1;
end;
1.1.32.(Сообщил Ю. В. Матиясевич)
Дана функция $$f:\{1\ldots N\} \to\{1\ldots N\}$$ Найти период последовательности $$1,f(1),f(f(1),\ldots $$ Количество действий
должно быть пропорционально суммарной длине предпериода
и периода (эта сумма может быть существенно меньше N ).
Решение. Если отбросить начальный кусок,
последовательность
{Обозначение: f[n,1]=f(f(...f(1)...)) (n раз)}
k:=1; a:=f(1); b:=f(f(1));
{a=f[k,1]; b=f[2k,1]}
while a <> b do begin
| k:=k+1; a:=f(a); b:=f(f(b));
end;
{a=f[k,1]=f[2k,1]; f[k,1] входит в периодическую часть}
l:=1; b:=f(a);
{b=f[k+l,1]; f[k,1],...,f[k+l-1,1] различны}
while a <> b do begin
| l:=l+1; b:=f(b);
end;
{период равен l}
1.1.33.
(Э. Дейкстра)
Функция f с натуральными аргументами
и значениями определена так: $$f(0) = 0$$, $$f(1)=1$$, $$f(2n) = f(n)$$, $$f(2n+1) = f(n) + f(n+1)$$.
Составить программу вычисления $$f(n)$$ по
заданному n, требующую порядка $$log n$$ операций.
Решение.
k := n; a := 1; b := 0;
{инвариант: 0 <= k, f (n) = a * f(k) + b * f (k+1)}
while k <> 0 do begin
| if k mod 2 = 0 then begin
| | l := k div 2;
| | {k=2l, f(k)=f(l), f(k+1) = f(2l+1) = f(l) + f(l+1),
| | f (n) = a*f(k) + b*f(k+1) = (a+b)*f(l) + b*f(l+1)}
| | a := a + b; k := l;
| end else begin
| | l := k div 2;
| | {k = 2l + 1, f(k) = f(l) + f(l+1),
| | f(k+1) = f(2l+2) = f(l+1),
| | f(n) = a*f(k) + b*f(k+1) = a*f(l) + (a+b)*f(l+1)}
| | b := a + b; k := l;
| end;
end;
{k = 0, f(n) = a * f(0) + b * f(1) = b, что и требовалось}
1.1.34. То же, если $$f(0) = 13$$, $$f(1) = 17$$, $$f(2) = 20$$, $$f(3) = 30$$, $$f(2n) = 43\,f(n) + 57\,f(n+1)$$, $$f(2n+1) = 91\,f(n) + 179\,f(n+1)$$ при $$n\ge 2$$.
Указание. Хранить коэффициенты в выражении $$f(n)$$ через три соседних числа.
1.1.35.
Даны а и b, причем $$b>0$$.
Найти частное и a на b,
оперируя лишь с div и , за исключением 2 четных
чисел; число шагов не должно превосходить $$C_1 log(a/b)+C_2$$ для некоторых
Решение.
b1 := b;
while b1 <= a do begin
| b1 := b1 * 2;
end;
{b1 > a, b1 = b * (некоторая степень 2)}
q:=0; r:=a;
{инвариант: q, r - частное и остаток при делении a на b1,
b1 = b * (некоторая степень 2)}
while b1 <> b do begin
| b1 := b1 div 2 ; q := q * 2;
| { a = b1 * q + r, 0 <= r, r < 2 * b1}
| if r >= b1 then begin
| | r := r - b1;
| | q := q + 1;
| end;
end;
{q, r - частное и остаток при делении a на b}
В следующих задачах переменные $$x,y,z$$
предполагаются описанными как (где n - некоторое 0 ), если иное не оговорено явно.
1.2.1.
Заполнить x нулями. (Это означает, что нужно
составить фрагмент программы, после выполнения которого все
значения x[1]..x[n ] равнялись бы нулю, независимо от
начального x.)
Решение.
i := 0;
{инвариант: первые i значений x[1]..x[i] равны 0}
while i <> n do begin
| i := i + 1;
| {x[1]..x[i-1] = 0}
| x[i] := 0;
end;
1.2.2.
Подсчитать количество нулей в массиве x. (Составить
фрагмент программы, не меняющий значения x, после
исполнения которого значение некоторой целой
переменной k равнялось бы числу нулей среди компонент
массива x.)
Решение.
...
{инвариант: k = число нулей среди x[1]...x[i] }
...
1.2.3.Не используя x:=y.
Решение.
i := 0;
{инвариант: значение y не изменилось, x[l]=y[l] при l<=i}
while i <> n do begin
| i := i + 1;
| x[i] := y[i];
end;
1.2.4.Найти x[1]..x[n].
Решение.
i := 1; max := x[1];
{инвариант: max = максимум из x[1]..x[i]}
while i <> n do begin
| i := i + 1;
| {max = максимум из x[1]..x[i-1]}
| if x[i] > max then begin
| | max := x[i];
| end;
end;
1.2.5.Дан x: , причем
известно, что $$x[1]\le x[2]\le\ldots\le x[n]]$$.
Найти количество различных чисел среди элементов этого
массива.
Решение. Вариант 1.
i := 1; k := 1;
{инвариант: k - количество различных среди x[1]..x[i]}
while i <> n do begin
| i := i + 1;
| if x[i] <> x[i-1] then begin
| | k := k + 1;
| end;
end;
Вариант 2. Искомое число на 1 больше количества тех
чисел i из 1..n-1, для которых x[i] не равно x[i+1].
k := 1; for i := 1 to n-1 do begin | if x[i]<> x[i+1] then begin | | k := k + 1; | end; end;
1.2.6.
Дан x: . Найти
количество различных чисел среди элементов этого массива.
(Число действий должно быть порядка $$n^2$$ )
1.2.7. Та же задача, если требуется, чтобы количество действий было порядка $$n log n$$.
Указание.
Смотри лекцию 4 (
1.2.8.Та же задача, если известно, что все элементы массива -
числа от 1 до k и число действий должно быть
порядка $$n+k$$.
1.2.9.
(Сообщил А. Л. Брудно)
Прямоугольное поле $$m\times n$$ разбито на $$mn$$
квадратных клеток. Некоторые клетки покрашены в черный
цвет. Известно, что все черные клетки могут быть разбиты на
несколько непересекающихся и не имеющих общих вершин черных
array [1..m] of array [1..n] of boolean;
подсчитать число черных
Решение. Число
1.2.10.
Дан x[1]..x[n] целых чисел. Не используя других
массивов, переставить элементы массива в обратном порядке.
Решение. Элементы x[i] и x[n+1-i] нужно поменять
местами для всех i, для которых $$i<n+1-i$$, то есть$$$\w{2}\w{i} < \w{n} +
\w{1}$~$\Leftrightarrow$
$\w{2}\w{i}\le \w{n}\hm\Leftrightarrow \w{i} \le \text{n div 2}$$$
for i := 1 to n div 2 do begin | ...поменять местами x[i] и x[n+1-i]; end;
1.2.11.
(Из книги Д. Гриса) Дан x[1]..x[m+n], рассматриваемый как соединение двух его
отрезков: начала x[1]..x[m] длины m и конца x[m+1]..x[m+n] длины n. Не используя дополнительных
массивов, переставить начало и конец.
(Число действий порядка $$m+n$$
Решение.
Вариант 1. Перевернем (расположим в обратном
порядке) отдельно начало и конец массива, а затем перевернем
весь
Вариант 2. (А. Г. Кушниренко)
Рассматривая
Вариант 3. Рассмотрим более общую задачу - обмен двух
участков массива x[p+1]..x[q] и x[q+1]..x[r].
Предположим, что длина левого участка (назовем его $$A$$ ) не
больше длины правого (назовем его $$B$$ ). Выделим
в $$B$$
начало той же длины, что и $$A$$, назовем его $$B_1$$,
а
p := 0; q := m; r := m + n;
{инвариант: осталось переставить x[p+1..q], x[q+1..r]}
while (p <> q) and (q <> r) do begin
| {оба участка непусты}
| if (q - p) <= (r - q) then begin
| | ..переставить x[p+1]..x[q] и x[q+1]..x[q+(q-p)]
| | pnew := q; qnew := q + (q - p);
| | p := pnew; q := qnew;
| end else begin
| | ..переставить x[q-(r-q)+1]..x[q] и x[q+1]..x[r]
| | qnew := q - (r - q); rnew := q;
| | q := qnew; r := rnew;
| end;
end;
Оценка времени работы: на очередном шаге оставшийся для обработки участок становится короче на длину $$A$$ ; число действий при этом также пропорционально длине $$A$$.
1.2.12.Коэффициенты многочлена лежат в массиве a: ( n - x, то есть $$a[n]\,x^n+\ldots+a[1]\,x+a[0]$$.
Решение.
(Описываемый алгоритм называется
k := 0; y := a[n];
{инвариант: 0 <= k <= n,
y= a[n]*(x в степени k)+...+a[n-1]*(x в степени k-1)+...+
+ a[n-k]*(x в степени 0)}
while k<>n do begin
| k := k + 1;
| y := y * x + a [n-k];
end;
1.2.13.
(Для знакомых с основами анализа; сообщил
А. Г. Кушниренко) Дополнить алгоритм
Решение. Добавление нового коэффициента соответствует
переходу от многочлена $$P(x)$$ к
Общее утверждение о сложности вычисления производных таково:
1.2.14.
(В. Баур, Ф. Штрассен)
Дана программа
Указание.
Можно считать, что каждая команда -
1.2.15.В массивах a: и b:
хранятся коэффициенты двух
k и l. Поместить в c: коэффициенты их
произведения. (Числа $$k,l,m$$ - натуральные, $$m=k+l$$ ; элемент массива с индексом i
содержит коэффициент при степени i.)
Решение.
for i:=0 to m do begin | c[i]:=0; end; for i:=0 to k do begin | for j:=0 to l do begin | | c[i+j] := c[i+j] + a[i]*b[j]; | end; end;
1.2.16.
Предложенный выше алгоритм перемножения
Указание.
Представим себе, что надо перемножить два многочлена
степени $$2k$$. Их можно представить в виде$$A(x)\,x^k + B(x) \quad и \quad C(x)\,x^k + D(x).$$
1.2.17.Даны два возрастающих массива x: и y: . Найти
количество общих элементов в этих массивах, то есть
количество тех целых t, для которых $$t = x[i] =
y[j]$$ для некоторых i и j. (Число действий
порядка $$k+l$$.)
Решение.
k1:=0; l1:=0; n:=0;
{инвариант: 0<=k1<=k; 0<=l1<=l;
искомый ответ = n + количество общих
элементов в x[k1+1]...x[k] и y[l1+1]...y[l]}
while (k1 <> k) and (l1 <> l) do begin
| if x[k1+1] < y[l1+1] then begin
| | k1 := k1 + 1;
| end else if x[k1+1] > y[l1+1] then begin
| | l1 := l1 + 1;
| end else begin {x[k1+1] = y[l1+1]}
| | k1 := k1 + 1;
| | l1 := l1 + 1;
| | n := n + 1;
| end;
end;
{k1 = k или l1 = l, поэтому одно из множеств, упомянутых
в инварианте, пусто, а n равно искомому ответу}
Замечание. В третьей k1, l1 ; вторая добавлена для
1.2.18.Решить предыдущую задачу, если про массивы известно лишь, что $$x[1]\le\ldots\le x[k]$$ и $$y[1]\le\ldots\le y[l]$$ (возрастание заменено неубыванием).
Решение. Условие возрастания было использовано в третьей
k1 и l1 на 1, мы
тем самым уменьшали на 1 количество общих элементов
в $$x[k1+1]\ldots x[k]$$
и $$x[l1+1]\ldots x[l]$$. Теперь это придется делать
сложнее.
...
end else begin {x[k1+1] = y[l1+1]}
| t := x [k1+1];
| while (k1<k) and (x[k1+1]=t) do begin
| | k1 := k1 + 1;
| end;
| while (l1<l) and (x[l1+1]=t) do begin
| | l1 := l1 + 1;
| end;
| n := n + 1;
end;
Замечание. Эта программа имеет дефект: при проверке условия$$$$
\w{(k1<k) and (x[k1+1]=t)}
$$$$
(или второго, аналогичного) при ложной первой скобке вторая
окажется бессмысленной (индекс выйдет за границы массива)
и возникнет ошибка.
Некоторые версии A and B, сначала
вычисляют A и при ложном A не вычисляют B. (Так
ведет себя, например, система Turbo
Но если мы не хотим полагаться на такое свойство
используемой нами реализации b:
и напишем:
if k1 < k then b := (x[k1+1]=t) else b:=false;
{b = (k1<k) and (x[k1+1] = t)}
while b do begin
| k1:=k1+1;
| if k1 < k then b := (x[k1+1]=t) else b:=false;
end;
Можно также сделать иначе:
end else begin {x[k1+1] = y[l1+1]}
| if k1 + 1 = k then begin
| | k1 := k1 + 1;
| | n := n + 1;
| end else if x[k1+1] = x [k1+2] then begin
| | k1 := k1 + 1;
| end else begin
| | k1 := k1 + 1;
| | n := n + 1;
| end;
end;
Так будет короче, хотя менее
Наконец, можно увеличить размер массива в его описании, включив в него фиктивные элементы.
1.2.19.
Даны два неубывающих массива x: и y: . Найти
число различных элементов среди $$x[1],\ldots,x[k],y[1],\ldots,y[l]$$. (Число
действий порядка $$k+l$$.)
1.2.20.
Даны два массива $$x[1]\le\ldots\le x[k]$$
и $$y[1]\le\ldots\le y[l]$$. "Соединить" их
в z столько раз, сколько раз он входит в общей
сложности в массивы x и y ). Число действий
порядка m.
Решение.
k1 := 0; l1 := 0;
{инвариант: ответ получится, если к z[1]..z[k1+l1] добавить
справа соединение массивов x[k1+1]..x[k] и y[l1+1]..y[l]}
while (k1 <> k) or (l1 <> l) do begin
| if k1 = k then begin
| | {l1 < l}
| | l1 := l1 + 1;
| | z[k1+l1] := y[l1];
| end else if l1 = l then begin
| | {k1 < k}
| | k1 := k1 + 1;
| | z[k1+l1] := x[k1];
| end else if x[k1+1] <= y[l1+1] then begin
| | k1 := k1 + 1;
| | z[k1+l1] := x[k1];
| end else if x[k1+1] >= y[l1+1] then begin
| | l1 := l1 + 1;
| | z[k1+l1] := y[l1];
| end else begin
| | { такого не бывает }
| end;
end;
{k1 = k, l1 = l, массивы соединены}
Этот процесс можно пояснить так. Пусть у нас есть две стопки карточек, отсортированных по алфавиту. Мы соединяем их в одну стопку, выбирая каждый раз ту из верхних карточек обеих стопок, которая идет раньше в алфавитном порядке. Если в одной стопке карточки кончились, берем их из другой стопки.
1.2.21.
Даны два массива $$x[1]\le\ldots\le x[k]$$
и $$y[1]\le\ldots\le y[l]$$. Найти их "
z
равняется x
и y. Число действий порядка $$k+l$$.
1.2.22.
Даны два массива $$x[1]\le\ldots\le x[k]$$
и $$y[1]\le\ldots\le y[l]$$ и число q. Найти сумму
вида $$x[i]+y[j]$$, наиболее близкую к числу q.
(Число действий порядка k+l, дополнительная память -
фиксированное число целых переменных, сами массивы
менять не разрешается.)
Указание.
Надо найти минимальное
1.2.23. (из книги Д. Гриса) Некоторое число содержится в каждом из трех целочисленных неубывающих массивов $$x[1]\le\ldots\le x[p]$$, $$y[1]\le\ldots\le y[q]$$, $$z[1]\le\ldots\le z[r]$$. Найти одно из таких чисел. Число действий должно быть порядка $$p+q+r$$.
Решение.
p1:=1; q1=1; r1:=1;
{инвариант: x[p1]..x[p], y[q1]..y[q], z[r1]..z[r]
содержат общий элемент}
while not ((x[p1]=y[q1]) and (y[q1]=z[r1])) do begin
| if x[p1]<y[q1] then begin
| | p1:=p1+1;
| end else if y[q1] <z[r1] then begin
| | q1:=q1+1;
| end else if z[r1] <x[p1] then begin
| | r1:=r1+1;
| end else begin
| | { так не бывает }
| end;
end;
{x[p1] = y[q1] = z[r1]}
writeln (x[p1]);
1.2.24. Та же задача, только заранее не известно, существует ли общий элемент в трех неубывающих массивах и требуется это выяснить (и найти один из общих элементов, если они есть).
1.2.25.
Элементами массива a[1..n] являются неубывающие
массивы [1..m] целых чисел:$$\begin{multiple}
\text{a: array [1..n] \text {of array} [1..m] \text {of integer};}\\
%
\w{a[1][1]}\le\ldots\le\w{a[1][m]},\ldots,
\w{a[n][1]}\le\ldots\le\w{a[n][m]}.
\end{multiple)$$
Известно, что существует число, входящее во все массивы a[i] (существует такое x, что для всякого i из 1..n найдется j из 1..m, для которого $$a[i][j]=x$$ ). Найти одно из таких чисел х.
Решение. Введем
for k:=1 to n do begin
| b[k]:=1;
end;
eq := true;
for k := 2 to n do begin
| eq := eq and (a[1][b[1]] = a[k][b[k]]);
end;
{инвариант: оставшиеся части пересекаются, т.е. существует
такое х, что для всякого i из [1..n] найдется j из [1..m],
не меньшее b[i], для которого a[i][j] = х; eq <=> первые
элементы оставшихся частей равны}
while not eq do begin
| s := 1; k := 1;
| {a[s][b[s]] - минимальное среди a[1][b[1]]..a[k][b[k]]}
| while k <> n do begin
| | k := k + 1;
| | if a[k][b[k]] < a[s][b[s]] then begin
| | | s := k;
| | end;
| end;
| {a[s][b[s]] - минимальное среди a[1][b[1]]..a[n][b[n]]}
| b [s] := b [s] + 1;
| for k := 2 to n do begin
| | eq := eq and (a[1][b[1]] = a[k][b[k]]);
| end;
end;
writeln (a[1][b[1]]);
1.2.26.
Приведенное решение предыдущей задачи требует порядка $$mn^2$$ действий. Придумать способ с числом действий
порядка mn.
Указание.
Придется пожертвовать
1.2.27.
(Двоичный поиск) Дана последовательность $$x[1]\le\ldots\le x[n]$$ целых чисел
и число a.
Выяснить, содержится ли a в этой последовательности, то
есть существует ли i из 1..n, для которого $$x[i]=a$$. (Количество действий порядка $$log n$$.)
Решение. (Предполагаем, что $$n>0$$.)
l := 1; r := n+1;
{r > l, если a есть вообще, то есть и среди x[l]..x[r-1]}
while r - l <> 1 do begin
| m := l + (r-l) div 2 ;
| {l < m < r }
| if x[m] <= a then begin
| | l := m;
| end else begin {x[m] > a}
| | r := m;
| end;
end;
(Обратите внимание, что и в случае $$x[m] = a$$
Каждый раз $$r-l$$ уменьшается примерно вдвое, откуда и вытекает требуемая оценка числа действий.
Замечание.$$l + (r-l)\,div\,2 = (2l + (r-l))\,div\, 2 = (r+l)\,div\,2.$$
В этой задаче существенно, что
1.2.28.
(Из книги Д. Гриса) Имеется x: , упорядоченный по
строкам и по столбцам:$$x[i][j] \le x[i][j+1],\\
x[i][j] \le x[i+1][j],$$
и число a. Требуется выяснить, встречается ли a
среди x[i][j].
Решение. Представляя себе x как a,
и будем его сужать. x[i][j] при $$1\le i\le l$$
и $$k\le j\le m$$
(допускаются пустые прямоугольники при $$l = 0$$
и $$k=m+1$$ ).
l:=n; k:=1;
{l>=0, k<=m+1, если a есть, то в описанном прямоугольнике}
while (l > 0) and (k < m+1) and (x[l][k] <> a) do begin
| if x[l][k] < a then begin
| | k := k + 1; {левый столбец не содержит a, удаляем его}
| end else begin {x[l][k] > a}
| | l := l - 1; {нижняя строка не содержит a, удаляем ее}
| end;
end;
{x[l][k] = a или прямоугольник пуст }
answer:= (l > 0) and (k < m+1) ;
Замечание.
Здесь та же ошибка: x[l][k] может оказаться
неопределенным.
(Ее исправление предоставляется читателю.)
1.2.29.
(Московская олимпиада по программированию) Дан неубывающий
n.
Решение. Пусть известно, что числа, представимые в виде
суммы элементов $$a[1],\ldots,a[k]$$, заполняют
1 до некоторого N. Если $$a[k+1] >
N+1$$, то $$N+1$$ и будет минимальным числом, не
представимым в виде 1 до N+a[k+1].
k := 0; N := 0;
{инвариант: числа, представимые в виде суммы элементов
массива a[1]..a[k], заполняют отрезок 1..N}
while (k <> n) and (a[k+1] <= N+1) do begin
| N := N + a[k+1];
| k := k + 1;
end;
{(k = n) или (a[k+1] > N+1); в обоих случаях ответ N+1}
writeln (N+1);
(Снова тот же дефект: в условии цикла при ложном первом условии второе не определено.)
1.2.30.
(Для знакомых с основами
(а) Определить
(б) Не используя других массивов, заменить
Указание.
(а)
1.2.31.
Дан a[1..n] и число b. Переставить числа
в массиве таким образом, чтобы слева от некоторой границы
стояли числа, меньшие или равные b, а справа от
границы - большие или равные b. Число действий
порядка n.
Решение.
l:=0; r:=n;
{инвариант: a[1]..a[l]<=b; a[r+1]..a[n]>=b}
while l <> r do begin
| if a[l+1] <= b then begin
| | l:=l+1;
| end else if a[r] >=b then begin
| | r:=r-1;
| end else begin {a[l+1]>b; a[r]<b}
| | ..поменять a[l+1] и a[r]
| | l:=l+1; r:=r-1;
| end;
end;
1.2.32.
Та же задача, но требуется, чтобы сначала шли элементы,
меньшие b, затем равные b, а лишь затем
большие b.
Решение. Теперь потребуются три границы: до первой будут
идти элементы, меньшие b, от первой до второй -
равные b, затем неизвестно какие до третьей, а после
третьей - большие b. (Более
l:=0; m:=0; r:=n;
{инвариант: a[1..l]<b; a[l+1..m]=b; a[r+1]..a[n]>b}
while m <> r do begin
| if a[m+1]=b then begin
| | m:=m+1;
| end else if a[m+1] > b then begin
| | ..обменять a[m+1] и a[r]
| | r:=r-1;
| end else begin {a[m+1] < b}
| | ..обменять a[m+1] и a[l+1]
| | l:=l+1; m:=m+1;
| end;
end;
1.2.33.
(Вариант предыдущей задачи, названный в книге Дейкстры
задачей о голландском флаге.) В массиве
длины n стоят числа 0, 1 и 2. Переставить
их в порядке возрастания, если единственной разрешенной
операцией (помимо чтения) над массивом является
n.
1.2.34.
Дан a[1..n] и число $$m\le n$$. Для
каждого участка из m стоящих рядом членов (таких
участков, очевидно, $$n-m+1$$ ) вычислить его
сумму. Общее число действий должно быть порядка n.
Решение. Переходя от участка к соседнему, мы добавляем один член, а другой вычитаем.
1.2.35.
Дана квадратная таблица a[1..n][1..n] и число $$m\le n$$. Для каждого
Решение. Сначала для каждого горизонтального
1 нужно добавить одно число и одно
вычесть.) Затем, используя эти суммы, вычисляем суммы
в
1.2.36.
В массиве $$a[1]\ldots a[n]$$ встречаются по одному
разу все 0 до n, кроме одного. Найти
пропущенное число за время порядка n и с конечной
дополнительной памятью.
Указание. Сложить все числа в массиве.
M - некоторое множество. Функция f, аргументами
которой являются последовательности элементов
множества M, а значениями - элементы некоторого
множества N, называется
Например, функция (сумма всех членов
последовательности)
Напротив, среднее арифметическое не является
Схема алгоритма вычисления
k := 0; f := f0;
{инвариант: f - значение функции на < x[1],...,x[k] > }
while k<>n do begin
| k := k + 1;
| f := F (f, x[k]);
end;
Здесь f0 - 0 ). Если
функция f определена только на непустых
последовательностях, то первая строка заменяется на
k:=1; f:=f(< x[1] > );
f не является индуктивной, полезно искать
ее f (это значит, что
существует такая функция t, что$$f (\langle x[1]\ldots x[n]\rangle)
= t (g(\langle x[1]\ldots x[n]\rangle))$$
при всех $$\langle x[1] \ldots x[n]\rangle$$ )F (минимальность означает, что
для любого g значения F
определяются значениями g ).
1.3.1. Указать индуктивные расширения для следующих функций:
(а) среднее арифметическое последовательности вещественных чисел;
(б) число элементов последовательности целых чисел, равных ее максимальному элементу;
(в) второй по величине элемент последовательности целых чисел (тот, который будет вторым, если переставить члены в неубывающем порядке);
(г) максимальное число идущих подряд одинаковых элементов;
(д) максимальная длина монотонного (неубывающего или невозрастающего) участка из идущих подряд элементов в последовательности целых чисел;
(е) число групп из единиц, разделенных нулями (в последовательности нулей и единиц).
Решение.
(а) $$\langle$$ сумма всех членов последовательности; длина $$\rangle$$ ;
(б) $$\langle$$ число элементов, равных максимальному; значение максимального $$\rangle$$ ;
(в) $$\langle$$ наибольший элемент последовательности; второй по величине элемент $$\rangle$$ ;
(г) $$\langle$$ максимальное число идущих подряд одинаковых элементов; число идущих подряд одинаковых элементов в конце последовательности; последний элемент последовательности $$\rangle$$ ;
(д) $$\langle$$ максимальная длина монотонного участка; максимальная длина неубывающего участка в конце последовательности; максимальная длина невозрастающего участка в конце последовательности; последний член последовательности $$\rangle$$ ;
(е) $$\langle$$ число групп из единиц, последний член $$\rangle$$.
1.3.2. (Сообщил Д. В. Варсанофьев) Даны две последовательности целых чисел $$x[1]\ldots x[n]$$ и $$y[1]\ldots y[k]$$. Выяснить, является ли вторая последовательность подпоследовательностью первой, то есть можно ли из первой вычеркнуть некоторые члены так, чтобы осталась вторая. Число действий порядка $$n+k$$.
Решение. Вариант 1. Будем сводить задачу к задаче меньшего размера.
n1:=n;
k1:=k;
{инвариант: искомый ответ <=> возможность из x[1]..x[n1]
получить y[1]..y[k1] }
while (n1 > 0) and (k1 > 0) do begin
| if x[n1] = y[k1] then begin
| | n1 := n1 - 1;
| | k1 := k1 - 1;
| end else begin
| | n1 := n1 - 1;
| end;
end;
{n1 = 0 или k1 = 0; если k1 = 0, то ответ - да, если k1<>0
(и n1 = 0), то ответ - нет}
answer := (k1 = 0);
Мы использовали то, что если $$x[n1]=y[k1]$$ и $$y[1]\ldots y[k1]$$ - подпоследовательность $$x[1]\ldots x[n1]$$, то $$y[1]\ldots y[k1-1]$$ - подпоследовательность $$x[1]\ldots x[n1-1]$$.
Вариант 2. Функция $$\langle x[1]\ldots x[n1]\rangle$$ $$\mapsto$$
[максимальное k1, для которого $$y[1]\ldots y[k1]$$ есть подпоследовательность $$x[1]\ldots x[n1]$$ ]
1.3.3. Даны две последовательности $$x[1]\ldots x[n]$$ и $$y[1]\ldots y[k]$$ целых чисел. Найти максимальную длину последовательности, являющейся подпоследовательностью обеих последовательностей. Количество операций порядка $$n\cdot k$$.
Решение (сообщено М. Н. Вайнцвайгом, А. М. Диментманом).
Обозначим через $$f(p,q)$$ максимальную длину
общей подпоследовательности последовательностей $$x[1]\ldots x[p]$$ и $$y[1]\ldots y[q]$$.
Тогда$$\begin{eqnarray*}
x[p]\ne y[q] \! \Rightarrow\!
f(p,q) = \max\,(f(p,q\!-\!1),
f(p\!-\!1,q)); \\
x[p]=y[q] \! \Rightarrow\! f(p,q) =
\max\,(f(p,q\!-\!1),
f(p\!-\!1,q),
f(p\!-\!1,q\!-\!1)\!+\!1);
\end{eqnarray*}$$
(Поскольку $$f(p-1,q-1)+1 \ge f(p,q-1), f(p-1,q)$$, во
втором случае f, имеющую размер $$n\cdot k$$. Можно
обойтись и памятью порядка k (или n ), если
индуктивно (по p ) вычислять $$\langle f(p,0),\ldots,f(p,k)\rangle$$ (как функция
от p этот набор
1.3.4. (из книги Д. Гриса) Дана последовательность целых чисел $$x[1],\ldots,x[n]$$. Найти максимальную длину ее возрастающей подпоследовательности (число действий порядка $$n\,log n$$ ).
Решение. Искомая функция не k ) также и числа $$u[1],\ldots,u[k]$$, где $$u[i]$$ - минимальный из последних членов возрастающих
подпоследовательностей длины i. Очевидно, $$u[1]\le\ldots\le u[k]$$. При добавлении нового члена
в x значения u и k корректируются.
n1 := 1; k := 1; u[1] := x[1];
{инвариант: k и u соответствуют данному выше описанию}
while n1 <> n do begin
| n1 := n1 + 1;
| ...
| {i - наибольшее из тех чисел отрезка 1..k, для
| которых u[i] < x[n1]; если таких нет, то i=0 }
| if i = k then begin
| | k := k + 1;
| | u[k+1] := x[n1];
| end else begin {i < k, u[i] < x[n1] <= u[i+1] }
| | u[i+1] := x[n1];
| end;
end;
Фрагмент ... использует идею двоичного поиска;
в u[0] равным минус
бесконечности, а u[k+1] - плюс бесконечности. Наша
цель: $$u[i] < x[n1]\le u[i+1]$$.
i:=0; j:=k+1;
{u[i] < x[n1] <= u[j], j > i}
while (j - i) <> 1 do begin
| s := i + (j-i) div 2; {i < s < j}
| if x[n1] <= u[s] then begin
| | j := s;
| end else begin {u[s] < x[n1]}
| | i := s;
| end;
end;
{u[i] < x[n1] <= u[j], j-i = 1}
Замечание.
Более простое (но не минимальное) i хранить
максимальную длину возрастающей подпоследовательности,
оканчивающейся на x[i]. Это расширение приводит
к алгоритму с числом действий порядка $$n^2$$. Есть
и другой изящный алгоритм с квадратичным временем работы
(сообщил М. В. Вьюгин):
найти максимальную общую подпоследовательность исходной
последовательности и отсортированной
последовательности с помощью предыдущей задачи.
1.3.5. Какие изменения нужно внести в решение предыдущей задачи, если надо искать максимальную неубывающую последовательность?
1.1.1.
Даны две целые переменные a, b. Составить фрагмент
программы, после исполнения которого значения переменных
поменялись бы местами (новое значение a равно
старому значению b и наоборот).
Решение. Введем дополнительную целую переменную t.
t := a; a := b; b := t;
Попытка обойтись без дополнительной переменной, написав
a := b; b := a;
не приводит к цели (безвозвратно утрачивается начальное
a ).
1.1.2.
Решить предыдущую задачу, не используя дополнительных
переменных (и предполагая, что значениями целых переменных
могут быть произвольные
Решение.Начальные значения a и b обозначим a0, b0.
a := a + b; {a = a0 + b0, b = b0}
b := a - b; {a = a0 + b0, b = a0}
a := a - b; {a = b0, b = a0}
1.1.3.
Дано а и натуральное (целое
неотрицательное) число n. Вычислить $$a^n$$.
Другими словами, необходимо составить программу, при
исполнении которой значения переменных а и n не
меняются, а значение некоторой другой переменной
(например, b ) становится равным $$a^n$$.
(При этом разрешается использовать и другие переменные.)
Решение. Введем целую переменную k, которая меняется
от 0 до n, причем поддерживается такое свойство: $$b = a^k$$ ).
k := 0; b := 1;
{b = a в степени k}
while k <> n do begin
| k := k + 1;
| b := b * a;
end;
Другое решение той же задачи:
k := n; b := 1;
{a в степени n = b * (a в степени k)}
while k <> 0 do begin
| k := k - 1;
| b := b * a;
end;
1.1.4.Решить предыдущую задачу, если требуется, чтобы число
действий (выполняемых
Решение. Внесем некоторые изменения во второе из предложенных решений предыдущей задачи:
k := n; b := 1; c:=a;
{a в степени n = b * (c в степени k)}
while k <> 0 do begin
| if k mod 2 = 0 then begin
| | k:= k div 2;
| | c:= c*c;
| end else begin
| | k := k - 1;
| | b := b * c;
| end;
end;
Каждый второй раз (не реже) будет выполняться первый
вариант k нечетно, то после
k уменьшается по крайней мере вдвое.
1.1.5.Даны а, b. Вычислить +, -, =, <>.
Решение.
k := 0; c := 0;
{инвариант: c = a * k}
while k <> b do begin
| k := k + 1;
| c := c + a;
end;
{c = a * k и k = b, следовательно, c = a * b}
1.1.6.
Даны натуральные числа а и b. Вычислить их сумму $$а+b$$. Использовать
Решение.
...
{инвариант: c = a + k}
...
1.1.7.
Дано натуральное (целое неотрицательное) число а
и целое положительное число d. Вычислить частное q и r при а на d, не используя
операций div и .
Решение. Согласно определению, $$a=q\cdot d+r$$, $$0 \le r <d$$.
{a >= 0; d > 0}
r := a; q := 0;
{инвариант: a = q * d + r, 0 <= r}
while not (r < d) do begin
| {r >= d}
| r := r - d; {r >= 0}
| q := q + 1;
end;
1.1.8.Дано натуральное $$n$$, вычислить $$n$$! ( $$0!=1$$, $$n! =n\cdot (n-1)$$!).
1.1.9.Последовательность Фибоначчи определяется так: $$a_0=0$$, $$a_1=1$$, $$a_k= a_{k-1} +a_{k-2}$$ при $$k\ge 2$$. Дано $$n$$, вычислить $$a_n$$.
1.1.10. Та же задача, если требуется, чтобы число операций было пропорционально $$log n$$. (Переменные должны быть целочисленными.)
Указание.
Пара соседних чисел Фибоначчи получается из предыдущей
- так что задача сводится к возведению
1.1.11. Дано натуральное n, вычислить
$$\frac{1}{0!} + \frac{1}{1!}+ \ldots+\frac{1}{n!}.$$1.1.12.
То же, если требуется, чтобы количество операций
(выполненных команд
Решение.
1.1.13.Даны два a и b, не равные нулю
одновременно. Вычислить НОД(a,b) - наибольший общий а и b.
Решение. Вариант 1.
if a > b then begin
| k := a;
end else begin
| k := b;
end;
{k = max (a,b)}
{инвариант: никакое число, большее k, не является
общим делителем}
while not ((a mod k = 0) and (b mod k = 0)) do begin
| k := k - 1;
end;
{k - общий делитель, большие - нет}
Вариант 2 (НОД(0,0)=0. Тогда НОД(a,b) =НОД(a-b,b) = НОД(a,b-a) ; НОД(a,0) =НОД(0,a) = a для всех $$a,b\ge 0$$.
m := a; n := b;
{инвариант: НОД (a,b) = НОД (m,n); m,n >= 0 }
while not ((m=0) or (n=0)) do begin
| if m >= n then begin
| | m := m - n;
| end else begin
| | n := n - m;
| end;
end;
{m = 0 или n = 0}
if m = 0 then begin
| k := n;
end else begin {n = 0}
| k := m;
end;
1.1.14.Написать модифицированный вариант алгоритма Евклида,
использующий соотношения НОД(a,b) = НОД(a
при $$a\ge b$$, НОД(a,b) = НОД(a, b при $$b\ge a$$.
1.1.15.
Даны натуральные a и b, не равные 0
одновременно. Найти d = НОД(a,b) и такие
целые x и y, что $$d = a\cdot x + b\cdot y$$.
Решение. Добавим в p, q, r, s и впишем в m = p*a+q*b ; n = r*a+s*b.
m:=a; n:=b; p := 1; q := 0; r := 0; s := 1;
{инвариант: НОД (a,b) = НОД (m,n); m,n >= 0
m = p*a + q*b; n = r*a + s*b.}
while not ((m=0) or (n=0)) do begin
| if m >= n then begin
| | m := m - n; p := p - r; q := q - s;
| end else begin
| | n := n - m; r := r - p; s := s - q;
| end;
end;
if m = 0 then begin
| k :=n; x := r; y := s;
end else begin
| k := m; x := p; y := q;
end;
1.1.16.Решить предыдущую задачу, используя в
1.1.17.
(Э. Дейкстра) Добавим в u, v, z:
m := a; n := b; u := b; v := a;
{инвариант: НОД (a,b) = НОД (m,n); m,n >= 0 }
while not ((m=0) or (n=0)) do begin
| if m >= n then begin
| | m := m - n; v := v + u;
| end else begin
| | n := n - m; u := u + v;
| end;
end;
if m = 0 then begin
| z:= v;
end else begin {n=0}
| z:= u;
end;
Доказать, что после исполнения алгоритма значение z
равно удвоенному наименьшему общему кратному
чисел a, b: $$z = 2\cdot НОК(a,b)$$.
Решение. Заметим, что величина $$m\cdot u + n\cdot v$$ не меняется в ходе выполнения алгоритма. Остается воспользоваться тем, что вначале она равна $$2ab$$ и что $$НОД (a,b) \cdot НОК (a, b) = ab$$.
1.1.18. Написать вариант алгоритма Евклида, использующий соотношения
$$\begin{multiple} НОД}(2}a}, 2}b})=2}\cdotНОД}(a},b}), \\ НОД}(2}a},b})= НОД}(a},b}) \quad \text {при нечетном b}}, \end{multiple}}$$не включающий 2 и проверку k.)
Решение.
m:= a; n:=b; d:=1;
{НОД(a,b) = d * НОД(m,n)}
while not ((m=0) or (n=0)) do begin
| if (m mod 2 = 0) and (n mod 2 = 0) then begin
| | d:= d*2; m:= m div 2; n:= n div 2;
| end else if (m mod 2 = 0) and (n mod 2 = 1) then begin
| | m:= m div 2;
| end else if(m mod 2 = 1) and (n mod 2 = 0) then begin
| | n:= n div 2;
| end else if (m mod 2=1) and (n mod 2=1) and (m>=n) then begin
| | m:= m-n;
| end else if (m mod 2=1) and (n mod 2=1) and (m<=n) then begin
| | n:= n-m;
| end;
end;
{m=0 => ответ=d*n; n=0 => ответ=d*m}
Оценка числа действий: каждое второе действие делит хотя бы
одно из чисел m и n пополам.
1.1.19.
Дополнить алгоритм предыдущей задачи поиском x и y, для которых $$ax+by=НОД(a,b)$$.
Решение. (Идея сообщена Д. Звонкиным.)
Прежде всего заметим, что одновременное a
и b пополам не меняет искомых x и y. Поэтому можно считать, что с самого начала одно из чисел a
и b нечетно. (Это свойство будет сохраняться и далее.)
Теперь попытаемся, как и раньше, хранить такие числа $$p,q,r,s$$, что$$\begin{align*}
m =ap + bq,\\
n =ar + bs.
\end{align*}$$
Проблема в том, что при m на 2 надо разделить p и q на 2, и они перестанут быть целыми (а станут двоично-рациональными). Двоично-d в виде
комбинации a и b с двоично-рациональными
коэффициентами. Иными словами, мы имеем$$2^{i}d = ax + by$$
для некоторых целых $$x,y$$ и натурального $$i$$.
Что делать, если $$i > 1$$? Если x и y четны, то на 2 можно сократить. Если это не так, положение
можно исправить преобразованием$$\begin{align*}
x\mathbin:=x + b, \\
y\mathbin:=y - a
\end{align*}$$
(оно не меняет $$ax+by$$ ). Убедимся в этом.
Напомним, что мы считаем, что одно из чисел a и b
нечетно. Пусть это будет a. Если при этом y четно,
то и x должно быть четным (иначе $$ax+by$$ будет нечетным). А при
нечетном y a
делает y четным.
1.1.20.Составить программу, печатающую квадраты всех натуральных чисел от 0 до заданного натурального n.
Решение.
k:=0;
writeln (k*k);
{инвариант: k<=n, напечатаны все
квадраты до k включительно}
while not (k=n) do begin
| k:=k+1;
| writeln (k*k);
end;
1.1.21.
Та же задача, но разрешается использовать из арифметических
операций лишь n.
Решение. Введем переменную k_square (k соотношением $${k{\_}square}= k^2$$:
k := 0; k_square := 0;
writeln (k_square);
while not (k = n) do begin
| k := k + 1;
| {k_square = (k-1) * (k-1) = k*k - 2*k + 1}
| k_square := k_square + k + k - 1;
| writeln (k_square);
end;
Замечание. Можно обойтись без
while not (k = n) do begin
| k_square := k_square + k;
| {k_square = k*k + k}
| k := k + 1;
| {k_square = (k-1)*(k-1)+(k-1)=k*k-k}
| k_square := k_square + k;
end;
1.1.22.
Составить программу, печатающую разложение на простые
множители заданного n ;
если $$n = 1$$, печатать ничего не надо).
Решение. Вариант 1.
k := n;
{инвариант: произведение напечатанных чисел и k равно
n, напечатаны только простые числа}
while not (k = 1) do begin
| l := 2;
| {инвариант: k не имеет делителей в интервале (1,l)}
| while k mod l <> 0 do begin
| | l := l + 1;
| end;
| {l - наименьший делитель k, больший 1, следовательно,
| простой}
| writeln (l);
| k:=k div l;
end;
Вариант 2.
k := n; l := 2;
{произведение k и напечатанных чисел равно n; напечатанные
числа просты; k не имеет делителей, меньших l}
while not (k = 1) do begin
| if k mod l = 0 then begin
| | {k делится на l и не имеет делителей,
| | меньших l, значит, l просто}
| | k := k div l;
| | writeln (l);
| end else begin
| | { k не делится на l }
| | l := l+1;
| end;
end;
1.1.23.
Составить программу решения предыдущей задачи, использующую
тот факт, что составное число имеет
Решение. Во втором варианте решения вместо l:=l+1 можно написать
if l*l > k then begin | l:=k; end else begin | l:=l+1; end;
1.1.24. Проверить, является ли заданное натуральное число $$n > 1$$ простым.
1.1.25.
(Для знакомых с основами
(a) Проверить, является ли оно простым (в $$\mathbb{Z}[i]$$ ).
(б) Напечатать его разложение на простые (в $$\mathbb{Z}[i]$$ ) множители.
1.1.26.Разрешим применять команды лишь при $$i=0,1,\allowbreak2,\ldots,9$$. Составить
программу, печатающую десятичную запись заданного
Решение.
base:=1;
{base - степень 10, не превосходящая n}
while 10 * base <= n do begin
| base:= base * 10;
end;
{base - максимальная степень 10, не превосходящая n}
k:=n;
{инвариант: осталось напечатать k с тем же числом
знаков, что в base; base = 100..00}
while base <> 1 do begin
| write(k div base);
| k:= k mod base;
| base:= base div 10;
end;
{base=1; осталось напечатать однозначное число k}
write(k);
Типичная ошибка при решении этой задачи: неправильно
обрабатываются числа с нулями посередине. Приведенный
1.1.27.
То же самое, но надо напечатать десятичную запись
в обратном порядке. (Для $$n=173$$ надо напечатать 371.)
Решение.
k:= n;
{инвариант: осталось напечатать k в обратном порядке}
while k <> 0 do begin
| write (k mod 10);
| k:= k div 10;
end;
1.1.28.
Дано натуральное n. Подсчитать количество решений
Решение.
k := 0; s := 0;
{инвариант: s = количество решений неравенства
x*x + y*y < n c x < k}
while k*k < n do begin
| ...
| {t = число решений неравенства k*k + y*y < n
| с y>=0 (при данном k) }
| k := k + 1;
| s := s + t;
end;
{k*k >= n, поэтому s = количество всех решений
неравенства}
Здесь ... - пока еще не написанный кусок программы,
который будет таким:
l := 0; t := 0;
{инвариант: t = число решений
неравенства k*k + y*y < n c 0<=y<l }
while k*k + l*l < n do begin
| l := l + 1;
| t := t + 1;
end;
{k*k + l*l >= n, поэтому t = число
всех решений неравенства k*k + y*y < n}
1.1.29.
Та же задача, но количество операций должно быть порядка $$\sqrt{n}$$. (В предыдущем решении, как можно
подсчитать, порядка n операций.)
Решение. Нас интересуют точки решетки (с целыми
X ) состоит из
Идея решения состоит в том, чтобы "двигаться вдоль его
границы", спускаясь по верхнему его краю, как по
лестнице. Координаты движущейся точки обозначим <k,l>.
Введем еще одну переменную s и будем поддерживать
истинность такого условия:$$\begin{quote}
\rule{0pt}{0pt}<k,l> находится сразу над k-ым столбцом; \\
s - число точек в предыдущих столбцах.
\end{quote}$$
Формально:
<k,l> не принадлежит X ;X.Обозначим эти условия через (И).
k := 0; l := 0;
while <0,l> принадлежит X do begin
| l := l + 1;
end;
{k = 0, l - минимальное среди тех l >= 0,
для которых <k,l> не принадлежит X}
s := 0;
{инвариант: И}
while not (l = 0) do begin
| s := s + l;
| {s - число точек в столбцах до k-го включительно}
| k := k + 1;
| {точка <k,l> лежит вне X, но, возможно, ее надо сдвинуть
| вниз, чтобы восстановить И}
| while (l <> 0) and (<k, l-1> не принадлежит X) do begin
| | l := l - 1;
| end;
end;
{И, l = 0, поэтому k-ый столбец и все следующие пусты, а
s равно искомому числу}
Оценка числа действий очевидна: сначала мы движемся вверх не более чем на $$\sqrt{n}$$ шагов, а затем вниз и вправо - в каждую сторону не более чем на $$\sqrt{n}$$ шагов.
1.1.30.
Даны n и k, $$n>1$$.
Напечатать k десятичных знаков числа $$1/n$$.
(При наличии двух десятичных разложений выбирается то из
них, которое не содержит девятки в периоде.) Программа
должна использовать только целые переменные.
Решение. Сдвинув в десятичной записи числа $$1/n$$
запятую на k мест вправо, получим число $$10^k/n}$$. Нам надо напечатать его целую часть,
то есть разделить $$10^k$$ на n нацело.
Стандартный способ требует использования больших по
величине чисел, которые могут выйти за границы r:
l := 0; r := 1;
{инв.: напечатано l разрядов 1/n, осталось напечатать
k - l разрядов дроби r/n}
while l <> k do begin
| write ( (10 * r) div n);
| r := (10 * r) mod n;
| l := l + 1;
end;
1.1.31.
Дано
Решение. Период дроби равен периоду в последовательности
остатков (докажите это; в частности, надо доказать, что он
не может быть меньше). Кроме того, в этой
последовательности все периодически повторяющиеся члены
различны, а предпериод имеет длину не более n. Поэтому
достаточно найти $$(n+1)$$ -ый член
последовательности остатков и затем минимальное k, при
котором $$(n+1+k)$$ -ый член совпадает
с $$(n+1)$$ -ым.
l := 0; r := 1;
{инвариант: r/n = результат отбрасывания l знаков в 1/n}
while l <> n+1 do begin
| r := (10 * r) mod n;
| l := l + 1;
end;
c := r;
{c = (n+1)-ый член последовательности остатков}
r := (10 * r) mod n;
k := 1;
{r = (n+k+1)-ый член последовательности остатков}
while r <> c do begin
| r := (10 * r) mod n;
| k := k + 1;
end;
1.1.32.(Сообщил Ю. В. Матиясевич)
Дана функция $$f:\{1\ldots N\} \to\{1\ldots N\}$$ Найти период последовательности $$1,f(1),f(f(1),\ldots $$ Количество действий
должно быть пропорционально суммарной длине предпериода
и периода (эта сумма может быть существенно меньше N ).
Решение. Если отбросить начальный кусок,
последовательность
{Обозначение: f[n,1]=f(f(...f(1)...)) (n раз)}
k:=1; a:=f(1); b:=f(f(1));
{a=f[k,1]; b=f[2k,1]}
while a <> b do begin
| k:=k+1; a:=f(a); b:=f(f(b));
end;
{a=f[k,1]=f[2k,1]; f[k,1] входит в периодическую часть}
l:=1; b:=f(a);
{b=f[k+l,1]; f[k,1],...,f[k+l-1,1] различны}
while a <> b do begin
| l:=l+1; b:=f(b);
end;
{период равен l}
1.1.33.
(Э. Дейкстра)
Функция f с натуральными аргументами
и значениями определена так: $$f(0) = 0$$, $$f(1)=1$$, $$f(2n) = f(n)$$, $$f(2n+1) = f(n) + f(n+1)$$.
Составить программу вычисления $$f(n)$$ по
заданному n, требующую порядка $$log n$$ операций.
Решение.
k := n; a := 1; b := 0;
{инвариант: 0 <= k, f (n) = a * f(k) + b * f (k+1)}
while k <> 0 do begin
| if k mod 2 = 0 then begin
| | l := k div 2;
| | {k=2l, f(k)=f(l), f(k+1) = f(2l+1) = f(l) + f(l+1),
| | f (n) = a*f(k) + b*f(k+1) = (a+b)*f(l) + b*f(l+1)}
| | a := a + b; k := l;
| end else begin
| | l := k div 2;
| | {k = 2l + 1, f(k) = f(l) + f(l+1),
| | f(k+1) = f(2l+2) = f(l+1),
| | f(n) = a*f(k) + b*f(k+1) = a*f(l) + (a+b)*f(l+1)}
| | b := a + b; k := l;
| end;
end;
{k = 0, f(n) = a * f(0) + b * f(1) = b, что и требовалось}
1.1.34. То же, если $$f(0) = 13$$, $$f(1) = 17$$, $$f(2) = 20$$, $$f(3) = 30$$, $$f(2n) = 43\,f(n) + 57\,f(n+1)$$, $$f(2n+1) = 91\,f(n) + 179\,f(n+1)$$ при $$n\ge 2$$.
Указание. Хранить коэффициенты в выражении $$f(n)$$ через три соседних числа.
1.1.35.
Даны а и b, причем $$b>0$$.
Найти частное и a на b,
оперируя лишь с div и , за исключением 2 четных
чисел; число шагов не должно превосходить $$C_1 log(a/b)+C_2$$ для некоторых
Решение.
b1 := b;
while b1 <= a do begin
| b1 := b1 * 2;
end;
{b1 > a, b1 = b * (некоторая степень 2)}
q:=0; r:=a;
{инвариант: q, r - частное и остаток при делении a на b1,
b1 = b * (некоторая степень 2)}
while b1 <> b do begin
| b1 := b1 div 2 ; q := q * 2;
| { a = b1 * q + r, 0 <= r, r < 2 * b1}
| if r >= b1 then begin
| | r := r - b1;
| | q := q + 1;
| end;
end;
{q, r - частное и остаток при делении a на b}
В следующих задачах переменные $$x,y,z$$
предполагаются описанными как (где n - некоторое 0 ), если иное не оговорено явно.
1.2.1.
Заполнить x нулями. (Это означает, что нужно
составить фрагмент программы, после выполнения которого все
значения x[1]..x[n ] равнялись бы нулю, независимо от
начального x.)
Решение.
i := 0;
{инвариант: первые i значений x[1]..x[i] равны 0}
while i <> n do begin
| i := i + 1;
| {x[1]..x[i-1] = 0}
| x[i] := 0;
end;
1.2.2.
Подсчитать количество нулей в массиве x. (Составить
фрагмент программы, не меняющий значения x, после
исполнения которого значение некоторой целой
переменной k равнялось бы числу нулей среди компонент
массива x.)
Решение.
...
{инвариант: k = число нулей среди x[1]...x[i] }
...
1.2.3.Не используя x:=y.
Решение.
i := 0;
{инвариант: значение y не изменилось, x[l]=y[l] при l<=i}
while i <> n do begin
| i := i + 1;
| x[i] := y[i];
end;
1.2.4.Найти x[1]..x[n].
Решение.
i := 1; max := x[1];
{инвариант: max = максимум из x[1]..x[i]}
while i <> n do begin
| i := i + 1;
| {max = максимум из x[1]..x[i-1]}
| if x[i] > max then begin
| | max := x[i];
| end;
end;
1.2.5.Дан x: , причем
известно, что $$x[1]\le x[2]\le\ldots\le x[n]]$$.
Найти количество различных чисел среди элементов этого
массива.
Решение. Вариант 1.
i := 1; k := 1;
{инвариант: k - количество различных среди x[1]..x[i]}
while i <> n do begin
| i := i + 1;
| if x[i] <> x[i-1] then begin
| | k := k + 1;
| end;
end;
Вариант 2. Искомое число на 1 больше количества тех
чисел i из 1..n-1, для которых x[i] не равно x[i+1].
k := 1; for i := 1 to n-1 do begin | if x[i]<> x[i+1] then begin | | k := k + 1; | end; end;
1.2.6.
Дан x: . Найти
количество различных чисел среди элементов этого массива.
(Число действий должно быть порядка $$n^2$$ )
1.2.7. Та же задача, если требуется, чтобы количество действий было порядка $$n log n$$.
Указание.
Смотри лекцию 4 (
1.2.8.Та же задача, если известно, что все элементы массива -
числа от 1 до k и число действий должно быть
порядка $$n+k$$.
1.2.9.
(Сообщил А. Л. Брудно)
Прямоугольное поле $$m\times n$$ разбито на $$mn$$
квадратных клеток. Некоторые клетки покрашены в черный
цвет. Известно, что все черные клетки могут быть разбиты на
несколько непересекающихся и не имеющих общих вершин черных
array [1..m] of array [1..n] of boolean;
подсчитать число черных
Решение. Число
1.2.10.
Дан x[1]..x[n] целых чисел. Не используя других
массивов, переставить элементы массива в обратном порядке.
Решение. Элементы x[i] и x[n+1-i] нужно поменять
местами для всех i, для которых $$i<n+1-i$$, то есть$$$\w{2}\w{i} < \w{n} +
\w{1}$~$\Leftrightarrow$
$\w{2}\w{i}\le \w{n}\hm\Leftrightarrow \w{i} \le \text{n div 2}$$$
for i := 1 to n div 2 do begin | ...поменять местами x[i] и x[n+1-i]; end;
1.2.11.
(Из книги Д. Гриса) Дан x[1]..x[m+n], рассматриваемый как соединение двух его
отрезков: начала x[1]..x[m] длины m и конца x[m+1]..x[m+n] длины n. Не используя дополнительных
массивов, переставить начало и конец.
(Число действий порядка $$m+n$$
Решение.
Вариант 1. Перевернем (расположим в обратном
порядке) отдельно начало и конец массива, а затем перевернем
весь
Вариант 2. (А. Г. Кушниренко)
Рассматривая
Вариант 3. Рассмотрим более общую задачу - обмен двух
участков массива x[p+1]..x[q] и x[q+1]..x[r].
Предположим, что длина левого участка (назовем его $$A$$ ) не
больше длины правого (назовем его $$B$$ ). Выделим
в $$B$$
начало той же длины, что и $$A$$, назовем его $$B_1$$,
а
p := 0; q := m; r := m + n;
{инвариант: осталось переставить x[p+1..q], x[q+1..r]}
while (p <> q) and (q <> r) do begin
| {оба участка непусты}
| if (q - p) <= (r - q) then begin
| | ..переставить x[p+1]..x[q] и x[q+1]..x[q+(q-p)]
| | pnew := q; qnew := q + (q - p);
| | p := pnew; q := qnew;
| end else begin
| | ..переставить x[q-(r-q)+1]..x[q] и x[q+1]..x[r]
| | qnew := q - (r - q); rnew := q;
| | q := qnew; r := rnew;
| end;
end;
Оценка времени работы: на очередном шаге оставшийся для обработки участок становится короче на длину $$A$$ ; число действий при этом также пропорционально длине $$A$$.
1.2.12.Коэффициенты многочлена лежат в массиве a: ( n - x, то есть $$a[n]\,x^n+\ldots+a[1]\,x+a[0]$$.
Решение.
(Описываемый алгоритм называется
k := 0; y := a[n];
{инвариант: 0 <= k <= n,
y= a[n]*(x в степени k)+...+a[n-1]*(x в степени k-1)+...+
+ a[n-k]*(x в степени 0)}
while k<>n do begin
| k := k + 1;
| y := y * x + a [n-k];
end;
1.2.13.
(Для знакомых с основами анализа; сообщил
А. Г. Кушниренко) Дополнить алгоритм
Решение. Добавление нового коэффициента соответствует
переходу от многочлена $$P(x)$$ к
Общее утверждение о сложности вычисления производных таково:
1.2.14.
(В. Баур, Ф. Штрассен)
Дана программа
Указание.
Можно считать, что каждая команда -
1.2.15.В массивах a: и b:
хранятся коэффициенты двух
k и l. Поместить в c: коэффициенты их
произведения. (Числа $$k,l,m$$ - натуральные, $$m=k+l$$ ; элемент массива с индексом i
содержит коэффициент при степени i.)
Решение.
for i:=0 to m do begin | c[i]:=0; end; for i:=0 to k do begin | for j:=0 to l do begin | | c[i+j] := c[i+j] + a[i]*b[j]; | end; end;
1.2.16.
Предложенный выше алгоритм перемножения
Указание.
Представим себе, что надо перемножить два многочлена
степени $$2k$$. Их можно представить в виде$$A(x)\,x^k + B(x) \quad и \quad C(x)\,x^k + D(x).$$
1.2.17.Даны два возрастающих массива x: и y: . Найти
количество общих элементов в этих массивах, то есть
количество тех целых t, для которых $$t = x[i] =
y[j]$$ для некоторых i и j. (Число действий
порядка $$k+l$$.)
Решение.
k1:=0; l1:=0; n:=0;
{инвариант: 0<=k1<=k; 0<=l1<=l;
искомый ответ = n + количество общих
элементов в x[k1+1]...x[k] и y[l1+1]...y[l]}
while (k1 <> k) and (l1 <> l) do begin
| if x[k1+1] < y[l1+1] then begin
| | k1 := k1 + 1;
| end else if x[k1+1] > y[l1+1] then begin
| | l1 := l1 + 1;
| end else begin {x[k1+1] = y[l1+1]}
| | k1 := k1 + 1;
| | l1 := l1 + 1;
| | n := n + 1;
| end;
end;
{k1 = k или l1 = l, поэтому одно из множеств, упомянутых
в инварианте, пусто, а n равно искомому ответу}
Замечание. В третьей k1, l1 ; вторая добавлена для
1.2.18.Решить предыдущую задачу, если про массивы известно лишь, что $$x[1]\le\ldots\le x[k]$$ и $$y[1]\le\ldots\le y[l]$$ (возрастание заменено неубыванием).
Решение. Условие возрастания было использовано в третьей
k1 и l1 на 1, мы
тем самым уменьшали на 1 количество общих элементов
в $$x[k1+1]\ldots x[k]$$
и $$x[l1+1]\ldots x[l]$$. Теперь это придется делать
сложнее.
...
end else begin {x[k1+1] = y[l1+1]}
| t := x [k1+1];
| while (k1<k) and (x[k1+1]=t) do begin
| | k1 := k1 + 1;
| end;
| while (l1<l) and (x[l1+1]=t) do begin
| | l1 := l1 + 1;
| end;
| n := n + 1;
end;
Замечание. Эта программа имеет дефект: при проверке условия$$$$
\w{(k1<k) and (x[k1+1]=t)}
$$$$
(или второго, аналогичного) при ложной первой скобке вторая
окажется бессмысленной (индекс выйдет за границы массива)
и возникнет ошибка.
Некоторые версии A and B, сначала
вычисляют A и при ложном A не вычисляют B. (Так
ведет себя, например, система Turbo
Но если мы не хотим полагаться на такое свойство
используемой нами реализации b:
и напишем:
if k1 < k then b := (x[k1+1]=t) else b:=false;
{b = (k1<k) and (x[k1+1] = t)}
while b do begin
| k1:=k1+1;
| if k1 < k then b := (x[k1+1]=t) else b:=false;
end;
Можно также сделать иначе:
end else begin {x[k1+1] = y[l1+1]}
| if k1 + 1 = k then begin
| | k1 := k1 + 1;
| | n := n + 1;
| end else if x[k1+1] = x [k1+2] then begin
| | k1 := k1 + 1;
| end else begin
| | k1 := k1 + 1;
| | n := n + 1;
| end;
end;
Так будет короче, хотя менее
Наконец, можно увеличить размер массива в его описании, включив в него фиктивные элементы.
1.2.19.
Даны два неубывающих массива x: и y: . Найти
число различных элементов среди $$x[1],\ldots,x[k],y[1],\ldots,y[l]$$. (Число
действий порядка $$k+l$$.)
1.2.20.
Даны два массива $$x[1]\le\ldots\le x[k]$$
и $$y[1]\le\ldots\le y[l]$$. "Соединить" их
в z столько раз, сколько раз он входит в общей
сложности в массивы x и y ). Число действий
порядка m.
Решение.
k1 := 0; l1 := 0;
{инвариант: ответ получится, если к z[1]..z[k1+l1] добавить
справа соединение массивов x[k1+1]..x[k] и y[l1+1]..y[l]}
while (k1 <> k) or (l1 <> l) do begin
| if k1 = k then begin
| | {l1 < l}
| | l1 := l1 + 1;
| | z[k1+l1] := y[l1];
| end else if l1 = l then begin
| | {k1 < k}
| | k1 := k1 + 1;
| | z[k1+l1] := x[k1];
| end else if x[k1+1] <= y[l1+1] then begin
| | k1 := k1 + 1;
| | z[k1+l1] := x[k1];
| end else if x[k1+1] >= y[l1+1] then begin
| | l1 := l1 + 1;
| | z[k1+l1] := y[l1];
| end else begin
| | { такого не бывает }
| end;
end;
{k1 = k, l1 = l, массивы соединены}
Этот процесс можно пояснить так. Пусть у нас есть две стопки карточек, отсортированных по алфавиту. Мы соединяем их в одну стопку, выбирая каждый раз ту из верхних карточек обеих стопок, которая идет раньше в алфавитном порядке. Если в одной стопке карточки кончились, берем их из другой стопки.
1.2.21.
Даны два массива $$x[1]\le\ldots\le x[k]$$
и $$y[1]\le\ldots\le y[l]$$. Найти их "
z
равняется x
и y. Число действий порядка $$k+l$$.
1.2.22.
Даны два массива $$x[1]\le\ldots\le x[k]$$
и $$y[1]\le\ldots\le y[l]$$ и число q. Найти сумму
вида $$x[i]+y[j]$$, наиболее близкую к числу q.
(Число действий порядка k+l, дополнительная память -
фиксированное число целых переменных, сами массивы
менять не разрешается.)
Указание.
Надо найти минимальное
1.2.23. (из книги Д. Гриса) Некоторое число содержится в каждом из трех целочисленных неубывающих массивов $$x[1]\le\ldots\le x[p]$$, $$y[1]\le\ldots\le y[q]$$, $$z[1]\le\ldots\le z[r]$$. Найти одно из таких чисел. Число действий должно быть порядка $$p+q+r$$.
Решение.
p1:=1; q1=1; r1:=1;
{инвариант: x[p1]..x[p], y[q1]..y[q], z[r1]..z[r]
содержат общий элемент}
while not ((x[p1]=y[q1]) and (y[q1]=z[r1])) do begin
| if x[p1]<y[q1] then begin
| | p1:=p1+1;
| end else if y[q1] <z[r1] then begin
| | q1:=q1+1;
| end else if z[r1] <x[p1] then begin
| | r1:=r1+1;
| end else begin
| | { так не бывает }
| end;
end;
{x[p1] = y[q1] = z[r1]}
writeln (x[p1]);
1.2.24. Та же задача, только заранее не известно, существует ли общий элемент в трех неубывающих массивах и требуется это выяснить (и найти один из общих элементов, если они есть).
1.2.25.
Элементами массива a[1..n] являются неубывающие
массивы [1..m] целых чисел:$$\begin{multiple}
\text{a: array [1..n] \text {of array} [1..m] \text {of integer};}\\
%
\w{a[1][1]}\le\ldots\le\w{a[1][m]},\ldots,
\w{a[n][1]}\le\ldots\le\w{a[n][m]}.
\end{multiple)$$
Известно, что существует число, входящее во все массивы a[i] (существует такое x, что для всякого i из 1..n найдется j из 1..m, для которого $$a[i][j]=x$$ ). Найти одно из таких чисел х.
Решение. Введем
for k:=1 to n do begin
| b[k]:=1;
end;
eq := true;
for k := 2 to n do begin
| eq := eq and (a[1][b[1]] = a[k][b[k]]);
end;
{инвариант: оставшиеся части пересекаются, т.е. существует
такое х, что для всякого i из [1..n] найдется j из [1..m],
не меньшее b[i], для которого a[i][j] = х; eq <=> первые
элементы оставшихся частей равны}
while not eq do begin
| s := 1; k := 1;
| {a[s][b[s]] - минимальное среди a[1][b[1]]..a[k][b[k]]}
| while k <> n do begin
| | k := k + 1;
| | if a[k][b[k]] < a[s][b[s]] then begin
| | | s := k;
| | end;
| end;
| {a[s][b[s]] - минимальное среди a[1][b[1]]..a[n][b[n]]}
| b [s] := b [s] + 1;
| for k := 2 to n do begin
| | eq := eq and (a[1][b[1]] = a[k][b[k]]);
| end;
end;
writeln (a[1][b[1]]);
1.2.26.
Приведенное решение предыдущей задачи требует порядка $$mn^2$$ действий. Придумать способ с числом действий
порядка mn.
Указание.
Придется пожертвовать
1.2.27.
(Двоичный поиск) Дана последовательность $$x[1]\le\ldots\le x[n]$$ целых чисел
и число a.
Выяснить, содержится ли a в этой последовательности, то
есть существует ли i из 1..n, для которого $$x[i]=a$$. (Количество действий порядка $$log n$$.)
Решение. (Предполагаем, что $$n>0$$.)
l := 1; r := n+1;
{r > l, если a есть вообще, то есть и среди x[l]..x[r-1]}
while r - l <> 1 do begin
| m := l + (r-l) div 2 ;
| {l < m < r }
| if x[m] <= a then begin
| | l := m;
| end else begin {x[m] > a}
| | r := m;
| end;
end;
(Обратите внимание, что и в случае $$x[m] = a$$
Каждый раз $$r-l$$ уменьшается примерно вдвое, откуда и вытекает требуемая оценка числа действий.
Замечание.$$l + (r-l)\,div\,2 = (2l + (r-l))\,div\, 2 = (r+l)\,div\,2.$$
В этой задаче существенно, что
1.2.28.
(Из книги Д. Гриса) Имеется x: , упорядоченный по
строкам и по столбцам:$$x[i][j] \le x[i][j+1],\\
x[i][j] \le x[i+1][j],$$
и число a. Требуется выяснить, встречается ли a
среди x[i][j].
Решение. Представляя себе x как a,
и будем его сужать. x[i][j] при $$1\le i\le l$$
и $$k\le j\le m$$
(допускаются пустые прямоугольники при $$l = 0$$
и $$k=m+1$$ ).
l:=n; k:=1;
{l>=0, k<=m+1, если a есть, то в описанном прямоугольнике}
while (l > 0) and (k < m+1) and (x[l][k] <> a) do begin
| if x[l][k] < a then begin
| | k := k + 1; {левый столбец не содержит a, удаляем его}
| end else begin {x[l][k] > a}
| | l := l - 1; {нижняя строка не содержит a, удаляем ее}
| end;
end;
{x[l][k] = a или прямоугольник пуст }
answer:= (l > 0) and (k < m+1) ;
Замечание.
Здесь та же ошибка: x[l][k] может оказаться
неопределенным.
(Ее исправление предоставляется читателю.)
1.2.29.
(Московская олимпиада по программированию) Дан неубывающий
n.
Решение. Пусть известно, что числа, представимые в виде
суммы элементов $$a[1],\ldots,a[k]$$, заполняют
1 до некоторого N. Если $$a[k+1] >
N+1$$, то $$N+1$$ и будет минимальным числом, не
представимым в виде 1 до N+a[k+1].
k := 0; N := 0;
{инвариант: числа, представимые в виде суммы элементов
массива a[1]..a[k], заполняют отрезок 1..N}
while (k <> n) and (a[k+1] <= N+1) do begin
| N := N + a[k+1];
| k := k + 1;
end;
{(k = n) или (a[k+1] > N+1); в обоих случаях ответ N+1}
writeln (N+1);
(Снова тот же дефект: в условии цикла при ложном первом условии второе не определено.)
1.2.30.
(Для знакомых с основами
(а) Определить
(б) Не используя других массивов, заменить
Указание.
(а)
1.2.31.
Дан a[1..n] и число b. Переставить числа
в массиве таким образом, чтобы слева от некоторой границы
стояли числа, меньшие или равные b, а справа от
границы - большие или равные b. Число действий
порядка n.
Решение.
l:=0; r:=n;
{инвариант: a[1]..a[l]<=b; a[r+1]..a[n]>=b}
while l <> r do begin
| if a[l+1] <= b then begin
| | l:=l+1;
| end else if a[r] >=b then begin
| | r:=r-1;
| end else begin {a[l+1]>b; a[r]<b}
| | ..поменять a[l+1] и a[r]
| | l:=l+1; r:=r-1;
| end;
end;
1.2.32.
Та же задача, но требуется, чтобы сначала шли элементы,
меньшие b, затем равные b, а лишь затем
большие b.
Решение. Теперь потребуются три границы: до первой будут
идти элементы, меньшие b, от первой до второй -
равные b, затем неизвестно какие до третьей, а после
третьей - большие b. (Более
l:=0; m:=0; r:=n;
{инвариант: a[1..l]<b; a[l+1..m]=b; a[r+1]..a[n]>b}
while m <> r do begin
| if a[m+1]=b then begin
| | m:=m+1;
| end else if a[m+1] > b then begin
| | ..обменять a[m+1] и a[r]
| | r:=r-1;
| end else begin {a[m+1] < b}
| | ..обменять a[m+1] и a[l+1]
| | l:=l+1; m:=m+1;
| end;
end;
1.2.33.
(Вариант предыдущей задачи, названный в книге Дейкстры
задачей о голландском флаге.) В массиве
длины n стоят числа 0, 1 и 2. Переставить
их в порядке возрастания, если единственной разрешенной
операцией (помимо чтения) над массивом является
n.
1.2.34.
Дан a[1..n] и число $$m\le n$$. Для
каждого участка из m стоящих рядом членов (таких
участков, очевидно, $$n-m+1$$ ) вычислить его
сумму. Общее число действий должно быть порядка n.
Решение. Переходя от участка к соседнему, мы добавляем один член, а другой вычитаем.
1.2.35.
Дана квадратная таблица a[1..n][1..n] и число $$m\le n$$. Для каждого
Решение. Сначала для каждого горизонтального
1 нужно добавить одно число и одно
вычесть.) Затем, используя эти суммы, вычисляем суммы
в
1.2.36.
В массиве $$a[1]\ldots a[n]$$ встречаются по одному
разу все 0 до n, кроме одного. Найти
пропущенное число за время порядка n и с конечной
дополнительной памятью.
Указание. Сложить все числа в массиве.
M - некоторое множество. Функция f, аргументами
которой являются последовательности элементов
множества M, а значениями - элементы некоторого
множества N, называется
Например, функция (сумма всех членов
последовательности)
Напротив, среднее арифметическое не является
Схема алгоритма вычисления
k := 0; f := f0;
{инвариант: f - значение функции на < x[1],...,x[k] > }
while k<>n do begin
| k := k + 1;
| f := F (f, x[k]);
end;
Здесь f0 - 0 ). Если
функция f определена только на непустых
последовательностях, то первая строка заменяется на
k:=1; f:=f(< x[1] > );
f не является индуктивной, полезно искать
ее f (это значит, что
существует такая функция t, что$$f (\langle x[1]\ldots x[n]\rangle)
= t (g(\langle x[1]\ldots x[n]\rangle))$$
при всех $$\langle x[1] \ldots x[n]\rangle$$ )F (минимальность означает, что
для любого g значения F
определяются значениями g ).
1.3.1. Указать индуктивные расширения для следующих функций:
(а) среднее арифметическое последовательности вещественных чисел;
(б) число элементов последовательности целых чисел, равных ее максимальному элементу;
(в) второй по величине элемент последовательности целых чисел (тот, который будет вторым, если переставить члены в неубывающем порядке);
(г) максимальное число идущих подряд одинаковых элементов;
(д) максимальная длина монотонного (неубывающего или невозрастающего) участка из идущих подряд элементов в последовательности целых чисел;
(е) число групп из единиц, разделенных нулями (в последовательности нулей и единиц).
Решение.
(а) $$\langle$$ сумма всех членов последовательности; длина $$\rangle$$ ;
(б) $$\langle$$ число элементов, равных максимальному; значение максимального $$\rangle$$ ;
(в) $$\langle$$ наибольший элемент последовательности; второй по величине элемент $$\rangle$$ ;
(г) $$\langle$$ максимальное число идущих подряд одинаковых элементов; число идущих подряд одинаковых элементов в конце последовательности; последний элемент последовательности $$\rangle$$ ;
(д) $$\langle$$ максимальная длина монотонного участка; максимальная длина неубывающего участка в конце последовательности; максимальная длина невозрастающего участка в конце последовательности; последний член последовательности $$\rangle$$ ;
(е) $$\langle$$ число групп из единиц, последний член $$\rangle$$.
1.3.2. (Сообщил Д. В. Варсанофьев) Даны две последовательности целых чисел $$x[1]\ldots x[n]$$ и $$y[1]\ldots y[k]$$. Выяснить, является ли вторая последовательность подпоследовательностью первой, то есть можно ли из первой вычеркнуть некоторые члены так, чтобы осталась вторая. Число действий порядка $$n+k$$.
Решение. Вариант 1. Будем сводить задачу к задаче меньшего размера.
n1:=n;
k1:=k;
{инвариант: искомый ответ <=> возможность из x[1]..x[n1]
получить y[1]..y[k1] }
while (n1 > 0) and (k1 > 0) do begin
| if x[n1] = y[k1] then begin
| | n1 := n1 - 1;
| | k1 := k1 - 1;
| end else begin
| | n1 := n1 - 1;
| end;
end;
{n1 = 0 или k1 = 0; если k1 = 0, то ответ - да, если k1<>0
(и n1 = 0), то ответ - нет}
answer := (k1 = 0);
Мы использовали то, что если $$x[n1]=y[k1]$$ и $$y[1]\ldots y[k1]$$ - подпоследовательность $$x[1]\ldots x[n1]$$, то $$y[1]\ldots y[k1-1]$$ - подпоследовательность $$x[1]\ldots x[n1-1]$$.
Вариант 2. Функция $$\langle x[1]\ldots x[n1]\rangle$$ $$\mapsto$$
[максимальное k1, для которого $$y[1]\ldots y[k1]$$ есть подпоследовательность $$x[1]\ldots x[n1]$$ ]
1.3.3. Даны две последовательности $$x[1]\ldots x[n]$$ и $$y[1]\ldots y[k]$$ целых чисел. Найти максимальную длину последовательности, являющейся подпоследовательностью обеих последовательностей. Количество операций порядка $$n\cdot k$$.
Решение (сообщено М. Н. Вайнцвайгом, А. М. Диментманом).
Обозначим через $$f(p,q)$$ максимальную длину
общей подпоследовательности последовательностей $$x[1]\ldots x[p]$$ и $$y[1]\ldots y[q]$$.
Тогда$$\begin{eqnarray*}
x[p]\ne y[q] \! \Rightarrow\!
f(p,q) = \max\,(f(p,q\!-\!1),
f(p\!-\!1,q)); \\
x[p]=y[q] \! \Rightarrow\! f(p,q) =
\max\,(f(p,q\!-\!1),
f(p\!-\!1,q),
f(p\!-\!1,q\!-\!1)\!+\!1);
\end{eqnarray*}$$
(Поскольку $$f(p-1,q-1)+1 \ge f(p,q-1), f(p-1,q)$$, во
втором случае f, имеющую размер $$n\cdot k$$. Можно
обойтись и памятью порядка k (или n ), если
индуктивно (по p ) вычислять $$\langle f(p,0),\ldots,f(p,k)\rangle$$ (как функция
от p этот набор
1.3.4. (из книги Д. Гриса) Дана последовательность целых чисел $$x[1],\ldots,x[n]$$. Найти максимальную длину ее возрастающей подпоследовательности (число действий порядка $$n\,log n$$ ).
Решение. Искомая функция не k ) также и числа $$u[1],\ldots,u[k]$$, где $$u[i]$$ - минимальный из последних членов возрастающих
подпоследовательностей длины i. Очевидно, $$u[1]\le\ldots\le u[k]$$. При добавлении нового члена
в x значения u и k корректируются.
n1 := 1; k := 1; u[1] := x[1];
{инвариант: k и u соответствуют данному выше описанию}
while n1 <> n do begin
| n1 := n1 + 1;
| ...
| {i - наибольшее из тех чисел отрезка 1..k, для
| которых u[i] < x[n1]; если таких нет, то i=0 }
| if i = k then begin
| | k := k + 1;
| | u[k+1] := x[n1];
| end else begin {i < k, u[i] < x[n1] <= u[i+1] }
| | u[i+1] := x[n1];
| end;
end;
Фрагмент ... использует идею двоичного поиска;
в u[0] равным минус
бесконечности, а u[k+1] - плюс бесконечности. Наша
цель: $$u[i] < x[n1]\le u[i+1]$$.
i:=0; j:=k+1;
{u[i] < x[n1] <= u[j], j > i}
while (j - i) <> 1 do begin
| s := i + (j-i) div 2; {i < s < j}
| if x[n1] <= u[s] then begin
| | j := s;
| end else begin {u[s] < x[n1]}
| | i := s;
| end;
end;
{u[i] < x[n1] <= u[j], j-i = 1}
Замечание.
Более простое (но не минимальное) i хранить
максимальную длину возрастающей подпоследовательности,
оканчивающейся на x[i]. Это расширение приводит
к алгоритму с числом действий порядка $$n^2$$. Есть
и другой изящный алгоритм с квадратичным временем работы
(сообщил М. В. Вьюгин):
найти максимальную общую подпоследовательность исходной
последовательности и отсортированной
последовательности с помощью предыдущей задачи.
1.3.5. Какие изменения нужно внести в решение предыдущей задачи, если надо искать максимальную неубывающую последовательность?
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.