Опубликован: 08.04.2009 | Доступ: свободный | Студентов: 485 / 0 | Длительность: 17:26:00
Специальности: Программист
Лекция 1:

Переменные, выражения, присваивания

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. Подсчитать количество решений неравенства x^2 + y^2 < 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 операций.)

Решение. Нас интересуют точки решетки (с целыми координатами) в первом квадранте, попадающие внутрь круга радиуса \sqrt{n}. Интересующее нас множество (назовем его X ) состоит из объединения вертикальных столбцов убывающей высоты.


Идея решения состоит в том, чтобы "двигаться вдоль его границы", спускаясь по верхнему его краю, как по лестнице. Координаты движущейся точки обозначим <k,l>. Введем еще одну переменную s и будем поддерживать истинность такого условия:
\begin{quote} 
  \rule{0pt}{0pt}<k,l> находится сразу над k-ым столбцом;   \\ 
  s -  число точек в предыдущих столбцах. 
\end{quote}
Формально:

  • l - минимальное среди тех l\ge 0, для которых <k,l> не принадлежит X ;
  • s - число пар натуральных x, y, для которых x<k и <x,y> принадлежит 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 > 1. Определить длину периода десятичной записи дроби 1/n.

Решение. Период дроби равен периоду в последовательности остатков (докажите это; в частности, надо доказать, что он не может быть меньше). Кроме того, в этой последовательности все периодически повторяющиеся члены различны, а предпериод имеет длину не более 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 и mod, за исключением деления на 2 четных чисел; число шагов не должно превосходить C_1 log(a/b)+C_2 для некоторых констант C_1, 
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}