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

Вы можете также действовать слева направо:

5cncn−1…c3c2 : 5 = cncn−1…c25

Деля левую цифру на 5, вы получаете cn = 1. Имея cn, вы можете продолжать деление. И здесь тоже вам нужно будет принимать во внимание перенос результата, полученного при предыдущем делении, и нужно будет знать, когда остановиться. Эти два метода по существу равносильны.

Остальное оставляю исследовать вам.

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

Обычно я бываю глубоко разочарован тем, что нахожу в книгах по информатике или по математике касательно квадратных корней. Чаще всего вам предлагают метод Ньютона: пусть вам нужно извлечь квадратный корень из числа x. Вы образуете возвратную последовательность ui по правилу

ui+1 = (ui + (x/ui))/2.

Вне всякого сомнения, вы можете взять u0 = 1 в качестве начального значения. Эта последовательность очень быстро сходится к квадратному корню из x. Если, например, взять x = 50 и воспользоваться формулой

ui+1 = целая_часть ((ui + (x/ui))/2),

чтобы иметь дело только с целыми числами, то в качестве последовательных значений и вы получите

u0 = 1, u1 = 25, u2 = 13, u3 = 8, u5 = 7, u6 = 7.

Чтобы использовать здесь этот алгоритм, вы должны написать программу целочисленного деления двух целых чисел большой длины.

Другой способ действия основан на том факте, что разность двух последовательных квадратов есть нечетное число:

(n + 1)² − n² = 2n + 1,

так что последовательные разности являются последовательными нечетными числами. Поэтому можно видеть, что сумма нечетных чисел от 1 до 2k − 1 включительно есть k². Обратно, если вычитать из n последовательно возрастающие числа, пока это возможно (не допуская, чтобы результат становился отрицательным), тогда искомый квадратный корень есть к, если последнее нечетное вычитаемое равно 2k − 1. Таким образом, для 50

50 − 1 = 49,

49 − 3 = 46,

46 − 5 = 41,

41 − 7 = 34,

34 − 9 = 25,

25 − 11 = 14,

14 − 13 = 1.

Нельзя продолжать, не получая отрицательной разности. Последнее нечетное вычитаемое равно 13, поэтому корень есть (13 + 1)/2 = 7 (и остаток 1). Этот способ гораздо лучше подходит для распространения на случай очень больших чисел, потому что вам требуется реализовать только две операции:

— прибавить 2 к большому числу;

— вычесть одно большое число из другого.

Но число шагов цикла равно искомому квадратному корню, а он может оказаться весьма большим.

Можно обобщить предыдущий алгоритм, используя свойства десятичной записи чисел. Данное число разделяется на куски по две цифры, начиная справа; затем мы начинаем вычитать последовательные нечетные числа из крайнего слева куска:

Если это нельзя продолжать дальше, то последнее вычитаемое число увеличивается на единицу, сдвигается на один шаг вправо, и следом за ней приписывается единица. Это — первое нечетное число, которое следует вычитать из предыдущего остатка.

В приведенном выше примере 7 + 1 = 8; приписывая 4, получаем 81 и продолжаем:

Поскольку продолжать дальше нельзя (последнее возможное вычитание из остатка — это крайнее справа), то последнее из вычитаемых чисел нужно увеличить на 1, а затем разделить на 2, чтобы получить корень. Последний остаток и есть остаток квадратного корня:

85 + 1 = 86, 86/2 = 43,

1909 = (43)2 + 60.

Этот алгоритм достаточно прост для программирования при длинных числах, и он дает вполне разумное время вычисления.

У вас много возможностей представлять свои данные. Так как мы оперируем с кусками из двух цифр, то вы можете задавать свои данные таблицами целых чисел в интервале от 0 до 99.

Вы можете представлять свои целые числа как цепочки символов, где используются только числовые символы (цифры) от 0 до 9. Выбор способа зависит от ваших предпочтений и от возможностей вашей машины оперировать с таблицами и цепочками. Тщательно рассмотрите, какие операции нужно сделать. Вы ничем не ограничены: почему бы не запрограммировать и сравнить два разных решения?

Я предложил вам алгоритм без доказательства. Поэтому попытайтесь его проверить…

Я предложил вам алгоритм для десятичной системы счисления. Можно предложить похожий алгоритм для двоичной системы. Тогда не возникнет цикл вычитаний последовательных нечетных чисел из каждого куска, поскольку в куске есть только одно нечетное число: 1. Алгоритм упрощается: если можно вычесть нечетное число — мы его вычитаем, в противном случае мы не делаем ничего. Затем сдвигаем, добавляем 1 и приписываем 1 в конце… Этот алгоритм намного легче реализовать. Но тогда нужно сначала перейти к основанию 2, а затем преобразовать двоичный результат в десятичный. Вам следует посмотреть, что более эффективно…

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

Аккуратно поставим задачу. То, что от вас требуется, — это не взятая глобально последовательность, а вот что: если начало последовательности выписано, то нужно найти следующее число. Возьмем пример, данный в головоломке 5: какое число следует за 50?

Есть ровно три возможности.

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

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

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