Читаем Программирование игр и головоломок полностью

Наконец, a, начиная с 4, умножается на 2 при каждом прохождении цикла; b начинается с 1, которое меньше соответствующего начального а = 4.

Тогда при переходе от a, b, p к a', b', p' либо

b' = b, а' = 2*а, так что если b < а, то и b' < а';

либо

b' = а + b, а' = 2*а, что также сохраняет справедливость отношения а' < b'.

Следовательно, вот ситуация, которую цикл оставляет инвариантной:

n = а*p + b2;

а — степень двойки,

p четно,

b нечетно, b < а.

Кроме того, мы знаем, что при выходе из цикла p < а.

Если p равно нулю, то n = b2. Тогда мы видим, что n — квадрат числа b, которое выводится, и все закончено.

Но n может оказаться полным квадратом и тогда, когда p не нуль. Попробуем рассмотреть все возможные случаи. Положим n = r2 (r нечетно). Соотношение

r2 = ар + b дает

r2b2 = ар.

Положим r + b = 2u, rb = 2v (r и b нечетны). Отсюда получаем 4uv = ар.

Поскольку r = u + v, где r нечетно, получаем, что u и v не могут быть числами одинаковой четности, так что одно из них четно, а другое нечетно. Так как а является степенью двойки, то нечетный сомножитель относится к p. Выявим его, полагая р = s2t, где s нечетно, a t ≥ 1 (p четно).

Напомним, что а = 2k. В этих обозначениях 4uv = ар = s2k+t, uv = s2k+t−2.

Возможные решения для пары u, v имеют вид пар

s'2k+t-2, s''

где s's" = s.

Покажем сначала, что s" — меньший из этих двух элементов пары. Вследствие t ≥ 1 имеем ktk + t − 2.

Вследствие p < а последовательно выводим

s2t < 2k,

s's"2t < 2k.

s's" < 2k-t ≤ 2k+t-2 ≤ s'22k+t-2

(потому что s' нечетен и не меньше 1).

Следовательно, нужно взять u = s'2k+t-2, v = s".

Покажем теперь, что нужно обязательно взять s' =1, s" = s. По выбору u и v

b = 2k+t−2s' − s" < а = 2k.

Отсюда получаем:

s" > 2k+t−2s' − 2k,

и, так как t ≥ 1:

s" > 2k−1s' − 2k,

s = s's" > 2k−1s'2 − 2ks = 2k−1s' (s' − 2).

Вследствие р = s2t < а = 2k выводим s < 2kt ≤ 2k−1.

Объединим два полученных неравенства:

2k−1s' (s' − 2) < x < 2k−1, поэтому s' (s' − 2) < 1.

Единственное нечетное число s', удовлетворяющее этому соотношению, это s' = 1. Следовательно, у нас остается единственная возможность:

u = 2k+t-2, v = s,

b = uv = 2k+t-2 − s < а = 2k,

s > 2k+t-2 − 2k.

Так как s < 2kt, то t должно быть таким, чтобы

2kt > 2k+t-2 − 2k.

Поскольку t должно быть строго положительно, то его единственными возможными значениями являются t = 1 и t = 2.

При t = 1 имеем

p = 2s, b = 2kts = a/2 − p/2.

Следовательно, если 2b = аp, то n — квадрат числа (а + p)/2 = аb.

При t = 2 имеем

p = 4s, b = 2ks = ap/4.

Следовательно, если p = 4(ab), то n — квадрат числа a + p/4 = 2аb.

Этим исчерпываются случаи, когда n может быть полным квадратом.

Можно спросить себя, могут ли эти различные случаи действительно осуществляться. Заметим, что при вступлении в цикл у нас b = 1, a = 4. После этого b может быть изменено добавлением а, т. е. кратным числа 4. Следовательно, b остается сравнимым с 1 по модулю 4. В трех возможных случаях:

p = 0, r = b,

p = а − 2b, r = ab,

p = 4 (ab), r = 2ab,

первый случай — единственный, в котором квадратный корень из n сравним с 1 по модулю 4; два других дают квадратный корень, сравнимый с 3 по модулю 4. При выходе из цикла равенство

b = ар + b2

