Один из возможных путей реализации программы — прямой перебор семизначных чисел и выбор среди них тех, что обладают «квадратным» свойством — отвергнем сразу. Во-первых, перебор очень большой. Кроме того, не совсем ясно, как производить отбор.
количество «квадратных» -значных номеров | |
---|---|
Выберем другой подход. Заметим, что каждый «квадратный» номер начинается с -значного квадрата, за которым следует -значный номер, также обладающий «квадратным» свойством. Иными словами, каждый квадратный номер можно представить в виде конкатенации «головы» и «хвоста», где «голова» — квадрат, а «хвост» — квадратный номер.
Такое рекурсивное свойство квадратных номеров ведёт к рекурсивному алгоритму их построения. Если перебирать все «головы», -значные квадраты, где , и к каждой из них приписывать всевозможные «хвосты», -значные «квадратные» номера, где , мы получим то, что хотим.
Таким образом, удалось свести задачу поиска семизначных номеров к задаче поиска -значных для всех , . Мы оказываемся с ситуации, когда полезно обобщить исходную задачу: вместо поиска 7-значных номеров научим программу искать -значные для любого целого неотрицательного . Казалось бы, поставив перед собой более общую задачу, мы добровольно усложняем себе жизнь. В данном случае это не так.
Рекурсивная постановка задачи вынуждает нас оформить поиск -значных квадратных номеров в виде
процедуры (назовём её sqPhones
). Ей придётся рекурсивно
вызывать её же самоё для поиска всевозможных -значных хвостов для всех
.
Процедура получает число как
параметр. Список найденного (в виде массива) будет возвращаться из процедуры.
К примеру, при
должен быть возвращён список строк ('0-0', '0-1', '0-4',
'0-9', '1-0', '1-1', '1-4', '1-9', '4-0', '4-1', '4-4', '4-9', '9-0', '9-1',
'9-4', '9-9', '16', '25' '36', '49', '64', '81')
,
при
— список ('0', '1', '4', '9')
. А что же возвратит
процедура при
?
Этот вопрос оказывается трудным для тех, кто только начал постигать рекурсию,
и вместе с тем он принципиален. Чаще всего отвечают: пустой список. Что ж,
сейчас мы с треском опровергнем это предположение. Для этого уясним, что
процедура sqPhones
содержит два вложенных цикла:
во внешнем перебираются головы, а во внутреннем — хвосты. В теле внутреннего
цикла очередная голова соединяется с очередным хвостом (между ними вставляется
дефис), и получившаяся строка добавляется в массив, изначально пустой. После
того как отработает внешний цикл, массив возвращается из процедуры.
Предположим, что процедура вызвана с параметром 1
. Тогда
внешний цикл прокрутится четырежды: найденные в нём головы — это
'0'
, '1'
, '4'
и '9'
. Для поиска хвостов, присоединяемых к каждой из
голов, состоится рекурсивный вызов, который, как мы неверно предполагаем,
возвратит пустой список. Во внутреннем цикле перебираются найденные хвосты, и,
заметим, этот цикл не сработает ни разу! А ведь именно в теле внутреннего цикла
происходило построение списка результатов. Таким образом, этот список так
и останется пустым, и процедура не найдёт ровным счётом ничего и для
,
и затем для
,
и вообще для любого .
И каков же правильный список -значных
квадратных номеров? Это список, состоящий из единственной пустой строки: ('')
. Это совсем не то же самое, что пустой список.
Тогда при
к каждой односимвольной голове (из списка ('0', '1', '4',
'9')
) будет присоединён единственный пустой хвост, что нам
и требовалось.
Осталась только одна проблема. Соединяя голову и хвост, мы собирались вставлять
между ними дефис. Но дефис между головой и пустым хвостом будет смотреться
странно: вместо ожидаемого списка ('0', '1', '4',
'9')
мы получим ('0-', '1-', '4-', '9-')
.
Если так, то знаком дефиса будут заканчиваться все строки, найденные процедурой
при любом положительном . Выход из
положения очень простой: соединяя голову с хвостом, вставлять дефис только если
хвост не пуст.