с учетом соотношений p < a, b < a дает n < 2a2 и, следовательно, при выходе из цикла a2 > n/2. Равенство

ар = nb2

дает p = (nb2)/a < n/а.

Если окажется, что n/а < a, то непременно p < а и цикл закончен. Чтобы цикл остановился, необходимо, чтобы a2 > n/2, и цикл заведомо останавливается, если a3 > n.

Следовательно, все зависит от положения n по отношению к степеням двойки. Существует такое целое n, что

4q < n < 4q+1.

Возможны два случая. Во-первых, может выполняться неравенство

4q = 22q < n < 22q+1,

и тогда для k = q число a2 = 22q > n/2 может быть значением остановки, но в этом нет уверенности. С другой стороны, если

22q+1 < n < 22q+2,

то единственное значение a, удовлетворяющее условию a2 > n/2, есть a = 2q+1, и для этого значения имеем a2 > n, что гарантирует остановку. Поскольку r = ab, то а = r + b > r и, следовательно, a2 > n.

Во втором случае

r = 2ab и b < а, откуда а < 2ab = r.

Таким образом, все три распознаваемые программой случая являются единственными возможными исходами программы, и каждый из них может произойти.

Таким образом, перед нами — очень забавный алгоритм, который дает значение квадратного корня, и который определяет случай, когда n не является корнем, но в этом случае не дает никакой дополнительной информации.

Программа заведомо останавливается при а = 2q+1, так что число шагов цикла не больше q − 1 (начиная с 4), причем q — логарифм квадратного корня из n по основанию 2. Таким образом, получилась программа порядка In n, что дает ту же сложность, что и обычный алгоритм, действующий кусками по две цифры. Но для этого последнего алгоритма нужен еще первый цикл, чтобы найти порядок величины n.

Головоломка 19.

Соотношение f(a, b) = f(b, a) следует из самой инициализации p и q:

p := max (a, b); q := min (a, b).

Перейти на страницу:

Похожие книги

Programming with POSIX® Threads
Programming with POSIX® Threads

With this practical book, you will attain a solid understanding of threads and will discover how to put this powerful mode of programming to work in real-world applications. The primary advantage of threaded programming is that it enables your applications to accomplish more than one task at the same time by using the number-crunching power of multiprocessor parallelism and by automatically exploiting I/O concurrency in your code, even on a single processor machine. The result: applications that are faster, more responsive to users, and often easier to maintain. Threaded programming is particularly well suited to network programming where it helps alleviate the bottleneck of slow network I/O. This book offers an in-depth description of the IEEE operating system interface standard, POSIX (Portable Operating System Interface) threads, commonly called Pthreads. Written for experienced C programmers, but assuming no previous knowledge of threads, the book explains basic concepts such as asynchronous programming, the lifecycle of a thread, and synchronization. You then move to more advanced topics such as attributes objects, thread-specific data, and realtime scheduling. An entire chapter is devoted to "real code," with a look at barriers, read/write locks, the work queue manager, and how to utilize existing libraries. In addition, the book tackles one of the thorniest problems faced by thread programmers-debugging-with valuable suggestions on how to avoid code errors and performance problems from the outset. Numerous annotated examples are used to illustrate real-world concepts. A Pthreads mini-reference and a look at future standardization are also included.

David Butenhof

Программирование, программы, базы данных
Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ
Эффективное использование C++. 55 верных способов улучшить структуру и код ваших программ

Эта книга представляет собой перевод третьего издания американского бестселлера Effective C++ и является руководством по грамотному использованию языка C++. Она поможет сделать ваши программы более понятными, простыми в сопровождении и эффективными. Помимо материала, описывающего общую стратегию проектирования, книга включает в себя главы по программированию с применением шаблонов и по управлению ресурсами, а также множество советов, которые позволят усовершенствовать ваши программы и сделать работу более интересной и творческой. Книга также включает новый материал по принципам обработки исключений, паттернам проектирования и библиотечным средствам.Издание ориентировано на программистов, знакомых с основами C++ и имеющих навыки его практического применения.

Скотт Майерс , Скотт Мейерс

Программирование, программы, базы данных / Программирование / Книги по IT