ВЛДАГЕНЕ ПК.ГРИГАС кЖ аугутис
ЗАДАЧ
ПО ПРОГРАММИРОВАНИЮ
ББК 32.973-01 Д 14
Рецензент: ведущий научный сотрудник Инс...
27 downloads
461 Views
82MB Size
Report
This content was uploaded by our users and we assume good faith they have the permission to share this book. If you own the copyright to this book and it is wrongfully on our website, we offer a simple DMCA procedure to remove your content from our site. Start by pressing the button below!
Report copyright / DMCA form
ВЛДАГЕНЕ ПК.ГРИГАС кЖ аугутис
ЗАДАЧ
ПО ПРОГРАММИРОВАНИЮ
ББК 32.973-01 Д 14
Рецензент: ведущий научный сотрудник Института программных систем АН России доктор педагогических н аук Ю. А. П ервин
Д14
Д аген е В. А. и др. 100 задач по программированию: Кн. для учащ ихся: Пер. с лит. / В. А. Д агене, Г. К. Григас, К. Ф. Аугутис.— М.: Просвещение, 1993.— 255 с.: ил.— ISBN 5-09-003864-3. В книге рассмотрены конкретные задачи по программированию. Все задачи интересны или своей формулировкой, или неожиданным результатом, или алгоритмом решения. Программы написаны на языке Паскаль. Книга будет интересна всем, кто ж ел а е т практически, по при мерам, научиться составлять программы разнообразных задач и ре шать их при помощи ЭВМ.
„ 4306020000— 330 — —— 34— 92, инф. письмо — 92 Д ------м 1 0 3 (0 3 )— 93 v ISBN 5-09-003864-3
©
m Б Б К 32.973-01
L eldykla «Sviesa», 1986
© Д аген е 1$. А. и другие. Перерод Шмелева Д. Д ., 1993
П РЕД И СЛО ВИ Е
Чтобы стать программистом, необходимо преодолеть про пасть, . разделяющую математическую за д а ч у и программу, т. е. уметь находить решение каж дой задачи и вы р аж ать его на язы ке программирования. А научить этому может только практика, поэтому необходимо самому составить мно го программ и разобрать очень много программ, составлен ных другими. Мы полагаем, что приведенные в этой книге программы о к аж утся д ля читателя полезными. Задачи подобраны разно образные. Д л я многих из них мы заимствовали идеи из по пулярных книг по математике, часть составили в процессе работы с учащимися Литовской заочной школы молодых программистов. Все программы написаны на язы ке П аскаль. Мы с т а р а лись подобрать программы таким образом, чтобы они были понятны начинающему программисту, зн акомящ емуся с основами программирования и с языком П аскаль. Мы д у маем, что приводимые программы помогут читателю найти ключ к решению многих других задач, здесь не разбираемых. Задачи излагаю тся таким образом, чтобы их можно было чи тать в произвольном порядке. Тесно связанные м еж ду собой чадачи мы старались д ать рядом. В книге есть и такие программы, которые сл у ж а т для решения ранее решенных задач или задач, которые легко решаются без помощи ЭВМ. Подобные программы приво д ятс я потому, что в них есть интересные моменты (ориги нальный способ решения, алгоритм, употребление разно образных конструкций язы ка программирования), что может пригодиться и д л я решения других задач. Почти после каждой задачи приводятся зад ан и я для с а мостоятельной работы. В конце, книги (в разделе «Заклю чение») содержится краткое обсуждение практических вопросов, касающихся выполнения программы на ЭВМ.
Приведенный в книге СПИСОК Литературы включает не только цитируемые, но и другие книги, которые пригодятся читателям, заинтересованным в более широком и глубоком знакомстве с различными вопросами программирования. За замечания и предложения авторы благодарят рецен зента книги доцента Вильнюсского университета В ладаса Тумасониса, сотрудника университета Р имантаса Д аги са, учащихся Литовской заочной школы молодых программистов Видаса Стаугайтиса и Айдаса Ж андариса (они читали рукопись, будучи д есятиклассни кам и ). Особо ценные замечания, советы и предложения авторы получили от учащегося Литовской заочной школы молодых программистов, т а к ж е десятиклассника Витолиса Бендинскаса. Авторы благодарны т а к ж е сотрудникам Викторасу Д агису и Зите Куралавичюте за выполнение приводимых в книге программ на вычислительной машине'. Д л я решения одной и той ж е задачи можно составить много программ. Мы старались составить и здесь представить лучшие (те, которые короче, яснее, естественнее отражаю т суть зад ач и ). Однако д ум аем , что и эти программы не самые совершенные. Будем благодарны читателям за замечания, новые решения, а т а к ж е интересные задачи. Наш адрес: Л и т ва, г. Вильнюс, ул. Акадёмиёс, 4, Институт математики и информатики.
6
ВВЕДЕНИЕ Д л я того чтобы решить зад ачу, мы должны прежде всего знать, что дано (исходные данны е), а т а к ж е формулировку задачи (условие.) Решив задачу,, мы получаем то, что тре буется по условию,— результат. Если з а д а ч а решается при помощи электронно-вычисли тельной машины (Э В М ), то работа раздел яется на две части. Человек (программист) .пишет программу, а машина выполняет эту программу и исходя из предъявленных ей ис ходных данных получает результат. Все решение задачи можно изобразить при помощи схемы, приведенной на ри сунке 1. Из схемы видно, что результат работы ЭВМ и я в л я е т ся результатом решения задачи. А результат работы про гр а м м и с т а — программа. С оздавая программу, он решает задачу по программированию, а машина, выполняя програм м у,— зад ач у на вычисление. Д л я одной и той ж е задачи можно составить очень много жвивалентных программ, выполняя которые ЭВМ будет получать один и тот ж е правильный результат. Какую же программу выбрать, какую считать самой лучшей? Из схемы (см. рис. 1) можно сделать вывод, что програм ма пишется для машины, т. е. программу пишет (составляет) человек, а читает (использует) ее машина. Так полагали прежде. От программиста не требовалось, чтобы он раскры вал содержание программы. Достаточно было, чтобы машина Исходные данные
Рис. 1. Схема решения задачи. 7
в соответствии с этой программой решила задачу. Крите рии оценки программы были чисто «машинными». Лучшей программой считалась та, которая экономно использовала ресурсы ЭВМ : время и память. Поскольку самые первые ЭВМ производили вычисления достаточно медленно и обладали небольшим объемом памяти, то эти критерии были действи тельно очень важными. С течением времени положение изменилось. Выросли быстродействие и объем памяти ЭВМ. Поэтому экономич ность программы утратила решающее значение. С другой стороны, практика показала, что машина не единственный читатель программы. Очень часто ее читает и разбирает че ловек. Тем самым он знакомится с идеями и опытом других программистов, учится программировать, самостоятельно составлять программы: ведь не стоит изобретать велосипед. Кроме того, при том или ином видоизменении задачи легче модифицировать старую программу, нежели создавать новую. Приходится читать и свои собственные программы. Чаще всего их усовершенствованием и заним ается сам автор. Когда программа только что написана и еще остается свежей в памяти, читать ее совсем легко. Однако с течением времени она забы вается. Во всех упомянутых случаях необходимо вникать в смысл программы. Поэтому она долж на быть на писана ясно и понятно. Таким образом, появляется новый критерий оценки программы — степень ее ясности. Приведенные в книге тексты программ — это результаты решения зад ач по программированию, аналогичные ответам математических задач. А д ля того чтобы получить ответ, нам часто приходится к ак следует поработать, испытать различные пути решения. Поэтому часто важ ен не только результат программирования — программа, но и способ, посредством которого нам удалось ее получить. Это осо бенно интересно д л я тех, кто сам собирается составлять программы. Пути составления многих программ в книге не описы ваются во всех подробностях. Это заняло бы немало места и было бы скучно повторно писать одни и те ж е поясне ния, поскольку почти везде применяется одинаковая мето дика программирования. Большая за д а ч а обычно распадается на части, к аж дую из которых можно программировать отдельно. Часто для более крупных частей пишется функция или процедура. Если функция или процедура т а к ж е о казы вается слишком сложной, то она программируется т а к ж е, к а к и вся зад ач а : разложением на меньшие части. Методика программиро
вания детально описана в «Н ач ал ах программирования» Ш . а здесь мы только поясним ее на одном примере. Тем самым мы покажем, к а к по тексту программы можно д о га даться о том, каким образом она была составлена. Например, вычислим, какое наименьшее число почтовых марок по 10, 4 и 1 к. надо наклеить на простую бандероль, нес которой представляет собой исходное данное. (П лата за посылку бандероли вычисляется следующим образом: за бандероль весом до 50 г взимается 10 к., за к а ж д ы е сле дующие полные или неполные 50 г — еще по 5 к.) Составим программу д л я решения этой задачи. Разобьем зад ач у на четыре следующие части: 1. Ввод исходных данных. 2. Вычисление платы за посылку бандероли. 3. Подсчет числа марок, 4. Печать результатов. Составим схему программы: program бандероль (input, output); (* вес бандероли *) v a r гр, (* плата за бандероль * коп, м\ 0, (* марки по 10 коп. *) (* марки по 4 коп. *) м4, (* марки по 1 коп. *) м 1: integer-, function плата (г р -.integer):integer-, плата за бандероль весом гр (граммов) procedure integer)-,
м арки
(K on:integer ;
var
м 10,
м4,
м \:
число марок по 10, 4 и 1 к., которыми можно в ы р а зить плату в коп (копейках) begin read (гр)-, коп: = плата (гр)\ марки (коп, м\0, м4, м\); печать результатов end. На схеме в законченном виде отраж ена только 1-я часть. ( )на записана при помощи одного из операторов язы ка
Паскаль — read (гр ). Три прочие части не закончены. Вместо них на схеме приведены прямоугольники с описанием их действий словами. Предполагается, что 2-я часть будет в ы р аж ен а функцией* 3-я часть — процедурой (заголовки для них мы у ж е создали), а 4-я часть будет внесена непосред ственно в текст программы. Детализируем действия, записанные в прямоугольниках. Действия первого прямоугольника (функция п лата) записы ваются так: begin п л ат а: = \0-\-((гр — 1) div 50)*5 end Действия второго прямоугольника (процедура марки)' может быть записана таким образом: :v: тоЕ begin м\ 0 : = к о п коп : = коп м4 : = коп м\ : = к о п ; end ,,
div 10 . mod 10 div 4 mod 4
,,
,
,
.,1
Действия по печати результатов совсем просты, поэтому мы не выписываем их отдельно, а непосредственно включаем в текст программы. Теперь, написав функцию п лата и про цедуру марки, мы получим законченную программу: pro gram бандероль (input, output)', v a r гр. (* вес бандероли *) коп, (* плата за бандероль *) м 10, ;* марки по 10 коп.*} м4, (* марки по 4 коп.*) м\: ;* марки по 1 коп.*) , г; integer-,
'' .1
function плата {zp:integer):integer\ (* плата за бандероль весом гр (граммов)*)
п л а т а : = 10 + ((гр — 1) div 50)*5 end; procedure марки (Kon:integer-, v ar ж 10, м4, м \ ’. integer) (* число марок по 10, 4 и 1 к .,* ) (* которыми можно выразить плату в коп (копейках)* begin ж 10: — коп div 10; коп: = коп mod 10; ю
мА : = коп div 4; м\ : = коп mod 4; end; begin read (гр); ко п : = п лата (гр); марки (коп, м 10, л4, ж 1); writeln ( ВЕС Б АН Д ЕРО ЛИ : , г р :5 , Г ); а»гйв/п (Ч И С Л О М А Р О К :’, л 1 0 + л 4 + л 1 :7 ); шгг/е/я (’ИЗ НИХ’, м 1 0 :6 , ’ПО 10 КОП.’); writeln (” :9, м 4 :3 , ПО 4 КОП. ); writeln ( :9 , м 1 :3 , ПО 1 КО П .) end. Вот мы и составили программу. Обратим внимание на то, что во всей книге, к а к и в приведенном примере, действия, соответствующие незапрограммированным частям, формули руются в сж атом виде и приводятся в прямоугольниках. 11римоугольники могут находиться в любой части программы (на месте процедур, группы операторов, д а ж е на месте условия в операторе if и т. д., если эта группа является хоть в какой-то степени более сложной и самостоятельной пи сравнению с другими частям и). Теперь представим себя на месте читателя программы, у которого перед глазам и только окончательный текст проI рам мы бандероль и который хочет выяснить, каков был путь составления программы. Прежде всего познакомимся с описаниями переменных, щголовками функций и процедур (пока мы не будем в д а ваться в выполняемые ими д ей ств и я ). К ак следует разбе ремся в действиях основной части программы. Оператор инода данных ясен сам по себе. Д ал ее идет обращение с функции плата. Прочтем только комментарий после заIп.повка функции и, не рассм атри вая сами действия, поверим, ч го функция действительно вычисляет плату за посылку бан дероли. Следовательно, переменной коп приписывается зн а мение платы. Д ал ее — обращение к процедуре марки. Снова, прочитав комментарий после заголовка процедуры, поверим результатам процедуры. Печать результатов т а к ж е совер шенно очевидна. Вот мы и получили представление об общем виде програм мы. Мы уяснили себе именно ту часть программы, которая I,ыл а составлена прежде других. Каким образом программа составлялась дальш е, показывают функция и процедура. Разберемся теперь в них. Если бы в их составе были еще 11
какие-то функции и процедуры, то мы сначала только позна комились бы с их заголовками и лишь затем стали бы вни кать в их содержание. Тем самым, разбирая программу, мы идем тем ж е путем, каким программа была состав лена. Если читателя заинтересует не вся программа, а лишь какая-то её часть, он может и не разбирать всю программу. Предположим, читатель интересуется только тем, к а к плата в ы р а ж а ется в почтовых м ар ках (например, он обдумывает, как записать действия, при помощи которых можно данную сумм у выразить в монетах или банкнотах). Тогда ему д о ста точно разобраться в процедуре марки. Он обнаружит, что в ней отдельно записаны все интересующие его действия. Обратим внимание на то, что составитель программы одновременно может составлять какой-то небольшой блок (процедуру, функцию, группу операторов). Точно т а к ж е и читатель программы может сразу охватить взглядом неболь шой блок программы. Поэтому обычно стремятся программ ные блоки делать небольшими, чтобы можно было сразу! ж е разобраться во всем блоке. Его текст не должен превы шать страницы. В программах много места уделяется вводу и выводу д а н ных, особенно если требуется при печати красиво располо жить результаты. Однако для любознательного читателя программ более интересны алгоритмы, относящиеся к с у ществу задачи. Мы часто можем избеж ать углубления в детали ввода и вывода данных, составляя и д л я всей задачи вместо программы функцию или процедуру. Например, для рассматриваемой задачи вместо программы бандероль можно было бы составить процедуру, имеющую такую схему: procedure банд (ё р :integer-, v ar л*10, м4, м \ -.integer)-, (* число марок по 10, 4 и 1 к., которые нужно *) (* наклеить на бандероль весом гр (граммов)*) function п лата (г р -.integer):integer; procedure марки (ко п :integer; v a r ж 10, м4, м\ -.integer); begin (* банд *) марки (плата (гр), л* 10, м4, м\) end Программист, стремящийся полностью решить задачу, должен сам написать программу, обрамляющую такую про цедуру. В ней должны быть предусмотрены ввод исходных данных, обращение к процедуре и печать результатов. 12
Обратим внимание на то, что в книгах и ж ур н ал а х очень iacTO решение задачи оформлено при помощи функций I процедур, поскольку именно т а к можно в сж атом виде пек-дать существо задачи, а всякий программист сможет вклюшть эту процедуру или функцию в программу, которую он ■оставит в соответствии со своими потребностями. В данной книге мы т а к ж е часто прекращаем составление фограммы д ля той или иной задачи на этапе создания функщи или процедуры. Вообще, в программах, для которых (вод или вывод данных не относится к существу дела, мы громимся записать эти действия к а к можно проще и лакошчнее. Мы не будем программировать и контроль исходных 1.1ппых. Например, в программе бандероль входным данным может быть любое целое (в том числе отрицательное) чисKI, а вес бандероли должен принадлежать некоторому интерt.i.iiy (скаж ем , от 1 г до 3 к г). Проще всего проверить при надлежность исходных данных указанн ому интервалу, если переменные, соответствующие данным, описываются при помощи типа отрезка. Например: v a r гр : 1 .. 3000 Каким образом будет реагировать Э В М на недопустимые манные и к ак она об этом проинформирует пользователя, замисит от конкретной ЭВМ и ее транслятора. М е ж д у тем c h in ация будет значительно более ясной д ля пользователя, I I in ЭВМ напечатает сообщение об ошибке исходя из услопия задачи. Д л я того чтобы сделать это, надо записать и программе проверку значений переменной гр, а изменять |е описание не надо. Например: ii ,■/> -3000 then write ( ВЕС БАНДЕРОЛИ ПРЕВЫШАЕТ НОРМУ’) else if гр < С = 0 then write (’ВЕСЫ Н ЕИ СП РА ВН Ы ) else ■ « , вычисление и печать результатов Как видим, исчерпывающий анализ исходных данных и печать сообщений об ошибках занимают немало места. По т о м у мы не включаем эти действия в программы, хотя они пчеиь важны , особенно если программой пользуются многие люди.
1. Факториал
Функция вычисления факториала хорошо известна про граммистам. Она приводится почти в каж дой книге по про граммированйю. Поэтому с нее мы и начнем. Факториалом числа п назы вается произведение 4ncej от 1 до п включительно: п\ = 1 - 2 - 3 - ... -п. Кроме того, принимается, что 0! = 1. Факториал определяется еще и таким образом: /0! = 1, \п\ = п-(п — 1)!, 1. Это рекурсивное определение. Опираясь на него, легко написать рекурсивную функцию: function f (п '.integer)'.integer; (* факториал числа п *) begin if //— 0 then f : — 1 else f : = n*f(n — 1) ! end f.a г о. .. .1 Факториал можно вычислить и посредством нерекурсив ной функции. Она могла бы выглядеть так: function f (n :in t e g e r ) :in t e g e r ; (* факториал числа п *) v a r ff, i : integer; begin //: = !; for i : — 1 to я do i-= ff
end Эту функцию мы включим в программу, которая печа тает первые десять натуральных чисел и их факториалы 14
program факториалы (output ); v ar k '.integer-, function f ( n : in te g e r): integer; v ar ff, i : integer; begin f f - = 1; for i : = 1 to re do ff:= i* ff; f := ff end;
begin for k : = 1 to 10 do w r i t e l n (k, f(k))
(Mid.
■
: :‘
Выполнив данную программу, ЭВМ напечатала бы такие Iи :»ультаты: 1 1 . i ■ii - ; 2 2 ,040': 6 3 ■jo ■f-Hqot, 24 4 120 5 -- ;\:-'iv== 720 6 5040 7 40320 8 ,hxi d : i 362880 9 3628800 10 При внимательном рассмотрении можно заметить, что ил программа нерациональна: факториал каж до го числа вычисляется заново с самого начала. Этих повторных вы числений следовало бы избегать, особенно когда важно беречь машинное время. Перенеся действия функции в основ ную часть программы и соединив их с печатью, мы получили (ц,1 значительно более быстродействующую программу: program факториалы (output); й-1 \ var ff, i'.integer; begin //: = i ; . for /: = 1 to 10 do begin ff:= i* ff; writeln(i, ff) end end. 15
Возникает вопрос: а нельзя ли было бы сделать наобо рот — включить печать в функцию? Тогда функция вы глядела бы так: function f ( п : in te g e r) : integer; v a r //, i : i n t e g e r\ begin f f' = i; for г : = 1 to n do begin
w'riteln(i, ff) end; f-= ff end -
О бращ аясь к этой функции, мы получаем не только фа кто риал числа п (который в данном случае, возможно, и не по надобится), но и все числа от 1 до п, напечатанные вмеси с их факториалами. В таком случае говорят, что функция имеет побочный эффект (в рассматриваемом примере по бочным эффектом является печать). Вообще говоря, посред ством функций принято описывать такие действия, резуль татом которых является одно определенное значение. В языке П аскаль значение функции может быть только простым чис лом, скалярным, логическим, символьным значением или у к а зателем. А все прочее относится к побочным продуктам. В силу сказанного, если одного значения недостаточно, обычно используют процедуру. Так, упомянутые действия по печати: факториалов лучше описать посредством процедуры: procedure фпечать (n '.in teger); v a r ff, i'.integer; begin f f ’ = i; for i : = 1 to n do begin
writeln (г, ff) end end
0 Входное данное — натуральное число а. Состав функцию f, значение которой удовлетворяло бы такой сис теме неравенств: lf ( a ) \ < a , \ (f(a)+ \ )\ > a. 16
2. Осторожно: maxintl Самым большим целым числом, с которым может иметь дело ЭВМ, в язы ке П аскаль яв л яется стандартная констан та maxint. Если диапазон чисел симметричен, то наимень шим числом будет — maxint. Когда при выполнении ариф метических операций получаем слишком большое или слиш ком маленькое число, создается аварийная ситуация и фик сируется ошибка, именуемая переполнением. В таком случае » В М чаще всего печатает сообщение о том, что имеет место аварийная ситуация, и прекращает вычисление. Конечно, польза от этого невелика, так к а к результаты остаются неиодсчитанными. 11оэтому, если грозит опасность переполнения, целесооб разно заранее предусмотреть в программе действия, помо гающие избежать этого. Чаще всего мы сталкиваемся с переполнением при вычис лении факториала. Если 10! можно вычислить при помощи почти всякой ЭВМ, то 20! не «ум естится» в большинстве ЭВМ. 11срестроим приведенную в зад ач е 1 программу факто риалы таким образом, чтобы она печатала самое большое натуральное число и его факториал, который еще может 01,т . вычислен на имеющейся электронно-вычислительной машине. program факториал (output ); (* наибольший факториал, допустимый для данной ЭВМ *) v ar г, ff'.integer; begin /: = i ; = repeat i: = i-\- 1; ff:= i* ff until ff > maxint div (г+ 1 ); («'!*(/ + 1 ) > m axint *) writeln (F (\ г :1 ,’) = ’, ff) end. ЭВМ EC, используя транслятор язы к а П аскаль, пред назначенный д ля обучения, напечатала следующий резуль тат: F( 12) = 479001600. Если при решении задачи появляются и отрицательные числа, то переполнение угр о ж ает с обеих сторон и избежать ого труднее. Составим программу, которая бы проверяла, не приведет Гаказ 133
п ^ __ ..
1
СРЕДНЯЯ
17
ШКОЛА * I v
ли суммирование двух исходных данных — целых чисел а и b — к переполнению. Если будет переполнение, то напеча таем соответствующее сообщение. Д л я проверки, нет ли пе реполнения, создадим функцию переполн : function переполн (a, b: integer): boolean;, begin if .(аг^О ) and ( b ^ 0) or ( a ^ ; 0 ) and (b ^ O ) then переполн: = false else begin a : = a b s (a); b : = a b s (b)\ if maxint — a Куб любого натурального числа п равен сумме п не>
Пояснимj o k h m образом по этой формуле можно получить корень у = °фс. Сначала примем, что корень равен произволь ному числу (например, уо = 1; доказано, что получающе еся в результате значение корня не зависит от того, какое значение у 0 мы избрали в качестве исходного), вычисляем новое значение корня у\, затем опять новое значение кор ня у 2 и т. д. К аж дое новое значение у п оказы вается все ближе к подлинному значению корня. Когда разность м е ж д у значением у п и предыдущим значением г/„_| становится весьма малой — меньшей, нежели допустимая погрешность, принимаем, что полученное значение у п является корнем, и вычисление заканчивается. Приведем ряд примеров, показывающих, к а к и звлекает ся кубический корень из 125 при различных исходных зн а чениях:
у 0= х = 125.0; У\ = 8 3 .3 ; 1/2= 55.6; 1(1 I 1111 уу. ■ . " Ч 1 о| |,|иим функцию извлечения корня т -й степени (где натуральное число) из неотрицательного числа х с 11hi in ic I ыо до 10- 5 : f и 1111Ion корень (x\real\ m : in t e g e r ) :r e a l; ■ корень m -й степени из x *) const эп силон = \Е — 5; vai /у, у у , w, z, m m :r e a l; k : integer; Ill'IjlU
mr. 1.0; (* исходное значение корня *) т ш : —( т — 1) / т ; z : = x / m ;
repeat !Г -= у у ;
к : — l;
29
w := z ;
while ( £ < m ) and ( w ^ - эпсилон) do begin w := w / y y ; k := k + \ end; у у : = y *m m -\ -w until ab s(y у —y)
,
IHtucdmc п аск (var t -.треуг); vai I, /: I ..At; i «и i : for
1 to n do 1 to n do t\L /]: = 0 ; for /: = ! to л do t\i, 1]: = 1; for i : = 2 to n do for /: = 2 to i do t [i, j \ : = t [ i — 1, j — 1 ] + ■m l ; (* паск *) p im cd u re узо р ( t -.треуг; k :in te g e r ) ; v ar i, j : 1..n; tie-in
|J:
/: = = /+
/ [/ —
1,
/]
39
for г: 1 to /г do begin write ( :( « — / + 1)); (* пробелы в начале строки for j : = 1 to г do if t [i, j] mod k = 0 then write (^ ’ : 2) else write ( M ’ : 2); writeln end end; (* узор *) begin (* треугольник *) . паск (tt); узор (tt, k) end. © Составьте программу, которая сохраняла бы толь одну строку треугольника П аскал я — вычисляла бы по оч реди все строки и тотчас ж е печатала бы их.
12. Треугольник Исходные данные — три натуральных числа, вы р а ж а щих длины отрезков. Требуется определить, можно ли из эт отрезков построить треугольник. Составим логическую функцию д ля решения этой з а л а 1 Будем опираться на известное положение: из трех отрезке можно построить треугольник тогда и только тогда, коп сумма длин любых двух отрезков превосходит длину третьег function треугольник (a, b, c :in te g e r):b o o le a n ; begin треугольник: = (а -\ -Ь > с) and ( a - \ - c > b ) and end (6 + 0 0 ) Эту зад ач у можно решить и в случае, если используют исходные данные вещественного типа. Тогда во всех фуь циях целый тип следует заменить на вещественный тр Поскольку операции с действительными числами выполняю ся приближенно, то в пограничных случаях можно получи неверный результат. Например, если
\a-\-b — с \ = г (где е — весьма малое число, сравнимое с погрешностям которые допускает Э В М ), то результат функции может ок заться каким угодно. 40
и i-ttt к I i |шмму, которая определяла бы и вид пНЙЬМИМ (N /I*| I ниц,и о гречки позволяют его постро енной. нфншшн, раипобедренный, разносторонний, i М М н^ни I in >s 11).'ti.iiiiii”i , остроугольный. • immi ........ min m ax. . т и п при» ......... рая находила бы все возможные i , i " t i HHt>H ...... . I горим которых (а, b и с — натуральI ип )и) > ......... пюря.пи бы неравенству пин ■ а b^ .с ^ т а х . I I i
b n
i i i
н а т у р а л ь н ы е
ч и с л а
и
Мчиим | 1п|н и | I I ....... треугольника печатайте на отдель1 1 in in ■ п. I пи,Iе функцию, которая определяла бы, [НМНн *i|i и . и...... . четырех отрезков (а, Ь, с и d — н атураль н а ни ни * иг I ш и т . прямоугольник. •I I
I | И Ц
I Мнф.мпримы числа • \m.-hi I и I 111-111>и длин катетов а и b прямоугольного ||ци и...... 1 р......а киадрату длины гипотенузы с:
а 2 -\-Ь2 = с2. i i .....I I п т ур.мм.ных чисел, удовлетворяющих этому p a id I” , mi и,ищется Пифагоровыми числами. Например, I I ii in ииони I [пфагоровыми числами, поскольку 32 + 42 = 52. . .........i i Пифагоровых чисел была известна у ж е в 11.. мн. I l iiim e 1'оворят, что строители пирамид, чтобы ■■ее |•11111• прямой угол, пользовались веревкой, разделен1. 111 и,1 Г. ’ ранных частей. С гибая ее, получали треугольник, I>•..........чнорого составляли 3, 4 и 5 частей. " I апим программу для нахождения и печати всех Пипмрмшд чисел, не превышающих 20: миопии п и ф а г п р ( o u t p u t ) - , var и , b , с, с х ' . i n t e g e r - , i ■• )■in tor a : = 1 to 20 do for b = a to 20 do begin :
cx:
=
a
*
a
+
b*b\
c: = 1; while (c * c < c x ) and ( c < 2 0 ) do с : = c + 1; 41
if c*c = cx then (* числа Пифагора *) writeln (a, b, c) end end. Тройка Пифагоровых чисел, не имеющих общих делит лей, назы вается основной. Умножив к аж до е из чисел, вх дящих в основную тройку, на какое-нибудь натуральш число, снова получим тройку Пифагоровых чисел, называ мую производной тройкой. Например: 3 4 5 основная тройка Пифагоровых чисел 6
8
10
| производные
9 12 15 ? 15 20 25 J
тройки Пифагоровых чис
v . ( 'оставим программу, используя эти формулы: |1П)({гат пифаг (input, output); var и, v, max-.integer-, Inaction взаимпрост (x, y :in te g e r ):b o o le a n ; (♦являются ли числа взаимно простыми*) ... (см. з а д а ч у 22) Ь< ц1п ( * пифаг * ) read (max); и: = 1; и: = 3; while (u*u-\-v*v) div 2 ^ m ax do begin while (u*u-\-v*v) div 2 ^ m ax do begin if взаимпрост (и, v) then writeln (u * v , (u*u — v*v) div 2; (u*u-\-v*v) div 2); u : = u -\ -2 end; v : = v-\-2; u : = v - j-2 end i-iul. 43
© Составьте программу, которая будет находить все шения уравнения
x 2Ary2 = zn (п — исходное данное) в промежутке [2; 100]. © © Треугольники, у которых длины сторон и площа представляют собой целые числа, называю тся треугольнш ми Герона. Например, таков треугольник, длины стор которого равны 13, 14 и 15, а площадь — 84. Составьте функцию, определяющую, яв л яется ли Tf угольник, длины сторон которого а, Ь, с, треугольник! Герона. © 0 © Треугольников Герона очень много. Составь программу д л я нахождения самых интересных треугольп ков Герона, у которых: а) стороны выражены соседними числами (наприм! длины сторон таких треугольников 3, 4, 5 или 13, 14, 15 б) площадь равна периметру (например, таков прял угольный треугольник, длины сторон которого 6, 8, 10). В обоих случаях ищутся треугольники, длины стор( которых находятся в указанном интервале.
14. Разрезание прямоугольника на квадраты Д ан прямоугольник, длины сторон которого а и Ь пре ставляю т собой натуральные -числа. Составим программ которая будет находить, на сколько квадратов, сторон которых выражены натуральными числами, можно разреза' данный прямоугольник, если от него каж ды й раз отрезае ся ква д р а т максимально большой площади. Например, прямоугольник 7 X 1 2 следовало бы раздели' так, как это показано на рисунке 6. Было бы хорошо, если б ЭВМ напечатала I I I - I I 'I' зультаты таким обр; зом: ДАН ПРЯМОУГОЛ НИК: 7*12 КВАДРАТЫ: 7*7 1 5*5 1
Рис. 6. Разрез прямоугольника 7 X 1 2 на квадраты. 44
2*2
2
1*1
2
ВСЕГО КВАДРАТОЕ
"ИРМИН "■ 1111 ■i() чтобы найти число квадрадлинную сторону) разделить на fhi.it.nitс т . и. ((п IIйннчымшн
111и1111. 1м мп
выглядит так :
f f t f f l Щ§»и>1 ч ч ы {in p ul, o u tp u t);
I иt ,* i, i* ......... . i 11'рои прямоугольника*) '. " .и i ' т.ш ее число к в а д р а т о в * ) i* ми. ....... нинаковых квадратов *) i j flit It И l e i ') in !i , , , 141iti ■ m i ПРЯМОУГОЛЬНИК: , a : 1, * , .t т . i i I1И Щ РА ТЫ : );
Ннин
b: 1);
Ими (.!•(( и b меняются м естам и *)
u
tt**u In *: »; и ! - b; Ь'.анХ fit (I, (♦(! * l) +) k и tllv b\ it — it titttd b; &iih'hi ( b : I !>, * , b : 1, k); ii
n
| It
HHlil i (i ■. 0 );
г.' I ГО К В А Д Р А Т О В :, n ; 4)
i*
> ( i ,,, pi, pi ниш более простую задачу. Составим функцию I ПН н I и-пия числа квадратов, на которые можно раз.,111 прямоугольник. I mi I mu и, функцию можно, пользуясь текстом предыдуИ пршрпммы: объявить параметрами функции исходные in 11,i( (положим, что а ^ Ь ) , а т а к ж е исключить оператор щ и ............ л ко, хорошенько подумав, можно составить нан и, ""И г простую рекурсивную функцию. Вот она: и* Iton iui ( i i , b :in te g e r ):in te g e r ; ill и It b 0 then ice: = 0 else кв :==a div 6 + ке (b, a mod b) til
45
15. Равновеликие прямоугольники Составим программу для нахождения всех прямоуголь ников указанной площади (площадь — исходное данное, выраженное натуральным числом), стороны которых — натуральные числа. Например, если площадь равна 12, то получим три разных прямоугольника: 1X12 2X6 3X4 Будем считать одинаковыми прямоугольники, получаю щиеся один из другого, если поменять ребра местами. Обозначим площадь буквой р, а стороны прямоугольни ка — буквами а и Ь. Приравнивая одну сторону (напри мер, а) к 1, 2, 3, 4 и т. д. до тех пор, пока a ^ b (b получим, разделив площадь на а ) , мы сможем получить все прямо угольники, площади которых равны р. На язы ке П аскаль мы записываем это так: а : = 1; Ь: = р; while a ^ b do begin if a*b = p then печатается прямоугольник со сторонами н и 4
а : — а -р 1; b : -р div а end Поскольку всегда можно образовать по крайней мере один прямоугольник указанной площади, то лучше исполь зовать цикл repeat. Приведем законченную программу: program прямоугольник (input, output ); v ar р, (* у к а за н н а я площадь прямоуголь ника *) а , Ь, (* стороны прямоугольника *) сколько : integer-, (* сколько различных равновеликих *) (* прямоугольников *). begin . read (р); сколько : = 0 ; 46
а : = 1; b : =р\ repeat if a*b = p then (* найден прямоугольник*) begin сколько : = сколько + i ; writeln (a, ’*’, b: 1) end; a : — a-\ - 1; b: — p div a until a > b \ write {сколько, ’ПРЯМОУГОЛЬНИКОВ ПЛОЩАДЬЮ’, p) end. © Исходное данное — натуральное число v. Составьте программу для нахождения всех различных прямоугольных параллелепипедов, объем которых равен v, а ребра выражены натуральными числами. Параллелепипеды, получающиеся один из другого, если поменять ребра местами, считаются одинаковыми.
16. Равновеликие треугольники Рассказы ваю т, что английская королева Виктория, при д я в восторг от книги Льюиса Кэрролла (C a rro ll) «Алиса в стране чудес», однажды приказала принести ей другие произведения этого автора. К ак ж е удивилась королева, когда ей вручили несколько математических трудов! О ка залось, что эту чудесную сказочную книгу под псевдони мом Л. Кэрролл написал профессор математики Оксфордско го университета Чарльз Л а т в и д ж Доджсон (Dodgson, 1832— 1898). В его дневнике мы находим интересную зад ач у, кото рую и попытаемся решить. Ч. Л. Доджсон пишет, что он тщетно трудился, пытаясь найти хотя бы три прямоуголь ных треугольника равной площади, у которых длины сторон были выражены натуральными числами. Составим программу для решения этой задачи (будем искать треугольники возможно меньшей площади). Следовало бы просмотреть прямоугольные треугольники, стороны которых выражены натуральными числами, и у с т а новить, найдутся ли хотя бы три треугольника равной пло щади. Однако таких треугольников может быть много. Поэтому мы будем исследовать площадь треугольника, т. е. проверять, могут ли данную площадь иметь хотя бы три треугольника, стороны которых — натуральные числа. По 47
скольку площадь прямоугольного треугольника равна 1/2 а -b (где а, b — катет ы ), то удобнее исследовать удвоен ную площадь s = a - b . В этом случае значение S будет лю бым натуральным числом: S = 1, 2, 3, ... Итак, переформулированная зад ач а зв уч ал а бы так : у с т а новить, можно ли представить рассматриваемое н атур ал ь ное число в виде произведения двух натуральных чисел по крайней мере тремя различными способами. Кроме того, сле дует еще проверить, может ли к а ж д а я из пар чисел пред ставлять катеты удовлетворяющего условию треугольни ка, т. е. вы раж ен а ли гипотенуза натуральным числом. Н а пример, удвоенную площадь, равную 12, можно представить в виде произведения двух чисел тремя способами: 1 X1 2 2X6 3X4 Однако в треугольниках, у которых а = 1, 6 = 12 и а = 2, Ь = 6, гипотенузы с = У 12+ 1 2 2 и с =т / 2 2 62 не являю тся натуральными числами. Таким образом, число 12 не будет представлять собой удвоенную площадь, удовлетворяющую условию задачи. Подчеркнем, что следует отбросить те треугольники, у которых катеты меняются местами. Алгоритм решения задачи очень похож на алгоритм «Равновеликие прямоугольники» (см. з а д а ч у 15). Приведем схему программы: program carro ll (output)] v a r s, (* пробная двойная площадь треугольника*) а, Ь, (* катеты треугольника *) k : integer; (* число треугольников *) begin s : =0; . mv. -о repeat .,,ц. д ж ' S ’ = S +
1;
-Г
-
|чу; .
k : = 0; :
/-■ --" " А Д 1
Чл.— ,
берутся все возможные пары катетов а и Ь, такие, что: 1) а иведенн^Ю схемУ программы, мы видим, Г т я бы тоиРи н т е о ^ олняется до тех П°Р пока мы не найдем три иk неР ^^еую щ и х нас треугольника (до тех тех поипор, п0‘ ка значение «ьника ^до TnPvmnhHHKH BOOR равно о), д Чт0 будет, если такие П Кэпоолл м о ж е - ? ^ 6 Н6 сУществУ°т? Р аз их не нашел напишем еще д о п о 5 ыть’ и Э ^ М Н6 НайДет? Из осторожности ствии с которым н * следует Проверять т^уТольники СудвоенГ т у Г а Г н Г Г л > * Превышает м а к ^ Г ^ м о е останетсяМтолько^еи/1 ТРИ искомыхпг')еУгольника (^ = 3). т0 ш адь этих тпеугол к * «спечатать. Поскольку мы з н а е м пло щ адь этих р у Ь“НиК0В, мы без труда найлем их повторив б ы л о Т ; анН айЛ дяГИкЧНые Т0ЛЬК° что "роиГ
дТ нн1 ш
Хжно
ШТ у с л о в и ю з а д ^ “ й' " ибудь тРе»г«ьннк, удовлетворлютоинее ( п о и м Ж * " ' за" омнить « " « и его сторон, во “ тороны 6 ” оче“ У!>’ зиая "*ВД Д Ь, заново ВЫЧИСпрограмму:' ТаКУИ
“ Р Р « ™ Ровку, напишем законченную
program carroll (o utf)uty L ^ ° б н а я двойная площадь треугольника*) a b k'in te ge r- (* и теты тРеУгольника , ' . & ’ ,чИсло треугольников*) . . . (см. з а д а чК' ,р9е" ь из " aTW “«b»b,x ч и сел .)
ф и п и ^ У -Ш ^ , ральных чисеД звлечении квадРатНого корня из нату. . . (см. з а д 9Чу] 9) begin (* carroll *) s: = 0; 49
repeat s : = s + 1; k := 0 ; a : = l ; b : = s div a; (* первая п ара катетов a ^ b * ) repeat if (a*b = s) and (sqrootmod (sqr (a) -{-sqr ( b )) = 0). then (* гипотенуза — натуральное число *) k : = k -j- 1; a : = a + 1 (* берется н о в а я * ) b : = s div a (* пара катетов *) until a > b until ( k ^ 3 ) or (s = maxint)', (* печатаются найденные треугольники *) if k ^ 3 then begin a : = 1; b : = s div a; repeat if ( a * 6 = s ) and (,sqrootmod (sqr (a)-{-sqr (b)) = 0) then (* гипотенуза — натуральное число *) writeln {a, b, sqroot (sqr (a) + sqr (ft))); a : = a + 1; b: = s div a until a > b end end. Выполнив эту программу, Э В М напечатала такие три треугольника: 15 112 113 24 70 74 40 42 58 © Измените программу так, чтобы она находила и печата л а не только три треугольника наименьшей площади, но и все прочие треугольники, площадь которых не превышает s (s — исходное данное) и которые удовлетворяют выше указанному условию.
17. Уравнение а 3
Ь3 — с3-{-d3
Вот что рассказы ваю т об индийском м атематике С. Р а мануджане (R a m a n u ja n , 1887— 1920). Как-то раз больного математика навестил его друг-европеец. Гость пожаловался, 50
что прибыл в кэбе с очень скучным номером, и сообщил четырехзначное число. Р ам а н у д ж ан ему возразил: назван ное число очень интересно. Это наименьшее натуральное число, которое можно представить в виде суммы кубов двух натуральных чисел не единственным способом. Составим программу, которая бы нашла это число и на печатала бы д ве пары чисел, сумма кубов которых равна найденному числу. Иными словами, требуется найти наимень шее натуральное решение уравнения a 3- f b3 = c3 jr d 3, причем а ф с и а ф А . При прочтении условия задачи возникает мысль прове рить подряд все числа, пока не будет найдено решение. Одна ко такое простейшее решение нерационально. Ведь каж дое проверяемое число надо будет представлять в виде суммы д вух слагаем ы х и проверять, не яв ляю тся ли эти слагаемые кубами каких-нибудь чисел. Поскольку число N можно р а з бить на д в а слагаем ы х N — 1 способом то, чтобы проверить
N первых чисел, необходимо произвести N
Дей
ствий. Таким образом, число действий пропорционально к ва д р а ту числа, являющ егося решением; поэтому, если реше нием является большое число (а какое число будет реше нием в действительности, мы не знаем, составляя програм му) , д а ж е быстродействующая вычислительная машина будет решать эту з а д а ч у слишком долго. Будем искать лучшее решение, рассмотрев аналогич ную, но более простую задачу. Вместо суммы кубов возьмем сумму квадратов, т. е. будем искать наименьшее число, равное двум суммам, к а ж д а я из которых яв л яется суммой квадратов д ву х натуральных чисел, различных в первом и втором случае. Составим таблицу сумм квадратов для первых одиннад цати натуральных чисел (табл. 3 ). Левую нижнюю часть таблицы мы не заполняли из-за того, что при перемене мест слагаем ы х мы получили бы ту ж е самую сумму. Чтобы сделать разбор задачи (а позднее и составление программы) более простым, упорядочим с л а гаемые в парах таким образом, чтобы первое слагаемое не превосходило второе (а ^ .Ь ). Выберем из таблицы суммы квадратов и ‘запишем их в порядке возрастания: . 2, 5, 8, 10, 13, 17, 18, 20, 25, 26, 29, 3 2 ,3 4 , 3 7 , 4 0 , 4 1 , 4 5 , 5 0 , 5 0 , 5 2 , , . . 51
Наименьшее число, которое повторяется в этой последова тельности д в а ж д ы , и будет искомым решением. Таким числом в данной последовательности яв л яется 50. Из таблицы 3 н а ходим, что 12 -+- 72= 50 и 52 + 52 = 50. Т аб л и ц а 3
Таблица чисел а 2 + 62
b а
1 2 3
1 2 5 2 8 3 4 5 6 7 8 9 10 11
4
5
10 17 26 13 20 29 18 25 34 32 41 50
6
7
8
9
10
11
37 40 45 52 61 72
50 53 58 65 74 85 98
65 68 73 80 89 100 113 + 128 +
82 85 90 97 106* 117 + 130 145 162
101 104 109 + 116 + 125 136 149 164 181 200
122 + 125 + 130 137 146 157 170 185 202 221 242
Таким образом, д л я того чтобы решить зад ачу, надо уметь найти члены упомянутой последовательности, следую щие по порядку др уг за другом. Первые д ва равных члена этой последовательности и будут представлять собой искомое решение. Предположим, что у нас есть процедура, посредством ко торой мы по предъявлении любого члена последовательности могли бы получить другой, непосредственно следующий за ним член этой последовательности. Тогда д ля решения з а д а чи мы бы могли составить такую схему программы: program к ва д р а ты (output); v a r стар, Hoe'.integer; procedure новое (s : integer; v a r n ’. integer); находится новый член ряда — п, следующий з а старым членом ряда — s 52
begin нов: = 2 ; repeat с т а р : — нов; новое (стар, нов) until стар = нов; write (стар) end. Эта программа печатает не вполне исчерпывающий ре зультат. Следовало бы напечатать не только сумм у к в а д р а тов, но и сами квадр аты , а еще лучше — числа, возводимые в квадрат. Эти числа мы могли бы найти из суммы квадратов, но их можно получить и проще: требуется, чтобы процедура новое производила действия не с суммами, а с возводимыми в квадр ат числами. Внесем изменения в программу и в заголовок процедуры новое ; д ля вычисления суммы квадратов двух чисел введем функцию /: program квад р аты (output)-, v ar а, b, (* первая, стар ая пара *) с, d :in te g e r ; (* вторая, новая п а р а * ) function f (a, b :in te g e r ):in te g e r ; begin /: = а * а + b*b end; procedure новое (a, b'.integer; v a r c, d :in te g e r); (* находим новую пару чисел (с, d ):* ) (* c ^ d , f (a, b ) ^ f ( c , m axint *) (* и нет другой пары (х, у): .*)' {*1 (ft, b ) ^ f (x, y ) < f ( c , d) *) begin (* к вадр аты *) с : = 1; d : = 1; repeat a ' . —'c; b : = d ; новое (a, b, c, d) until f (a, b) = f (с , d); writeln (a, b , c, d, f (a, b)) end. Остается составить процедуру новое, которая будет нахо дить новую пару чисел. Проще всего было бы просмотреть все пары чисел и из них выбрать ту, которая удовлетворяет предъявляемым тре бованиям. Поскольку таких пар бесконечно много, то попы 53
таем ся ограничить область, в которой может находиться пара, удовлетворяю щ ая условию. Заглянем в таблицу 3. Допустим, что ста р ая пара: а = 5, Ь = 9. Сумма их к в а д р а тов /(5, 9 ) = 106 в таблице помечена звездочкой. В каж дой строке таблицы ( а ^ Ь ) можно выделить только по одному кандидату на новую пару — сумму квадратов. Они помечены знаком «плюс». Числа, находящиеся слева от 106, не под ходят из-за того, что они меньше 106, а справа — больше, чем обозначенная сумма. В строках, номера которых больше b (в данном случае больше 9 ), т а к ж е не стоит искать нового кандидата, поскольку он будет больше, нежели уж е подобранные. Обнаружив самого подходящего кандидата, т. е. сумм у квадратов двух чисел, получим новую пару чисел. Из этого рассуж дения вытекает, что новая пара (с, d) долж на удовлетворять одному из следующих условий: 1) 1 и d>b; 2) a-\ -\ ^ .a^ L d ^ Z b . Приведем схему процедуры, ищущей новую пару: procedure новое (a, b'.integer; v ar с, d '.integer); v a r fab, (*f (a, b)*) fed, (*f (c, cl)*) x, у '.integer; (* временная пара (с, d)*) begin fed: = m axint; fab : = f (a, b); ищется пара (с, d): 1 ^ c ^ a , d i> b , f a b ^ f ( c , d ) ^ . maxint
ищется пара (с, d): a -\-1 f a b ^ f ( c , d ) ^ . maxint end Запишем на язы ке П аскаль действия, заключенные в прямоугольники: (* ищется пара (с, d): \ ^ с ^ a, d > b *) ( * f a b ^ .f (с, d) f ( x , у) do y : = y + 1; if f (x, y ) < f e d then begin fed: = f (x, y); c: = x ; d := y end end; (* ищется пара (с, d ) :a - \ - 1 s ^ c ^ d ^ f t * ) (*fab^Lf (с, d ) ^ maxint *) for x : = a + 1 to b do begin y: =x; w hile (f a b > f ( x , y)) and (y < b ) do У- = y + 1 ; if f (x, у) < fed then begin fed : — f (x, y)\ c: =x\ d := y end end Остается только соединить все части, и мы получим законченную программу, решающую уравнение а 2-\-Ь2 = = c2-{-d2. Чтобы решить зад ачу, сформулированную в н а чале, т. е. найти наименьшее число, сл уж ащ ее решением уравнения a 3 + b3= c3 + d3, достаточно только изменить функцию f — вместо суммы квадратов вычислять сумму кубов: f : = а*а*а-\- b*b*b. Обратим внимание на то, что аналогичным образом мы могли бы получить и решения уравнений a 4 + 64 = c4 + d 4, а ъ-1г Ь5 = с5 + d? и т. д. Законченная программа выглядит так : p ro gram кубы ( output ); v ar а, b (* первая пара *) с, d '.integer: (* вторая п а р а * ) function / (a, b :in te g e r ):in te g e r ; (* сумма кубов чисел а и Ь*) begin f : = а * а * а -\ - b*b*b end; 55
procedure новое (a, b :in te g e r ; v a r с, d ’. integer); v a r fab, (*f (a, b)*) fed, (*f (c, d)*) x, у ’. in teg er; (^промежуточные значения с и d*) begin (* новое *) f e d : = maxint; f a b : = f (a, b); (* ищется пара (с, d):\ d > b ,* ) (*fab ^ f (с, d) ^ maxint *) for x : = I to a do begin У ■= 6 + 1; while f a b > f ( x , y) do У- = y + 1; if f (x, y ) < f c d then begin fcd'.f (x, y); c : = x; d := y end end; (* ищется пара (с, d ) : a + 1 - ( * f a b ^ f (c, d ) ^ . maxint *) for x : = a + 1 to b do begin y: =x; while (f a b > f ( x , y)) and ( y < b ) do y : = y + 1; if f (x, y ) c f c d then begin fed : = f (x, y); c: = x ; d:=y end end end; (* новое *) begin (* кубы *) с : = 1; d : = 1; repeat a : = c ; b: = d ; новое (a, b, c, d) until f (a, b) = f (c, d); writeln (a, b, c, d, f (a, b)) end. 56
Выполнив эту программу, ЭВМ н ап ечатала \
1
12
9
10
1729
Значит, число, которым восхитился С. Р ам ануд ж ан, было 1729, так как 1729 = 103 + 93= 123 + 13. © Эту з ад ач у можно решить подобно тому, как решались зад ач и о равновеликих прямоугольниках или треугольниках (см. задачи 15 и 16). Воспользовавшись идеями упомяну тых задач, составьте другой вариант программы кубы. Какой из вариантов эффективнее?
18. Задача Антанаса Баранаускаса Автор поэмы «Аникщайский бор» А. Б ара н а уска с (1835— 1902) был не только поэтом, но и математиком-любителем. Особенно интересовался он теорией чисел: он исследовал свойства простых чисел. Приведем одну из самых любимых зад ач его детства, над которой, как он сам позднее писал, бился недели две, пока не нашел решения: сколько можно купить быков, коров и телят, платя за быка 10 р., за корову — 5 р., а за теленка — полтинник, если на 100 рублей надо купить 100 голов скота? Условие этой задачи можно описать при помощи систе мы уравнений: / 10 6 + 5 /с+ 0,5 г== 100, { б + /с + г = 100, где б - — число быков; к — число коров; т — число телят. Обе части первого уравнения умножим на 2 (т. е. деньги будем считать в п олтин н иках):
( 20 6 + 1 0 к + г = 2 0 0 , { б + /с + т = 100. Мы имеем систему двух уравнений с тремя неизвестными. В общем случае т а к а я система уравнений имеет бесконеч ное число решений. Однако в данном случае число решений конечно. Во-первых, решениями могут быть только неотрица тельные числа. Во-вторых, возможные значения .неизвестных ограничены и сверху: за 100 р. нельзя купить более 10 быков (д аж е если покупать только одних б ы к о в ), более 20 коров и более 200 телят. 57
Таким образом, 0 0 ) a n d ( у > 0) *) function нод (х, у '.integer)'.integer; (* наибольший общий делитель *) . . . (см. зад ач у 21) begin (* нок *) нок: = х div нод (х, у) *у end С начала осущ ествляется деление, а затем — умножение, чтобы уменьшить вероятность переполнения. Применяя приведенную функцию нок, составим програм му для печати таблицы наибольших общих кратных всех идущих по порядку натуральных чисел от 1 до л (п — входное д а н н о е ): p ro g ra m кратное (input, output); v a r k, кратн, n: integer; function нок (x, у '.integer): integer; (* наименьшее общее кратное *) function нод (x, у '.integer)'.integer; (* наименьший общий делитель *) begin 3 В. А. Дагене
65
if x = 0 then н о д : = у else нод: = н о д (у mod х, х) end; (* нод *) begin нок: = х div нод (х, у) * у end; (& нок *) begin (* кратное *)
read (п); кратн: = 1; for k: = \ to я do begin
кратн: =HOK(k, кратн); writeln (k, кратн) end end. © Исходные данные — последовательность натуральных чисел. В конце последовательности — нуль. Составьте программу для нахождения наименьшего общего кратного всех членов ряда. Кратным р яд а считается наименьшее натуральное число, которое делится без остатка на все члены ряда. © © Составьте функцию, значением которой было бы наименьшее общее кратное всех натуральных чисел от 1 до п включительно (п — параметр функции). © © © Составьте программу д ля нахождения макси мального вмещ аю щ егося в ЭВМ числа, которое без остатка делится на как можно большее количество следующих друг за другом натуральных чисел.
24. Бильярд Запечено, что математики лю бят играть на бильярде. Возможно, это потому, что траекторию ш ара, отскакиваю щ е го от краев бильярдного стола, можно вычислить д о ста точно точно. Приведем зад ачу, тесно связанную с б ильяр дом. Предположим, что дан прямоугольник, длины сторон ко торого в ы раж аю тся натуральными числами а и Ь. Из одной вершины прямоугольника проводится линия, которая состав ляет угол в 45° со сторонами прямоугольника. Д ойдя до стороны прямоугольника, линия, не пересекая ее, и зл ам ы вается под углом в 90°. Д о й д я до другой стороны, она снова изламы вается под углом в 90° и т. д. до тех пор, пока она не дойдет до какой-либо из вершин прямоуголь ника. (Убедитесь, что это произойдет во всех случаях, когда 66
b a-(b-a-a)
Рис. 8. Схема установления числа отрезков траекторий би льярдного мяча, вы читая короткую сторону.
длины сторон прямоугольника выражены натуральными числами.) П олучаю щ аяся л о м ан ая напоминает траекторию б и л ь ярдного ш ар а на прямоугольном столе. Составим функцию бильярд , которая будет устанавли вать, из скольких отрезков состоит л о м ан ая (концы отрезков должны находиться на сторонах прямоугольника). Можно составить эту функцию несколькими способами. Каким об разом производятся вычисления, показано на рисунке 8. (Чтобы не заг р ом ож дать чертеж, мы не провели ломаную до конца.) Длинную сторону заданного прямоугольника обозначим max, короткую — min. Тогда функция бильярд будет выгля деть следующим образом: function бильярд (a, b '.integer):integer-, v a r max, (* длинная сторона прямоугольника *) min, (* короткая сторона прямоугольника *) п, (* число отрезков в ломаной *)
d : integer-, begin if a ^ b then begin m a x : = a \ m i n : = b end else begin m a x : = b ; m i n : = a end; n :=0;
d: = m in : whiie d -Ф 0 do begin
d: = m a x — d; n: = = « + 1; whiie d ^ m i n do 67
begin
d: = d — min; n: = n + 1 end; if
then begin
d \ = m i n — d; n := n+1 end end;
б ильярд: = n end
Рис. 9. Пример траектории б и л ь я р д ного мяча.
Значительно лучший вариант функции можно составить, если использовать наименьшее общее кратное длин сторон — нок (см. зад ач у 23). Разберем пример, приведенный на рисунке 9. Развернем этот чертеж таким образом, чтобы л ом ан ая шла только
Тогда лом аная, вместо того чтобы о т р аж ат ь ся вт корот кой стороны, пересекает ее. Получаю тся отрезки одинаковой длины. Их число равно (убедитесь!): нок (min, max) div min Чтобы получить упоминаемое в зад ач е число отрезков, остается прибавить внутренние короткие отрезки, которых всего имеется нок (min, max) div m a x — 1 Значит, траектория бильярдного ш ара состоит из
нок (min, max) div min + нок (min, max) div m a x — 1 отрезков. Поскольку, поменяв местами длины сторон min и max, мы просто поменяем местами первые два слагаемых (при перемене мест слагаемых сумма не меняется), то приведен68
ную выше формулу можно упростить, если не обращ ать внимания на то, к а к ая из сторон длиннее:
нок (a, b) div а-{-нок (а, b) div b — 1 Таким образом, вся функция выглядит так: function бильярд (a, b :integer):integer; function нок (х , y:integer):integer; (* наименьшее общее кратное *) . . . (см. зад ач у 23) begin (* бильярд *) бильярд: = нок (a, b) div а-\-нок (a, b) div b — 1 end
25. Делители Самый простой способ найти все делители числа п — это проверить по очереди делимость п на каж дое из чисел 1, 2, 3, ..., п. Легко убедиться, что в интервале [п div 2 + 1 ; п — 1] делителей числа п нет. Д л я нахождения делителей числа п составим такую про цедуру: procedure делители (п:integer); v a r d'.integer; begin for d: = 1 to n div 2 do if n mod d = 0 then
write (d); write (n) end Например, когда я = 30. Выполнив эту процедуру, ЭВМ напечатает следующие результаты — делители числа п: Г
2
3
5
6
10
15
30
Обратим внимание на то, что, д ля того чтобы найти делитель числа п, достаточно обнаруж ить делители, не пре вышающие л[п. Все остальные делители получаются в р е зультате деления числа п на найденные делители. Например, если и = 30, то достаточно найти делители 1, 2, 3, 5 (натуральный квадратны й корень из 30 равен имен но 5), а все прочие делители получаем следующим о б разом: 69
30 30 30 30
div div div div
1=30; 2 = 15; 3=10; 5 = 6.
Составим процедуру, которая будет находить только что рассмотренным способом делители любого данного числа. При составлении можем использовать либо цикл for (в этом случае придется д ля извлечения квадратного корня из нату ральных чисел использовать функцию sqroot — см. зад ач у 9), либо цикл while. Выберем последнюю возможность: procedure делители ( п '.integer); v a r d: integer; begin d: = 1; while d*d ■г;\V •'■ г 1 ■- ■ЦБ : procedure дел (п, d: integer)-, v a r dd: integer-, begin while (n m od d=/= 0) a n d (d*d « end. Выполнив эту программу, ЭВМ наш ла в первой тысяче целых 35 пар близнецов. Чтобы находить близнецов в боль шем интервале, следовало бы увеличить константу п. Однако, увеличивая ее, можно быстро превысить пределы в о зм о ж ностей ЭВМ: массив числа не уместится в памяти. З н ач и тельно больший числовой интервал можно обследовать при помощи процедуры рил, использующей множества (см. з а д а - чу 29). © Составьте функцию, которая будет проверять, яв л яю т ся ли числа находящиеся по обе стороны от заданного четного числа, близнецами.
31. Скатерть Улама О днаж ды математик Станислав Улам ( Ulam, род. 1909) слушал какое-то очень длинное и скучное сообщение. Ж е л а я как-то отвлечься, он разделил лист бумаги на клетки и уже собирался зан яться составлением шахматных этюдов, но передумал и, написав в центре 1, начал писать по спирали против часовой стрелки все натуральные числа подряд, обводя простые числа в круж ок (рис. 12). Скоро, к его удивлению, простые числа выстроились в довольно-таки закономерном порядке, об разуя интересный узор. Этот узор позже стал объектом исследования и получил название скатерть Улама. Составим программу, позволяющую нарисовать скатерть Улама размером 100Х 100 клеток. Не будем печатать числа, а только поставим звездочку (*) на месте простых чисел, Чтобы решить эту задачу, прежде всего надо найти все простые числа в интервале [1; 10 000]. Д л я этой цели 83
используем процедуру решето, составленную по методу Эратосфенова решета (см. зад ач у 29). Кроме того, надо о б р а зовать спираль, состоящую из элементов одномерного м а с сива. Подчеркнем, что д ля этой задачи можно составить р а з личные программы. Рассмотрим некоторые способы. 1. Геометрический
Клетки скатерти заполняю тся в том ж е порядке (т. е. элементы решета развор ач и ваю тся начиная от центра с к а терти), как рисовал их Улам. В этом случае мы моделируем в программе геометрические действия. 2. Вычисление координат скатерти
Составляется процедура, в которой входное данное — индекс элемента решета, а результат — координаты соответ ствующей этому индексу клетки скатерти. Если мы будем просматривать элементы решета по порядку, начиная с самого малого индекса, то будем заполнять клетки скатерти в том же самом порядке, что и при первом способе. 3. Вычисление индекса (координаты ) элем ента реш ета
С оставляется функция (или процедура), в которой исход ные данные — координаты клетки скатерти, а результат — индекс элемента решета, соответствующего данной клетке. При наличии такой функции можно заполнять клетки ск а терти, просматривая эти клетки в каком угодно порядке. П росматривая клетки слева направо и сверху вниз, т. е. в том порядке, в каком символы печатаются, можно вовсе не запоминать клетки скатерти, а просто печатать их. Таким
© 96 95 94 93 92 91 65 64 63 62 © 60 © 58 57 90 66 © 36 35 34 33 32 ® 56 © © 38 © 16 15 14 © 30 55 88 66 39 18 © 4 © 12 © 54 87 69 40 ® 6 1 ©© 28 © 86 70 © 20 0 8 9 10 27 52 85 ® 42 21 22 © 24 25 26 51 84 72 © 44 45 46 © 48 49 50 ® © 14 15 16 77 18 ® 80 81 82 100 99 98
84
Рис. 12. Спираль Улама.
У
wo
— -- ■*--
51 50
1
0
1
. . .
50 51
100
X
Рис. 13. Схема вращения спирали Улама.
образом, если используется этот способ, в памяти нужно меньше места, нежели при способах, описанных выше. Составим программу для геометрического способа о б р а зования скатерти. Поскольку у нас есть процедура решето, которая нахо дит все простые числа в интервале [2; п\ и сохраняет результаты в массиве a r r a y [2..п] of boolean (напомним, что значение true присваивается, когда индекс элемента явлется простым числом, a false — в противном случае), то наиболее сл ож н ая часть решения — упомянутый одно мерный массив (точнее, его индексы) скрутить в виде спира ли. Как мы будем скручивать его, изображ ено на рисунке 13. Рассмотрим его. Скатерть разделена на квадраты, вписанные один в другой. В каж дом квадрате все четыре линии (стрел ки) имеют одинаковую длину. Д лина стрелки зависит от квадрата, которому она принадлежит. Эти длины равны соответственно 1, 3, 5, 7, ..., 99 клеткам. Составим схему программы: p ro g r a m скатерть (output)-, 85
const m = 100; (* скатерть m*m клеток*) (* m — четное число *) « = 10000; (* m*m = n * ) type числа = a r r a y [2. .n] of boolean ; (* реш ето*) v a r скат: a r r a y [1 ..m, 1,.m\ of boolean ; (* скатерть *) p : числа; x , у, (* координаты клеток скатерти *) d:integer; (* на сколько клеток идти*) (* в одном направлении *) procedure решето (var s: числа); (* нахождение простых чисел *) (* методом Эратосфенова решета *) ... (см. зад ач у 29) begin (* скатерть *)
решето(р); х : = п г div 2; у: = m div 2; (* начало в р ащ е н и я* ) d: = 1; (* длина линии (стрелки) *) repeat заполняется л ев ая линия (вертикальная стрелка)
заполняется ниж няя линия (горизонтальная стрелка)
заполняется п р авая линия (вертикальная стрелка)
заполняется верхняя линия (горизонтальная стрелка)
переход в больший квад р ат
d: = d + 2 until d = m-\- 1; печать скатерти end. 86
Первые четыре (центральные) клетки удобнее заполнить отдельно. Д а л ь ш е клетки заполняются в соответствии со специальным законом. Р азо б рав схему вращения, приведен ную на рисунке 13, мы можем описать заполнение клеток скатерти следующим образом:
вниз вправо вверх влево Следовательно, цикл repeat в первых четырех прямоуголь никах можно было бы детализировать так: for г : = 1 to d do ш аг вниз for z : = 1 to d do шаг вправо for
2
: = 1 to d do
шаг вверх for z : = 1 to d do ш аг влево В прямоугольнике ш аг вниз нам нужно записать т ак ж е действия: У- = у — 1;
k : = k - \ - \ ; (* берется следующий элемент решета *) скат \х, у \ : = р [k] В прямоугольнике ш аг вправо выполняются такие действия: х: = х +
1; 87
f t : = f t + l ; (* берется следующий элемент решета *)
скат [х, у}: — p[k] Аналогичные действия мы долж ны были бы выполнить и с другими прямоугольниками: ш аг вверх и ш аг влево. Поскольку действия во всех четырех прямоугольниках очень похожи, мы можем все их соединить в один общий цикл. Самое трудное место в процессе заполнения клеток — это переход из одного (малого) кв ад р ата в другой (боль ший). Это можно запрограм мировать различным образом. В приведенном здесь варианте программы движение начи нается из левой верхней (угловой) клетки квадрата: с н а чала мы передвигаемся на одну клетку (делается один ш а г ) , а затем заполняем клетку. Переход из одного квад рата в другой записы вается при помощи следующих операторов присваивания:
х: = * — 1; У - = У + 1. Подчеркнем еще, что перейти в правый кв ад р ат удоб нее всего в цикле rep eat — еще до всякого вращения. Поэто му наш а программа несколько отличается от приведенной выше схемы. Приведем составленную программу: p ro g r a m скатерть (output)-, const m = i 0 0 ; (* скатерть m*m клеток *) (* m — четное число *) п = 10000; (* m*m = п *) type числа = a r r a y [2..re] of boolean; (* решето *) v a r скат: a r r a y \\..m, L.m] of boolean; (* скатерть *).
идти: (вниз, вправо, вверх, влево); р: числа; х, у, (* координаты клеток скатерти *) d, (* на сколько клеток идти *) ft,
(* в одном направлении *) (*индекс элемента, взятого из реш ета *)
z'.integer; procedure решето (var s : числа); (* нахождение простых чисел *) (* методом Эратосфенова решета *) ... (см. за д а ч у 29) begin (* скатерть *)
решето (р); x : = m div 2; у : = т div 2; (* н ачало в р ащ е н и я* ) ^ за п о л н ен и е четырех серединных клеток скатерти *) скат[х, y ] := fa ls e ; (*1*) 88
\
скат [х+ 1, у]: = р [2]; (*2*) скат х + 1, у + 11: = Р [3]; (*3*) скат[х, y + l j : = p [ 4 j ; (*4*)
У':’— 1; к =4; у \= У + 1 ; repeat
х : ~ = х — 1; (* переход в больший к в а д р а т * ) у : ~ ' у - (-1; (* в левую верхнюю клетку*) d \ ~=d -j- 2; for идти: = вниз to влево do for z: = 1 to d do begin (* идти сн ачала *) case идти of в н и з : у : = у — 1;
вправо :х: = х - \ - 1; вверх: у: = у - \ - 1; влево :х: = х — 1 end;
k: = k + !; (* а затем заполняется клетка *) скат [х, у}: = р \k\ end; until d = m — 1; (* печать скатерти *) for у: = m do w nto 1 do begin for x : = 1 to m do if скат [x, у | then write ( * ) else write (’ ’);
writeln
end end. Вот и вся программа. Обратим внимание на то, что при некотором изменении программы можно было бы скрутить в виде спирали любой одномерный массив независимо от того, что в нем х р а нится. © Составьте процедуру для вычисления координат с к а терти (см. второй способ). © © Составьте функцию для вычисления индекса (коор динат) решета (см. третий способ).
89
32. Совершенные числа Совершенным числом назы вается число, равное сумме всех своих делителей, м еньш и х,чем оно само.. Например: 2 8 = 1 + 2 + 4 + 7+14. Древним грекам были известны только четыре первых совершенных числа. Выдающийся греческий философ и математик Никомах из Герасы (I в.) писал: «Совершенные числа прекрасны. Однако известно, что прекрасные вещи редки, негодных ж е всюду полно». Совершенные числа чрез вычайно высоко ценились. Недаром в Библии сказано, что мир был сотворен за 6 дней: ведь это первое совершенное число. Д а ж е в XII в. церковь у т в ер ж д ал а, что для спасения души достаточно найти пятое число. Это число'было найдено только в XV в. Добавим, что совершенные числа еще не полностью ис следованы: так, неизвестно, имеется ли конечное число со вершенных чисел или их число бесконечно, до сих пор не найдено ни одно нечетное совершенное число (и не доказано, что таких чисел не су щ еств ует). Составим функцию, которая будет проверять, является ли заданное число совершенным: function совершенное (х :integer): boolean; v a r i, cyMMa:integer; begin сумма : = 1; for i: = 2 to x div 2 do if x mod i = 0 then сумм а: = сумма + i; совершенное : = х = сумма end Чтобы найти совершенные числа в некотором указанном интервале, следовало бы применить функцию совершенное к каж д ом у числу в этом интервале. Если интервал большой, поиск д а ж е при помощи Э В М ок а зал ся бы долгим. Поэтому мы хотим напомнить тот путь, которым мы шли при изучении простых чисел. Д л я того чтобы проверить, является ли конкретное число простым, мы составили логическую функ цию (см. зад ач у 26). Д л я поиска же простых чисел на промежутке [2; п\ мы прибегли к значительно лучшему способу, имеющему в качестве основания Эратосфеново ре шето. Оказы вается, идею Эрагосфенова решета можно при менить и для данной задачи. Только вместо логического массива (см. зад ач у 29) нужно взять массив чисел, а в 90
качестве его элементов — суммы делителей. Итак, мы соста вили такую программу: program совершенные (output ); c o n st « = 10000; (* верхняя граница интер вал а* ) var дел: array [2..«] of integer ; (* сумма делителей *)
ft, р :integer\ begin for ft: = 2 to « do
дел [ft]: = 1; (* все числа делятся на 1*) (* сумма делителей *) for ft: = 2 to « div 2 do begin
p: = f t + ft; w hile p = begin
do
C «
дел
[p]:
p: = p
+
=дел
[p] +
ft;
ft
end end;
(* совершенные числа *) for k : = 2 to n do if дел [ft] = ft then (* совершенное*)
writeln(k) end.
Выполнив эту программу, ЭВМ напечатала числа:
6 28 496 8128 © С совершенными числами тесно связаны числа М е р сенна (см. зад ач у 29). Ещ е Евклид д оказал , что каждое число, которое можно представить в виде произведения 2Р'~1(2Р— 1), является совершенным числом при условии, что 2Р— 1 — простое число. Л. Эйлер (Euler, 1707— 1783) доказал, что по формуле Евклида можно найти все четные совершенные числа. (К ак выглядят нечетные совершенные числа и существуют ли они вообще, неизвестно до сих пор.) Таким образом, если у нас есть простые числа Мерсенна, нетрудно находить и совершенные числа. Составьте программу, которая найдет все совершенные числа Мерсенна, помещ ающиеся в ЭВМ. Быть может, среди них окаж ется и пятое совершенное число?
33. Дружественны е числа Есть легенда, которая гласит, что, когда П и ф агора спро сили, что такое д р уж б а, он ответил: «220 и 284». Так возник термин «дружественные числа». Дружественными числами являются д ва натуральных числа, таких, что к а ж д о е из них равно сумме всех натуральных делителей другого, исключая само это другое число. Действительно, 220 и 284 являются дружественными числами, поскольку сумма делителей чис л а 220 — это 1 1-2 + 4 + 5 + 1 0 + 1 1 + 2 0 + 22 + 44 + 5 5 + 1 1 0 = 284, а сумма делителей числа 284 — это | + 2 +
4 + 71 +
142** 220.
Л1 и-ы 1п т т . н га кос определение дружественных чисел: . \мм i И'сч I!• ци|с.мсП одного и другого такого числа равна $ ( VММ нОиик чн гt*Л. | | . ( ир|«|иж(41ии иском 220 и 2Н4 были единственной ................ .. пар..и т 11\ и,ссIиснпы,ч чисел. Только и середи не V,\ „ (■|мт. | )мvt чн, та и. I 000 000, нашли 42 пары друI I I и. Ii mi ч 'и и i I 1I;. j i Iи н о м у в средние века полагали, *1 1. 1 I I пн I m u I ними числами укрепляют любовь. Чи ги 1ем искан, и указанном интервале дружественIII h чн, 1.1 гем /ке способом, что и совершенные числа. Мы ta I с и......лI,.чуем некоторую часть программы совершенные, .1 именно ту, и которой отыскивается сумма делителей. Законченная программа выглядит следующим образом: pro g ra m дружественные (output ); const п = 10000; (* верхняя граница и нтер вал а* ) v a r д е л : a r r a y [2..п) of integer-, (* сумма делителей*) i, /, k, р: integer-, begin for k: = 2 to n do (* все числа делятся *) дел [k]: = 1 + / е ; (* на 1 и самих себя *) (* сумма делителей *) for k : = 2 to п div 2 do begin
p: = k - \ - k ; while begin
do
дел[р}: = д е л [p\-\-k; p:=p + k end end; (* дружественные числа *) for i : = 2 to n — 1 do 92
for /': = / + 1 to n do if (дел [ / ] = / + /) a n d (дел [ / ] = / + /) then (* пара дружественных чисел *) writeln (г, /) end. Выполнив эту программу, Э В М н аш ла пять пар д р у жественных чисел: 220 1184 2620 5020 6232
284 1210 2924 5664 6368
© Составьте программу, которая н аш л а бы в интервале [1; 1000] число, имеющее больше всего делителей.
34. Цифры Взглянув на число, мы сразу можем сказать, из каких цифр оно состоит. Так что для человека нетрудно опреде лить, из каких цифр состоит число. Но для ЭВМ сделать это непросто. Мы пользуемся десятичной системой счисле ния, а в машинной памяти хранятся числа в иной записи, чащ е всего в двоичной системе. (Относительно систем счисле ния см. з ад ач у 43.) Нет прямого соответствия между привыч ными нам десятичными цифрами и цифрами прочих систем счисления. К тому же как в основах математики, так и в теории программирования число рассматривается как одно неделимое значение. А тот факт, что число состоит из цифр, обусловлен только принятым у нас способом записи чисел. С разу определить, из каких цифр состоит число, мы не можем ни пользуясь языком П аскаль, ни при помощи какихлибо других языков программирования. Во всех язы ках программирования с числами можно производить определен ные арифметические операции. С их помощью придется вы разить и действия над цифрами. Установить, из каких цифр состоит число, можно, исполь зуя операцию деления. Например, если известно, что число х является трехзначным, то его цифры а, b и с мы сможем найти следующим образом:
а: = х div 100; b : ==x div 10 mod 10; с: = х mod 10. 93
Составить число из цифр мы можем при помощи операций умножения и сложения. Например:
х : = 100*а + 10 *Ь + с. Опишем некоторые универсальные функции, имеющие дело с цифрами. Будем рассматривать только натуральные числа. Поэтому параметр п всех трех приведенных ниже функций — натуральное число. 1. Сколько цифр в данном числе function числоцф (п: integer): integer; (* число цифр *) v a r k: integer; begin k:=0;
repeat
k:=k+U n: = n div 10 until n = 0; числоцф: = k
>J
end 2. Нахождение суммы цифр числа function суммацф ( п : integer): integer; (* сумма цифр *) v a r s: integer; begin s : = 0;
repeat s : = s + n mod 10; n : = n div 10 until n = 0;
суммацф: — s end 3. Запись числа в обратном порядке Эта функция записы вает заданное число в обратном порядке (справа налево). Например, число 50 410 будет з а писано как 1405 (незначимые нули вначале опускаются). function наоборот (п:integer):integer; (* то ж е число, записанное в обратном порядке *) v a r a:integer; begin а : = 0; repeat а : = а * 1 0 + га mod 10;
n: = n div 10 until п = 0; наоборот: — а end
Все три приведенные функции осущ ествляют простые действия, с которыми мы легко справляемся и без помощи ЭВМ. Однако они потребуются при составлении программ для многих интересных зад ач с цифрами. © Числа, одинаково читающиеся и слева направо, и справа налево, назы ваю тся палиндромами. Например, чис л а 42 324 или 1331 — палиндромы. Составьте функцию, которая будет проверять, является ли заданное число п а линдромом. © © Числа, запись которых состоит из двух одина ковых последовательностей цифр, назы ваю тся симметрич ными. Например, 357 357 или 17 421 742 — симметричные числа. Составьте функцию, которая будет проверять, яв л яе т ся ли заданное число симметричным. 0 © 0 Если мы сложим все цифры какого-либо числа затем — все цифры найденной суммы и будем повторять это много раз, мы наконец получим однозначное число (цифру), называемое цифровым корнем данного числа. Н а пример, цифровой корень числа 561 равен 3 (5 + 6 + 1 = -12; 1 + 2 = 3). Составьте функцию для нахождения числового корня на турального числа.
35. Автоморфные числа Автоморфным называется такое число, которое равно последним цифрам своего квадрата. Например: 52 = 25; 252= 625. Д л я нахождения всех автоморфных чисел в интерва ле [пг; п] составим такую программу: p ro g ra m автоморф (input, output)-, v a r m, n, (* заданный интервал *) x, (* пробное число *) d: integer'-, (* 10, 100, 1000, begin
read (m, n); d: = 10; for x: = m to n do 95
begin while d ^ x do d : = d * 10; if x*x mod d = x then
writeln (x, x*x) end? end. Выполнив приведенную программу, ЭВ М н аш ла в интер вале [1; 1000] следующие автоморфные числа (в первом столбце напечатаны автоморфные числа, во втором — их к в а д р а т ы ); 1 5 6 25 76 376 625
1 25 36 625 5776 141 376 390 625
© Приведенная программа проверяет подряд все числа в интервале [т\ п\. Однако некоторые числа не надо д аж е проверять: итак ясно, что они не являю тся автоморфными. Это зависит от последней цифры. Чтобы число было автоморфным, оно долж но оканчиваться либо на 1, либо на 5, либо на 6. Убедитесь в этом. Поэтому программу можно усовершенствовать — уменьшить число переборов. Усовер шенствуйте составленную программу. . © © Аналогичным образом мы могли бы определить «кубические» автоморфные числа. Они равны последним цифрам своих кубов. Например: 63== 216; 513 = 132 651; 763 = 438 976. Составьте программу для нахождения «кубических» автоморфных чисел в интервале [ т ; п].
36. Нумерация книжных страниц В книге п страниц ( п — входное данное). Составим функцию, которая буДет находить, сколько цифр понадобится д л я ■того, чтобы занумеровать все страницы этой книги. Сколько нужно цифр для всех й-значных ( k = l , 2, 3, ...) чисел, мы можем выяснить, р ассуж д ая таким образом: для однозначных чисел требуется 9 X 1 цифр 96
9 0 X 2 цифр 9 0 0 X 3 цифр
для двузначных для трехзначных
для /г-значных чисел требуется 900 ... 00Х& цифр k — \ нулей
ИЛИ
(999 ... 9 — i ООО ... 00 + 1) X k цифр к цифр
к — 1 нулей
Иначе говоря, следует вычислить значение выражения
(наибольшее — наименьшее + 1) X k , где k обозначает число цифр ( k = \ , 2, 3, ...), а перемен ные наименьшее и наибольшее — соответственно наименьшее и наибольшее k -значное число. Д л я того чтобы установить, сколько цифр в числе п, применим функцию числоцф, опи санную в зад ач е 34: function страницы (п: integer): integer; (* сколько необходимо цифр, *) (# чтобы пронумеровать книгу из п страниц *) v a r k, (* количество цифр *) (* во всех fe-значных числах *) наименьшее, (* наименьшее и *) наибольшее, (* наибольшее fe-значное число *) сколько
:integer;
begin сколько
: =0;
наименьшее : = 1; наибольшее'. = 9 ; for k'. = \ to числоцф (п) — 1 do begin сколько
: — с к о л ь к о + (наибольшее — наименьшее +
+ 1 )*&;
наименьшее : = наименьшее* 10; наибольшее: = наименьшее*\0 — 1 end;
страницы: =
сколько
+ (и — наименьшее + \ )*числоцф(п)
end Составленные функции можно включить в программу (или процедуру) двумя способами: 1) функция числоцф является внешней функцией по отношению к функции стра ницы, т. е. обе упомянутые функции включаются в програм му параллельно; 2) функция числоцф является внутренней, включаемой внутрь функции страницы. 4 В. А. Д агене
© Составьте функцию для решения обратной задачи: для нумерации страниц книги потребовалось k цифр ( k — входное д а н н о е ). Сколько страниц в книге? Если указанное число цифр не может служить д ля нумерации какого-либо количества страниц, то результат функции считайте р а в ным 0.
37. Счастливые троллейбусные
билеты
Номера троллейбусных билетов представляют собой шес тизначные числа. Счастливым считается тот билет, у кото рого сумма первых цифр р ав н а сумме трех последних цифр. Например, билет 627 294 считается счастливым, так как 6 + 2 + 7 = 2 + 9 + 4 = 15. Условимся, что номера билетов п рин ад леж ат промеж ут ку [100 000; 999 999]. М ожно придумать много интересных зад ач , связанных с номерами счастливых билетов. Опишем одну из них, предоставив читателям самостоятельно решить еще несколь ко таких задач. П реж де всего составим очень простую функцию, которая будет устанавливать, является ли рассматриваемое число счастливым: function счаст ( х '.integer):boolean-, (* является ли число счастливым *) function суммацф (п: integer): integer; (* сумма цифр *) ... (см. зад ач у 34) begin (* счаст *) if ( х с 100 000) ог (х > 999 999) then (* число не является шестизначным *) счаст: = false else счаст: = суммацф (х div 1000) = суммацф [х, mod 1000) end Д ал ее мы составим программу (в ней мы используем и функцию счаст) д ля решения следующей задачи: найдите все номера счастливых билетов, такие, что из них можно извлечь натуральный корень какой-либо (превышающей 1) степени. Например, У?20 801 = 8 4 9 . Эту зад ачу можно решить, перебирая по очереди все шестизначные числа и проверяя, удовлетворяют ли они условию задачи: является ли число счастливым и и звл ека ется ли из него корень 2, 3, 4-й и т. д. степени. Шестизначных 98
чисел много, да и счастливых среди них немало (более 50 ООО — [20]) . Таким образом, вычисление здесь будет д о л гим. Поищем какой-нибудь другой способ решения. Попро буем пойти в обратном направлении — поочередно возводить натуральные числа то в одну, то в другую степень. Если по лученное число оказы вается шестизначным, то надо про верить, является ли оно счастливым. Самое большое из под вергаемых проверке натуральных чисел долж но быть меньше чем 1000, т а к - к а к в противном случае его квадрат будет иметь более шести знаков: 1ООО2 > 9 9 9 999. Поскольку к а ж дое число надо будет возводить в степень не более чем 20 раз (2020 — это у ж е семизначное число), то для решения зад ач и этим способом надо рассмотреть около 1000-20 = 20 000 чисел (в действительности намного меньше, т ак как, чем больше число, тем меньше раз придется его возводить в степень). Приведем законченную программу: p ro g r a m счастливые (output); v a r k, (* корень *) р, (* степень корня *) х : integer; (* кандидат в счастливые числа*) function с част (x'.integer): boolean; (* яв л яется ли число счастливым *) (* функция данной задачи *) begin (* счастливые *) for k : — 2 to 999 do (*1.000*100Q > .9 9 9 999*) begin (* пробные натуральные числа *) x:~k*k; («степени которых суть*) р;=2; (* шестизначные числа *) while х ^ 9 9 9 999 do begin if счаст (х) then
writeln(x, р, k); х : = x*k; р : —р -И end
'
end end. © Составьте программы, печатающ ие все такие номера счастливых билетов, которые равны: а) квад рату какого-либо натурального числа; б) кубу какого-либо натурального числа; в) кв ад рату какого-либо натурального числа и одновре менно кубу какого-либо другого натурального числа. 99
© © Составьте программу для нахождения всех номе ров счастливых билетов, у которых сумма первых (послед них) трех цифр, будучи возведенной в какую-либо степень, равна номеру счастливого билета.
38. Сумма кубов цифр Существуют натуральные числа, равные сумме кубов своих цифр. Таково, например, число 370, ибо З3 + 73 + 03 = 370. Составим программу для нахождения всех таких чисел. Эта за д а ч а интересна тем, что позволяет найти все числа, обладаю щ ие названным свойством. Но ведь ЭВ М не может проверить все числа. Значит, прежде всего надо попытаться определить интервал, в котором можно надеяться встретить такие числа. Возможно, он окаж ется конечным. С ам а я б ольш ая цифра — это 9. Поэтому с а м а я б ольш ая сумма кубов цифр однозначных чисел — это 93 = 729, д в у з н а ч н ы х — 93+ 93= 1458, трехзначных 93- f 93-|~ 93= = 2 1 8 7 , четырехзначных — 93 + 93 + 93-|-9 3 = 29 16. С ам ая больш ая сумма цифр пятизначных чисел — это 3645, т. е. четырехзначное число. Значит, пятизначные и более чем пятизначные числа не удовлетворяют указанном у условию, и поэтому можно их не проверять. Точно так же можно не проверять и четырехзначные числа, превосходящие 2916. Еще более вникнув в суть дела, увидим, что из чисел, мень ших чем 2916, но больших чем 2000, наибольшую сумму ку бов цифр имеет число 2899. Эта сумма равн а 23 + 83 + + 93 + 93= 1978, т. е. меньше чем 2000. Следовательно, числа, превосходящие 2000, нас не интересуют — достаточно проверить числа в промежутке [ 1; 2000]. Теперь приведем составленную программу: p ro g r a m кубы (output)-, v a r х, (* пробное число *) п, (* последняя цифра *) р, (* число без последней цифры *) s\ integer-, (* сумма кубов цифр *) begin for ;с: = 1 to 2000 do begin s: = 0 ; p: — x\
while p > 0 do (* нахождение сум мы*) (* кубов цифр числа х *) 100
begin
n: — p mod 10; p: = p div 10; s: = s-\-n*n*n end; if x = s then writeln(x ) end end. Выполнив эту программу, ЭВМ н ап ечатала следующие числа: 1 153 370 371 407 © Составьте программу, которая будет находить все натуральные числа, равные кубу суммы своих Цифр.
39. Числа Армстронга Число, состоящее из п ( п > 1) цифр, назы вается числом Армстронга, если сумма его цифр, возведенных в п-ю сте пень, равна самому этому числу. Например, числами Арм стронга являю тся 153 и 1634, так как 1 5 3 = 13 + 53 + 33; 1634 = 14+ 64+ З4 + 44. Составим программу, которая будет находить все ге-значные числа Армстронга (п — входное данное, причем г а < 10). Чтобы программа была более экономной, цифры от 0 до 9 мы возведем в нужную степень один раз и результаты сохраним в массиве v a r с т е л : a r r a y [0..9] of integer Составим схему программы: p ro g r a m армстронг [input, output ); v a r степ: a r r a y [0..9] of integer ; n, (* исходное данное *) x, (* пробное n -значное число *) mm, (* наименьшее n -значное число *) max'.integer; (* наибольшее n -значное число*) begin
read(n); 101
массив степ заполняется n -ми степенями цифр О, 1, 2, ..., 9
находятся наименьшее и наибольшее д-значные числа: min и max for x : = m i n to max do устанавливается, какие цифры образуют пробное число х, и находится сумма п-х степеней этих цифр; если эта сумма равна х, значит, х — число Армстронга . end Д етализируем действия, записанные в прямоугольниках. Массив степ можно заполнить следующим образом: for k: = 0 to 9 do begin
степ [k] \ — k\ for / ; = 2 to n do
степ [k]: — степ [k)*k end Д ействия второго прямоугольника элементарны. Р а с смотрим третий прямоугольник. Д л я того чтобы установить, какие цифры образуют пзначное число, и найти сумму п-х степеней — сумма, следует написать такой цикл:
р : = х ; сумма-. = 0 ; for /: = 1 to п do begin
сумма : = сумма + степ [р mod 10]; р: = р div 10 end Соединив все части, получим законченную программу: p ro g r a m армстронг (input, output)-, v a r степ: a r r a y [0..9] of integer; n, (* исходное данное *) 10 2
х, min, max, сумма,
(* пробное n -значное число *) (* наименьшее n -значное число *).■ (* наибольшее ге-значное число *) (* сумма п-х степеней *) (* цифр числа х *)
k, I, р '.integer; begin
read(n); (* заполняется массив степ *) for k : = 0 to 9 do begin
степ\к\: =-к; for /: = 2 to ri do CTen[k]: = cren[k}*k end; (* находятся наименьшее и наибольшее *) (* я-значные числа: min и m ax*) m i n : = 1; for /: = 1 to n — 1 do m i n : = min* 10; m a x '. = min * 10— 1; (* испытываются все n -значные числа *) for x : = min to max do begin
p: = x ; сумма : = 0 ; (* находится сумма n~x степеней *) (*цифр числа х *) for I : = 1 to п do begin сумма: = сумма-{-степ [р m od 10]; р : = р div 10 end; if сумма = х then (*х — число А рмстронга*)
writeln(x) end end. При входном данном п = 5 ЭВ М н ап ечатала такие числа Армстронга: 54 748 92 727 93 084 0 Обобщите данную программу таким образом, чтобы она напечатала все числа Армстронга, меньшие миллиарда. юз
0 © Составьте программу, которая обнаруж ит само большое число Армстронга, еще помещ аю щееся в ЭВМ.
40. Квадраты, состоящие из разных цифр Б. Кордемский пишет [12], что среди всех десятизн ач ных натуральных чисел, у которых все цифры различны, имеется 10 полных квадратов, т. е. 10 таких чисел, из ко торых можно извлечь точный квадратный корень. Было бы интересно исследовать меньшие л-значные числа ( « С 10). Составим программу, Которая д о л ж н а найти все л-значные (п — входное данное, причем 2 ^ л ; £ С 9 ) числа, о б л а даю щие названным свойством: число долж но состоять из разных цифр и быть полным квадратом какого-либо н ату рального числа. Один из самых интересных моментов составления про граммы — установить, состоит ли рассматриваемое число из различных цифр. Д л я этого мы составим логическую ф унк цию различные. Алгоритм таков: к а ж д а я цифра рассм атри ваемого числа вносится в множество — если встретится цифра, которая уж е имеется в множестве, то ясно, что цифры рассматриваемого числа не будут различными, и это число отбрасывается. Я вляется ли рассматриваемое п-значное число (п — 2, 3, ..., 9) полным квадратом какого-либсс натурального числа, можно проверить двумя способами. Первый способ: пере брать все л-значные числа, цифры которых различны, и извлечь из них квадратный корень — если извлекается точ ный корень, то рассматриваемое число обладает сформулиро ванным выше свойством. Второй способ: перебрать все нату ральные числа, квадраты которых представляют собой л-значные числа, и тогда проверить, состоят ли квадраты из р а зл и ч ных цифр. Эта зад ач а реш ается аналогично зад ач е 37. Нетрудно понять, что второй способ намного превосходит первый (проверяется меньше чисел). Поэтому мы вы би ра ем второй способ. П р еж де всего надо установить, у ка ко го наименьшего натурального числа квад рат представляет собой л-значное число. Д л я этого составим функцию
тткЬ. Самое большое число тахкв, квад рат которого пред ставляет собой л-значное число, можно найти, применив функцию т1пкв:
т а х к в : = Тпткв (п + 1) — 1 104
Составив все названны е функции и соединив их, получаем законченную программу: p ro g r a m квадраты (input, output)-, v a r х, (* пробные числа, *1 кв, (* квадраты которых суть *). п, (* л-значные (п — исходное *) (* данное) числа *) т а х к в '.integer; (* наибольшее пробное число*) function различные (х:integer): boolean; (* состоят ли числа из различных цифр *) var s : s e t of 0..9; цифра: 0..9;
р а з л : boolean; begin s : ==[ ]; разл: — true; repeat цифра: — x mod 10; if цифра in s then разл: = false else s : = s -{-[цифра]; x: — x div 10 until (x = 0) or not разл;
различны е: — разл end; function minKe (n '.integer):integer; (* находится наименьшее натуральное число,*) (* квад рат которого — ге-значное число *) v a r nmin, (* наименьшее л-значное число *)
i: integer; ■ function sqroot (x:integer)'.integer; (* квадратный корень из натуральных чисел *) ... (см. з ад ач у 9) function sqrootmod ( х '.integer):integer; (* остаток при извлечении квадратного корня *) ... (см. зад ач у 9) begin (* minKe *) n m in ; = 1; for i : = 2 to n do if
n m i n : = n m in * \0 ; sqrootmod (пт1п)-ф0 then minKe: — sqroot (nmin)-\-\ else minKe: — sqroot (nmin)
end; begin (* квадраты *) r e a d (n);
т а х к в : — т т кв (n + 1) — 1; for x : = minKe (n) to тахкв do
, 05
begin
к в : = x*x; if различные (кв) then writeln (кв, = , x : l , * , x : l ) end end. © Составьте функцию, которая будет вычислять, сколько различных букв содержится в заданном слове. © © Составьте аналогичную программу для н ах о ж д е ния кубов, состоящих из различных цифр.
41. 1 ! + 4 ! + 5! = 145 Б. Кордемский [12] указы вает одно интересное число 145, которое равно сумме факториалов своих цифр: 145 = 1! + + 4 ! + 5!. Он пишет, что неизвестно, есть ли ещ е такие числа, удовлетворяющие названному условию. Самый простой способ поиска таких ч и с е л — проверять все числа подряд. Но ведь множество натуральных чисел бесконечно, так что мы не смогли бы таким образом найти все возможные решения. Однако в данном случае можно заметить, что долж но существовать такое число, превосхо дить которое интересующие нас числа не могут. Тогда мы составили бы программу для проверки конечного числового интервала. Будем р ассуж д ать следующим образом. Ф акториал самой большой цифры (9) представляет со бой шестизначное число. Значит, среди шестизначных чисел еще могут встретиться искомые числа. М акси м альная сум ма ф акториалов цифр семизначного числа — это 7 X 9 ! = = 2 540 160. Мы видим, что это семизначное число неве лико. М акси м альная сумма ф акториалов цифр восьмизнач ного числа т а к ж е представляет собою всего лишь семи значное число. Следовательно, достаточно проверить числа, меньшие чем 2 540 160. Составляем программу: p ro g r a m суммафащ (output); const, тахп = 2 540 159; type интервал = 0..9; (* цифры *) v a r фциф: a r r a y [интервал] of integer; (* факториалы цифр искомых чисел *)
k : интервал; п '.integer; (* пробное число *) function сумф (п '.integer):integer; 106
(* сумма факториалов цифр числа п *) v a r сумма:integer-, begin (* сумф *) сумма: = 0 ; repeat сумма: = сумма-\- фциф {п mod 10]; п : = п div 10 until п = 0;
сумф: = сумма end; (* сумф *) begin фциф [0]; = 1; (* запоминаются факториалы циф р*) for k: = \ to 9 do фциф f/г]: = ф ц и ф [k — 1]*/г; for п: = 1 to тахп do if сумф (n) = n then writeln (n) end. Д л я того чтобы программа была экономной, факториалы цифр (от 0 до 9) вычисляются один раз и сохраняются в массиве фциф. Второй цикл в основной части программы повторяется более чем 2,5 млн. раз. В цикле содержится обращение к функции сумф , а в этой функции — еще один цикл. Так что д а ж е быстродействующая ЭВМ долго билась бы с этой программой, поэтому стоит поискать способы уско рения вычислений. Попытаемся уменьшить число повторений в основной части программы. Быть может, какие-то числа можно отбро сить заранее? Посмотрим, как изменяется сумма факториалов цифр у чисел, принадлеж ащ их одному десятку от 0 до 9. В одном случае и у числа ... 0, и у числа ...1 — сумма факториалов цифр одинакова, поскольку факториал и нуля, и единицы равен единице. Ещ е в одном случае — при замене числа ... 1 числом ... 2 — сумма факториалов цифр увеличивается на единицу — на столько же, на сколько увеличилось и само число. Во всех прочих случаях эта сумма растет быстрее чем число. И з этого мы можем сделать вывод, что если сумф ( п ) > п - \ - \ , то в данном десятке точно не найдется чисел, превышающих п (т. е. таких, у которых последняя цифра больше последней цифры числа п, а все остальные цифры совпадают с цифрами числа п) и удовлетворяю щих условию задачи. Составим новый вариант программы, в котором будет учтено рассмотренное свойство чисел. 107
p ro g r a m суммафакц (output)-, const m a x n = 2 540 159; type интервал = 0..9; (* цифры *) v a r фциф: a r r a y [интервал] of integer-, (* ф акториал цифр искомых чисел *)
k : интервал-, п, s, п п : integer-, function сумф
(* пробное число *) (* сумма факториалов цифр *) (* десятки *)
(п:integer):integer-,
(* сумма факториалов цифр числа п *) v a r c y M M a : in t eg e r \ begin (* сумф *) сумма: = 0 ; rep eat сумма: = сумма -\-фциф [п mod 10]; п: — п div 10 until n = 0; сумф: = сумма end; (* сумф *) begin (* суммафакц *)
фциф [0]: = 1; (* запоминаются факториалы циф р *) for k : = 1 to 9 do (* цифр *) фциф [k]: = фциф [k — \}*k\ for n n : = 0 to maxn div 10 do (* цикл для (* чисел *) begin п : = п п * 10;
д есяти*)
s: = с у м ф (п)\ repeat if s = n then writeln (n)\ n : = я + 1; (* корректируется сумма факториалов цифр *) s : = s — фциф !(>г — 1) mod Щ-\-фциф [п mod 10] until ( s > n ) or (* в десятке не удастся найти*) (* искомых чисел *) (п m od 10 = 0) (* закончился д есяток*) end end. Во втором варианте программы меньше содержится и обращений к функции сумф. Обращ ение к ней происходит только во внешнем цикле «десяток». Во внутреннем цикле сумма факториалов цифр пересчитывается путем коррекции предыдущей суммы. Выполнив эту программу, ЭВМ н ап ечатала результаты: 108
1 2 145 40 585 0 Составьте программу, ещ е быстрее производящую вычисления, необходимые для данной задачи. Используйте в ней закон перестановки слагаемых (от перемены мест слагаемых сумма не меняется). Поэтому достаточно прове рить, состоит ли сумма факториалов цифр рассматриваемого числа из тех же цифр, что и само число. Если да, то число, равное сумме факториалов цифр, удовлетворяет условию задачи, если нет — то условию задачи не удовлетворяют и все прочие числа, которые получаются из рассматриваемого числа путем перестановки цифр. Например, 2! + 4 ! + '5 ! = 146. Из цифр 2, 4 и 5 нельзя составить число, равное сумме ф акториалов этих цифр, а именно числу 146. Следовательно, все трехзначные числа, состоящие из цифр 2, 4 и 5 (245, 254, 425 и т. д .), не удовлетворяют условию задачи.
42. Наименьшее число не всегда мало Было бы интересно найти наименьшее число, оканчиваю щееся на пять, такое, что, если перенести его последнюю цифру в начало, число увеличится в пять раз. Д л я многих зад ач , в которых требовалось найти числа, обладаю щ ие каким-либо свойством, мы составляли очень простые •программы — проверяли все числа в заданном ин тервале и рассчитывали на быстродействие ЭВМ. Поскольку здесь требуется найти только одно минимальное число, удовлетворяющее указанному условию, то возникает идея проверять подряд все числа, оканчиваю щ иеся на пять: 5, 15, 25, 35, ...— до тех пор, пока мы не найдем то, которое нужно. К сожалению, ЭВМ будет биться над решением долго и тщетно —- искомое число столь велико, что д а ж е са м ая быстрая машина искала бы его невероятно долго. Поэтому приходится искать другое, лучш ее решение. Предположим, что цифры искомого числа — это х\, х 2, ..., х„. Мы знаем, что х„ = 5. Поэтому условие задачи можно записать так: 109
X i X 2X 3. . . X n - \
X 5
5 5
X[X2X3...Xn - i
Попытаемся поискать отдельные цифры. Умножив любую цифру хi ( х ; > 1 ) на пять, получим д ву значное число ab, состоящее из цифр а и Ь. Поскольку посл едняя цифра умножаемого числа известна, получим ab = = 25, т. е. а = 2 и Ь = 5. Таким образом, последняя цифра произведения «в уме» такова: хп- \ = 6 = 5. Но эта цифра тем самым является и предпоследней цифрой множимого. С л е довательно, теперь можно умножить на пять уж е ее и по лучить новую, предпоследнюю цифру произведения х п- 2. Так надо ум н ож ать до тех пор, пока мы не получим ab — Ъ. Таким образом, д ля получения всех цифр результата мы должны создать процедуру. Поскольку мы получаем цифры в обратном порядке (единицы, десятки, сотни и т. д .), то вначале надо их запомнить, а затем напечатать в порядке, обратном по отношению к тому, в котором они были полу чены. Поэтому мы составляем рекурсивную процедуру: procedure р (а,
(* в уме *)
b ’.integer)',(* число *) v a r a b : 0..99; begin ab: = 6 * 5 -fa; if а Ь ф Ъ then p (ab div 10, ab m od "10); write (ab m od Ш: 1) end Обращ ение к этой процедуре долж но происходить так:
р (0,5). Второй параметр показывает, что число оканчи вается на пять, а первый — что при выполнении умножения «в уме» ничего нет, так как умножение начнется с последней цифры. Приведем результат, напечатанный ЭВМ: 510204081632653061224489795918367346938775. Это фактически не искомое число, а число, которое по лучается из искомого перенесением в начало последней цифры. Попытаемся найти искомое число, пойдя в обратном н а правлении: будем искать наименьшее число, которое умень шится в пять раз, если его первую цифру перенести в ко нец числа. но
Цифры числа, получающегося в результате, можно пе чатать сразу же, не запоминая их, поскольку цифры частного мы получаем в обычном порядке — слева направо. Поэтому можно составить нерекурсивную программу. p ro g r a m деление (output ); v a r a b : 0..99; b: 0..9; begin ab: = 5; repeat b : = a b div 5; write (6:1); a b : = ( a b mod 5)* 10 + fr until ab = 5 end. М еж ду прочим, этот же результат нетрудно получить и без помощи ЭВМ. Попытайтесь сделать это. © Составьте программу для нахождения двух наимень ших чисел, которые начинаются на пять и из которых, пере неся первую цифру в конец, мы получим новое число, в пять раз меньшее, нежели искомое. © © Составьте программу, в которой входное данное — число п в интервале [2; 9], а результат — наименьшее число, у которого первая цифра — п и из которого, перенеся первую цифру в конец, мы получим новое число, в п раз меньшее, нежели искомое.
43. Двоичные числа В настоящее время почти во всем мире используется десятичная система счисления. Когда-то использовались и другие системы. Их реликты можно обнаружить и сейчас. Сюда относится шестидесятеричное исчисление времени (в часе 60 мин, в минуте 60 с). На основе различных систем счисления образованы старинные единицы измерения (см. зад ачу 53). Вольтер, например, пишет, что шведский ко роль Карл XII хотел ввести двенадцатеричную систему, по-видимому, ж е л а я таким образом увековечить свой д и н а с тический номер. Д есятич н ая система счисления удобна д ля начального обучения арифметике при помощи счета на пальцах, по скольку на обеих руках вместе у нас 10 пальцев. А для счета при помощи машин значительно удобнее двоичная система. Ведь многие физические объекты имеют два четко разли чи ill
мых состояния. Они используются для изображ ения цифр двоичной си с т е м ы — 0 и 1. Например, если по проводу те чет электрический ток — единица, если не течет — нуль; если железный сердечник намагничен — единица, если не намагничен-— нуль и т. д. . Хотя почти все ЭВМ оперируют двоичными числами, про граммист их не видит, так как десятичные числа, которые записываю тся в исходных данных или в тексте программы, ЭВМ автоматически переводит в двоичные, производит с ними действия, а полученные результаты опять переводит в десятичную систему счисления. Как числа переводятся из одной системы в другую, можно прочесть в книгах Г. Бермана и А. Д ом ор яд а [9, 10]. Число п можно однозначно выразить в р-ричной системе счисления при помощи цифр гк, Г к - 1, ГЩ г0 так:
П = р кГк + рк~ [Г к - 1 +
... + p r v+ r 0
и зап и сать его таким образом: П= ГкГк- \ ....... Г|Г0 Например, число 25 можно записать десятичными ц иф рами: , 10-2 10-5. Это же число цифрами двоичной системы можно записать так:
2 -- 1+ 23-1 + 22-0 + 2 ' •0 + 2°• 1.
Поэтому в двоичной системе оно записывается таким образом: 11001. Составим процедуру для ввода натурального числа, з а писанного в двоичной системе: p rocedu re чтение (var п '.integer)', var с : char ; begin repeat ■■(* пропускаются все символы*) read (c)(* перед числами *) until (с = ’ f ); п : —0; while (c = ’ o ’ ) or (c = ’f ) do begin n : = n*2; if c = 1 then h : = n - \ - 1; read (c) end end 112
Составим процедуру для печати заданного натурального числа в двоичной системе: procedure печать (п: integer)-, begin if r a > 0 then печать (п div 2); if odd (n) then write ( 1 ) else write ( 0 ) end Цифры двоичной системы получаются при делении з а д а н ного числа на д ва (основание двоичной системы). Они представляю т собой последовательность остатков от деления на два, расположенную в обратном порядке. Д л я того чтобы последовательность цифр была напечатана в обратном по рядке, используется рекурсия. © Составьте процедуру, которая читала бы число, з а писанное в двоичной системе, и печатала бы его так ж е в двоичной системе счисления. © © Составьте программу, которая прочитывала бы два целых числа, записанных в двоичной системе, ск лады вал а их и результат печатала бы так ж е в двоичной системе.
44. Шестнадцатеричная система При проектировании или эксплуатации вычислительной техники приходится иметь дело и с двоичными числами. Д л я человека они неудобны тем, что для представления числа требуется длинный ряд цифр. Например, число 1990 в двоичной системе записы вается так: 11111000110. Такое число трудно как прочесть, так и запомнить. Числа 2"-ричных систем удобно представлять в виде последовательности единиц и нулей, переводя в число двоичной системы к а ж дую цифру 2"-ричной системы. (К а ж д а я цифра числа 2"-ричной системы соответствует п цифрам двоичной системы.) Например, число, записываемое в четверичной системе как 133 002, можно записать при помощи диад:
И,
00
1
00
10 3
3
0
0
2
а восьмеричное число 3702 — при помощи триад: 011
111
000
010
3
7
0
2 из
Поэтому в вычислительной технике часто используется восьмеричная, а т а к ж е шестнадцатеричная системы счисле ния. Поскольку основание шестнадцатеричной системы пре вышает 10, то для цифр 10, 11, 12, 13, 14 и 15 надо ввести специальные обозначения. Принято обозначать их при по мощи букв А, В, С, D, Е, F. Составим программу для ввода и печати ш естнадцате ричного числа так, чтобы его цифры были представлены в виде тетрад: p ro g r a m печать (input, output)-, (* шестнадцатеричные числа напечатать *) (* в двоичной системе группами по четыре цифры *) v a r с : char\ begin rep eat (* пропускаются все символы *) read (с) (* перед числами *) until с in f’f ..’9 ’, ’А ’, ’В’, ’С ’, ’D ’, ’Е ’, ’Р.’]; while с in [O ’ ..’9 ’, А’, В ’, ’С ’, D ’, Е ’, F ] do begin case с of ’о write 0000’); ’1 write 000 f) ; ’2 write 0010’); ’3 : write ООП’); 4 : write 0100’); ’5 : write 0101); ’6 : write 0110’); ’7 : write 0111’); ’8 : write 1000); ’9 : write 100 Г); ’А : write 1010); ’В : write ’1011’); ’С : write ’ 1100’); D : write ’l 101); ’Е : write ’1110’); ’F ; write ’l l l l ’) end; * case *) ' write ( ’); (* промежутки между группами по четыре цифры *) read (с) end end. 114
© Составьте процедуру для: а) ввода; б) печати — заданного шестнадцатеричного числа. © © Составьте программу, которая читала бы предъяв ляемое двоичное число и печатала бы его в ш естнадцате ричной системе.
45. Палиндром Слово, ф р а за или стихотворная строка, одинаково чи таемые слева направо и справа налево, называю тся палин дромами. Например: ИДИ ПОТО П И С К А ТЬ ТАКСИ То ж е самое можно ск азать и о числах. Палиндромами являются, например, числа 121 1331 42 524 Является ли десятичное число палиндромом, человек может заметить сразу. Было бы интересно знать, является ли конкретное число палиндромом в какой-нибудь другой системе счисления. Например, число 45 является палиндро мом в двоичной (101101) и в восьмеричной (55) системах. (Относительно систем счисления см. зад ач у 43.) Составим программу для нахождения палиндромов з а данного натурального числа во всех системах счисления с основаниями 2, 3, ..., 10. П реж де всего определим тип данных для представления числа в любой системе счисления: const t = ... type Цифры = a r r a y [1 ... t\ of 0..9 К аж д ой цифре в новой системе счисления выделяется по одному элементу массива. Следовательно, константа t ограничивает количество цифр в самом большом числе. Если, например, мы имеем дело с двоичной системой счисления (записанные в ней числа имеют больше всего цифр), а *= 50, то в массив можно поместить числа до 250 — 1. Составим процедуру, которая будет представлять н ату ральное десятичное число п при помощи цифр заданной рричной системы счисления и проверять, является ли оно 115
палиндромом: procedure палиндром (п, (* число *) p'.integer; (* система счисления*) v a r ц:цифры; v a r палиндр: boolean); v a r i, k'.integer; begin (* запись числа n в р-ричной системе *) k : = t ; (* const t — верхняя граница массива ц *) repeat ц [k\: — п mod р; k: = k — 1; п: — п div р until /2 = 0 ; for i: = k dow nto 1 do Ц № =0; (* является ли р-ричное число палиндромом *) i : = t + 1; repeat i: = i — !; k: = k - \ - 1 until (ц [к]фц [/]) or (k ^ i ) ; палиндр: = ц [к] = ц [/] end Включим составленную процедуру в программу, нахо дящую палиндромы заданного натурального числа в систе мах счисления с основаниями 2, 3, ..., 10: p ro g r a m палиндромы (input, output); const / = 50; type цифры = a r r a y [1../J of 0..9; v a r ц: цифры; n, (* исходное данное *)
i, k: integer; p :2 ..1 0; (* система счисления*)
па линд р; boolean; p rocedu re палиндром (n, (* число *) p '.integer; (* система счисления*) v a r ц: цифры; v ar палинд р'.boolean);' (* процедура данной задачи *) begin
read (п); writeln (’П А Л И Н Д Р О М Ы Ч И С Л А ’, п: 1); for р : = 2 to 10 do 116
begin палиндром (n, p, ц, палиндр)] if палиндр then (* печать результатов *) begin /: = 1; while ц [t]= 0 do i '• — i- + 1; for k: = i to t do write (ц [k\: = 1); writeln ( [ , p: 1, ]) end end end. При исходном данном л = 100 ЭВМ н апечатала П А Л И Н Д Р О М Ы Ч И С Л А 100 10201[3] 202 [7] 121 [9] (В квадратных скобках указывается система счисления.)
46. Большое произведение П ерем н ож ая большие числа, можно быстро получить переполнение. Поэтому, для того чтобы напечатать произве дение, превышающее maxint, надо применить искусственные средства. Составим программу для печати произведения двух чисел, которое может превышать максимально допустимое целое число maxint (см. зад ач у 2): p ro g r a m произведение (input, output); • v a r л:, у '.integer-, (* исходные д анны е*) procedure умножение (а, (* множимое *) Ь, (* множитель *) s: integer (* частичное произве дение *)); begin (* умножение *) if b ф б then begin s : = s + a*(6 mod 10); умножение (a, b div 10, s div 10); write (s mod 10:1) end else if s ^ = 0 then write (s ) 117
end; begin (* произведение *) read (x, y); w rite (x, ’* , у: 1, ’ = if ( х < 0 ) ф у ( < 0 ) then (* если знаки неодинаковы, *) (* произведение отрицательно *) w rite ( — ); умножение (abs (х), abs (х), 0) end. Процедура умножение умножает число а на каждую ц иф ру числа Ь, начиная с единиц. Последняя цифра полученного произведения, сложенная с последней цифрой имеющегося в памяти частичного произведения, печатается, а все прочие цифры запоминаю тся — передаются как параметры при ре курсивном обращении к процедуре умножение. В самом конце производится умножение на первую (левую) цифру чис л а Ь. На этом умножение заканчивается. Тогда печатается начало результата — накопившееся частичное произведение без последней цифры (s div 10), а после него при в о звр а щении из рекурсии — все остальные цифры произведения (s mod 10) в обратном порядке по сравнению с тем, как они вычислялись при входе в рекурсию. В соответствии с этой программой ЭВМ перемножила числа 123 456 789 и 123 456 789 и напечатала такой ре зультат: 123456789*123456789 =15241578750190521 Значения параметров процедуры умножение ограничива ют максимально возможное частичное произведение. П о скольку на множитель (второй параметр) умножаются о т дельно взятые цифры, то он может находиться в интервале [0; m a x in t]. Убедитесь в этом! Д л я того чтобы мы могли вычислить промежуточный результат a*(b mod 10), з н а чение а не долж но превыш ать maxint div 9, а оператор присваивания s : = s + a*(b mod 10) мы сможем выполнить в том случае, если значение а будет вдвое меньшим, т. е. если максимальное значение множимо го (первого парам етра) будет примерно в 20 раз меньшим чем maxint. © Составьте программу умножения числа, не превы шающего maxint div 20, на число, превышающее maxint.
П8
47. Большие числа Иногда результат операций, выраженный большим чис лом, необходимо не только напечатать, но и сохранить в памяти ЭВМ, с тем чтобы с ним можно было производить какие-то другие действия. М ожно сгруппировать цифры числа и представить эти группы в виде отдельных чисел, объединенных в запись или массив. Тогда операции с б оль шими числами можно выразить в виде последовательности операций с числовыми компонентами. П рощ е всего (хотя и неэкономно) для каждой цифры числа выделить отдельный элемент массива. Так и сделаем. Определим массив для представления больших натуральных чисел: const / = Ю 0 ; type большое — a r r a y [1../] of 0..9 К а ж д а я цифра большого числа представлена отдельным элементом массива. Это естественное, но неэкономное пред ставление (в один элемент массива уместилось бы и большее количество ц и ф р ) . При помощи такого массива можно представить число, не превышающее Ю100. Этот предел установлен не случайно. Ведь 10шо часто рассматривают как пример очень большого числа (иногда в популярной литературе его назы ваю т гугол). Составим процедуру для сложения двух чисел типа боль шое: procedure сложение (а, Ь: большое; v a r с ’.большое); v a r / : 1../; вуме: 0..1; s:0..19; (* сумма двух цифр -f- в уме *) begin вуме: = 0 ; for j : = t dow nto 1 do begin s : = a \ j \ + 6 1/| -\-вуме; с [/]: = s mod 10; вуме: = 5 div 10 end; if в у м е = 1 then writeln ( П Е Р Е П О Л Н Е Н И Е ’) end Составим программу, которая напечатает факториалы чисел от 1 до 50: p ro g ra m больфак (output); const « = 50; /= 1 0 0 ; 119
type большое = a r r a y [1../] of 0..9; v a r f '.большое-, (* f:in teg er *) i, k'A ..t; j : 1..n; d:integer-, (* частичное произведение*) begin (* больфак *) f[t}: = 1; (* f: = 1*) for i: = 1 to / — 1 do f[« ']:= 0 ; for j : = 1 to n do begin d: = 0 ; for / : = f dow nto 1 do ( * / : = f * / * ) begin d : = d + f [/]*/; / [г]: = d mod 10; d: = d div 10 end; w rite (j, ’! = ’); k : — l;
while f [^J = 0 do (*writeln (f)*) begin шгг7е ( ); k: = k + l end; for i: = k to t do w rite (f [г]: 1); writeln end end. Д ействия с большими числами поясняются в коммента риях. Там указы ваю тся такие операции, которые надо было бы выполнять, если бы число f не было большим. © В программах, оперирующих с очень большими числа ми, было бы удобно иметь набор процедур, выполняющих арифметические операции ( + , — , *, div, mod) и операции сравнения ( < , > , = , ф ) . Составьте набор таких процедур. Большие числа представьте при помощи типа большое. © © Составьте программу для нахождения такого числа п, факториал которого близок числу Ю100, т. е. такого, что л ! < Ю ' 100; ( « + 1 )!> ’ю 100. 120
48. Точность действительных чисел Д ействительные числа в вычислительной машине пред ставлены приближенно. Д л я того чтобы оценить точность вычисления, необходимо знать, сколько знаков (цифр) вы делено для представления числа. В языке П аскаль, как и почти во всех прочих языках, нет констант (аналогичных константе m axint), которые бы непосредственно указывали точность действительных чисел. Поэтому спраш ивать у м а шины, сколько цифр выделено для действительного числа, приходится окольными путями. Приведем программу, которая печатает число десятич ных знаков, выделяемых для представления действительного числа: p ro g ra m точность (output); const система— 10; (* десятичная система счисления*) а — 1.0; (* число а имеет один точный зн ак *) v a r p:rea l; (*1.0, 0.1, 0.01, 0.001, ...*) п: integer-, (*число знаков*) begin р ; = 1.0; п: = 0; repeat п : —п + 1 ; р : = р / система until а = а-\-р; write (п) end. Цикл заканчивается, когда 1 . 0 = 1.0...01. Это означает, что последняя цифра числа 1.0...01 исчезла. Значит, ЭВМ вы деляет для представления действительного числа п — 1 десятичных знаков. Выполнив эту программу, ЕС ЭВМ напечатала число 7, а БЭСМ-6 — число 6. Если мы хотим выразить точность при помощи числа з н а ков в какой-либо другой системе счисления, следует изме нить константу система.
49. Точное частное Как разделить одно целое число на другое так, чтобы получить частное (действительное число), имеющее столько точных цифр после запятой, сколько мы хотим? Попытаемся составить программу решения этой задачи. 121
Операция деления действительных чисел (ее знак / ) в данном случае нас не устраивает, поскольку ее результат о казы вается приближенным. Хорошенько подумав, нетрудно найти выход: получив очередную цифру частного, стоящую после запятой, сразу печатаем ее. Цифры частного мы полу чаем тем ж е способом, что и при делении столбиком: о с т а ток умножаем на 10, а затем делим на делитель. p ro g ra m деление (input, output); v a r а, (* делимое *) b, (* делитель *) п, (* число точных цифр после запятой *) г: integer; begin (* деление *) read (а, b, п); w rite (а, : ', b: 1, ’ = if (а 0) или вниз (если п С О ) .
56. Случайные числа Мы не можем сразу сказать, какая сторона монеты или игральной кости окаж ется сверху, какую карту мы вытащим из колоды. Говорят, что результат такого эксперимента явля134
ется случайным. П овторяя эксперимент много раз, получаем последовательность случайных значений. Интересно, что не возможно предвидеть значение нового члена ряда — он не зависит от предшествующих членов. В зад ач ах , в которых моделируются жизненные ситуа ции (например, игры, эпидемии заболеваний и т. д .), тр е буются случайные значения. Значение любого числового типа можно получить из целых чисел, поэтому достаточно было бы иметь только генератор случайных целых чисел, т. е. такую функцию или процедуру, о б р ащ а ясь к которой мы каждый раз получали бы все новые и новые члены последо вательности случайных чисел (принадлеж ащ ие у казан но му и н те р ва л у ). Случайные числа можно получать различными методами, наиболее распространенный из них — рекуррентный, суть ко торого заклю чается в следующем: каж дое случайное число г к находится из предыдущего числа гк~ \ по формуле гк = = ( г к ~ I * сомножитель -{-прибавка) mod делитель. В этой формуле в качестве г0 выбирается любое число. Числа г 1, г 2, ..., Гк, получаемые по этой формуле, строго говоря, не будут случайными числами в теоретико-математическом смысле, т а к как Гк-е число зависит от значений гк- 1, г к_2, •••, г0. Поэтому числа, получаемые таким способом, принято назы вать псевдослучайными [4]. В соответствии с упомянутой формулой получаются числа на отрезке [0; делитель— 1]. Когда в ряду будет делитель чисел, ряд повторится заново. Какой величины будет повторяемая часть ряда, зависит от констант сомножи тель и прибавка. Чтобы таких повторений было меньше, т. е. получалась достаточно длинная последовательность сл у чайных чисел, необходимо надлеж ащ им образом подобрать упомянутые константы. В книге [4] предложены такие кон станты: делитель = 2 |б = 65 536; сомножитель = 25 173; прибавка = 13 849. Составим функцию для получения псевдослучайного н а турального числа на отрезке [1; 65536): v a r 4Aen:integer; function nceedo:integer; .• /■n + .p , . (* псевдослучайные натуральные числа *) const сомножитель— 2Ъ173; , ... п ри б авка = 13849; делитель = 65536;.
У■
■ 135
begin член: — (член* сомножитель + прибавка)тоАделитель + 1; п сев до : — ч л е н -\-1; end Эта функция вычисляет 65 536 случайных чисел — после этого расположение чисел повторится. Обратим внимание на то, что функция псевдо использует глобальную перемен ную член. Если бы мы объявили ее параметром функции, то она д о лж н а была бы быть переменным параметром (поскольку каждый раз эта функция д о л ж н а передавать различные значения, а это было бы нежелательным по бочным эффектом функции). При первом обращении к функ ции переменной член следует приписать какое-нибудь исход ное значение. Часто бывает нужно получать псевдослучайные числа в каком-либо интервале. Д л я этого строится отображение результатов функции в тот интервал, в какой необходимо. Например, бросание игральной кости моделируется таким оператором: оч ки : = псевдо m od 6 + 1 Д л я того чтобы получить псевдослучайные действитель ные числа, удобнее всего порождать их из интервала [0; 1), видоизменив саму функцию порождения псевдо случайных чисел: v a r 4AeH\lnteger\ function п с е в д о :real\ (* псевдослучайные действительные *) (* числа в интервале [0;1)*) const сомножитель = 25173; п р и б а в к а = 13849; делитель— 65536; begin член: — (член * сомножитель-{-прибавка) mod делитель; псевдо: — член/делитель end Чтобы получать псевдослучайные действительные числа в большем интервале, надо умножить значение данной функ ции на соответствующую константу.
57. Вычисление значения л путем бросания иглы Мы опишем интересный и неожиданный способ вычисле ния числа л. Берется короткая швейная игла (лучше с обломанным о стр и ем )/и м ею щ ая на всем протяжении одина 136
ковую толщину. На листке бумаги чертится несколько тон ких параллельных прямых линий так, чтобы расстояние между соседними прямыми было вдвое большим, нежели длина иглы. После этого с одной и той ж е высоты бросают иглу на бу магу и смотрят, пересекает ли она какую-либо из проведен ных прямых или нет (рис. 14). При этом пересечением счи тается и тот случай, когда игла касается прямой лиш ь кон цом. Чтобы игла не о тскаки вала, под лист бумаги можно положить лоскуток ткани, поролон или что-либо подоб ное. Иглу бросают много раз, например сто или тысячу, и к а ж дый раз отмечают, пересекла ли она прямую. Разделив число бросков иглы на число пересечений, получим приблизи тельное значение я: ^ __число бросков число пересечений
Чем больше число бросков, тем более точным оказывается полученное значение я. • Этот эксперимент осуществил в 1733 г. французский естествоиспытатель Ж о р ж де Бюффон (de Buff on, 1707— 1788). Интересно, какое значение я мы бы получили, если бы иглу «бросала» ЭВМ. Составим программу бросания иглы. Д л я того чтобы определить положение иглы на плоскости, достаточно знать координаты (сх, су) какой-либо точки иглы и угол а, который игла составляет с осью абсцисс (рис. 15). Условимся, что начерченные прямые параллельны оси абсцисс, а угол а острый. Д л я получения случайных чисел воспользуемся состав ленной в зад ач е 56 функцией псевдо, значения которой представляют собой действительные числа на промежутке [0; 1) и при этом точность всех значений одинакова. Иначе 137
обстоит дело, когда иглу бросают рукой на лист бумаги. Вероятность того, что игла упадет на край листа, много меньше, чем вероятность падения на середину листа. Так что в этом случае мы не можем применить функцию псевдо. Но ведь нам безразлично, около какой прямой упадет игла. Важны не координаты центра иглы, а расстояние от ее центра до ближайш ей прямой. Обозначим это расстояние буквой с. Тогда положение иглы мы можем определить при помощи двух случайных чисел с н а . Д лину иглы при мем равной 1 см, тогда расстояние от ее центра до б л и ж а й шей прямой представляет собой число, находящееся в промежутке [0; 1). (Если центр находится на одинаковом удалении от обеих прямых, выберем нижнюю прямую.) Ф а к тически мы ограничиваемся одной прямой, совпадающей с осью абсцисс, и считаем, что игла п адает недалеко от нее, т. е. ординату центра иглы мы можем случайным образом выбирать из промеж утка [ — 1; 1). В программе положение центра иглы мы будем определять так: с: = псевдо *2 .0— 1.0 Теперь необходимо получить случайный угол а. На к в а д р а т ном поле обозначим две точки (х\, у 1) и (х2, г/2) и найдем угол, который прямая, соединяю щая эти две точки, образует с осью абсцисс. Поскольку размеры поля не играют роли, то мы можем считать его высоту и ширину равными 1 см. Имея случайные числа x l , у 1, х 2 , г/2, мы следующим образом опре деляем угол а и его синус: if x l = x 2 then s': = 1.0 (* игла перпендикулярна пря мым *) else begin альфа: = a r c t a n ((у2 — y l)/(x 2 — xl)) s : = s i n (альфа) end Теперь мы у ж е в состоянии проверить, пересекает ли игла прямую. Д л я этого необходимо найти ординаты кон цов иглы и проверить, находится ли прямая, совп адаю щ ая с осью абсцисс, между концами иглы. Д л я такой проверки составлена функция пересек. Приведем окончательный вид программы: p ro g ra m игла (output); const чброс = 10000; (* число бросков*) 7 v a r k, (* число пересечений *) член, (* глобальная переменная функции псевдо *) 138
m : integer; с, x \ , у 1, x2, y2, s:rea l\ function п с е в д о :real\ (* псевдослучайные действительные *) (* числа в интервале [0; 1)*) ... (см. зад ач у 56) function пересеч (конецХ, конец2:real):boolean-, begin пересеч: = (конец\ ^ 0 . 0 ) an d (0 .0 ^ к о н е ц 2 ) or ( к о н е ц 2 ^ 0 .0 ) and ( 0 . 0 ^ конец 1) end; (* пересеч *) begin (* длина иглы 1 см, меж ду прямыми 2 см; *) (* предположим, что игла упала так, что *) (* б л и ж а й ш ая п рям ая до ее центра у — 0.0; *) (* игла может пересечь только эту прямую *) член; = 5 0 0 0 ; k: = 0 ; for m : = 1 to чброс do begin с : — псевдо*2 — 1; (* случайным образом берем центр*) (* рядом с прямой у = 0.0*) (* случайным образом берем *) х \ : =псевдо\ (* две точки: *) у \ : = псевдо-, ( * ( х \ , у \ ) и *) х2 : = псевдо; (* (х2, у2) *) у2: = псевдо-, if х\ = х 2 then s : = 1.0 else s: = s i n (arctan ((у 1 — y2)/(x \ — -^2))); if пересеч (c -\-s /2 , c — s /2 ) then k : = k - \ - l end; write (чброс/ k : 12:10) end. 0 Ж . де Бюффон сформулировал и решил такую задачу: найдите вероятность того, что игла пересечет проведенные прямые, если лист бумаги разлинован параллельными прямы ми не только в горизонтальном, но и в вертикальном н ап р ав лении (расстояния между всеми прямыми одинаковы). Вероятность вычисляется по формуле __число пересечений число бросков
Составьте программу для нахождения этой вероятности. 139
58. Площадь фигуры Покажем , каким образом можно приближений нм'нн'лить площ адь фигуры, ограниченной какой либо к|>пnii, методом Монте-Карло. Название метода связано с городом МонтеКарло, славящ и м ся на весь мир своими игорными домами. В Основании этого метода л е ж а т случайные числа. Он особенно пригоден для тех случаев, когда уравнение кривой оказы вается сложным, трудноинтегрируемым. Кратко поясним его сущность. Исследуемую фигуру впи сывают в какую-либо другую фигуру, площ адь которой вычислить легко (например, в квадрат или в прямоуголь ник). Выбираются случайные точки (т. е. порождаются слу чайные числа, определяющие координаты точек), находя щиеся в пределах фигуры известной площади, и вычисляет ся, сколько точек попало внутрь исследуемой фигуры и сколько о казалось снаружи. Соотношение числа точек, о к а завш ихся внутри фигуры, к общему числу имеющихся точек будет пропорционально соотношению площадей фигур. Чем больше точек мы исследуем, тем более точной окаж ется д а н ная пропорция. Из нее нетрудно вычислить площ адь иссле дуемой фигуры. Д л я того чтобы проил люстрировать данный ме тод, составим программу, которая будет вычислять площадь фигуры, ограни ченной синусоидой (т. е. кривой у = sin х при Рис. 16. Вычисление площади фигуры, О ^ х ^ л ) и линией у = 0. ограниченной половиной синусоиды. Д анную фигуру впишем в прямоугольник (рис. 16). p ro g ra m монтекарло (output); const г ч = 1 0 0 0 ; (* число пробных точек*) ли = 3.141592636; v a r х, у , (* координаты пробных точек *) площпр, (* площ адь прямоугольника *) площф: real; (* площ адь фигуры *) п, (* число точек, находящихся внутри фигуры *) член, (* глобальная переменная функ ции псевдо *) i: integer; function псевдо: real; (* псевдослучайные,действительные *) (* числа в интервале [0; 1) *) |4п ... (см. зад ач у 56)
begin член: = 2 0 0 0 ; n: = 0; for i: — 1 to тч do begin x: = n ceed o * n u ; (*0s^.x 0) дней *) v a r Mu,:integer: function дгод (dz:zod):integer; (* число дней в году *) begin if високос (гд) then д г о д : = 366 else дгод: = 3 6 5 end begin (* возврат в н ачало года дт. гд *) for мц: = 1 to дт.мц — 1 do дн: = д н - \ - д м е с (дт.гд, мц); дн: = д н - \ - дт.дн; (* вперед на дн дней *)
146
while д н > д г о д (дт.гд) do (* находится год*) (* новой даты *) '
begin дн: = д н — дгод (дт.гд); дт.гд: = д т.гд- \ - 1 end; дт.мц: = 1; while д н > д м е с (дт.гд, дт.мц) do (* находится месяц *) (♦•новой даты *) begin дн: = д н — дмес (дт.гд, дт.мц); дт.мц: = дт.мц + 1 end; д т .д н : = д н end Д л я того чтобы шаги были равны целым годам и целым месяцам, в начале процедуры у казан н ая д ата «отодвигается» назад к началу года, а указанное число дней дн соответствен но увеличивается. В процедуре будущ ее используются функции из предыду щих задач (см. зад ач у 59) : високос (гд ) — является ли год гд високосным — и дмес (гд, мц) — сколько дней в месяце мц года гд. Д а т а определяется по типу записи (такж е см. з а дачу 59). © Составьте процедуру для нахождения даты в про шлом, т. е. даты, которая была за дн дней до указанной даты.
62. Число дней между датами Составим функцию, которая будет находить, сколько дней прошло от одной даты до другой. Условимся, что во всех случаях вторая д ата является более поздней, нежели пер вая. Если обе даты совпадают, искомое число дней равно нулю. Например, от 5 октября 1990 г. до 11 октября того же года пройдет 6 дней, а от 1 декабря до 2 декабря одного и того же года — 1 день. Д аты определим по типу записи (см. зад ач у 59). Опять проще всего было бы применить для решения этой задачи процедуру завтдень (см. задачу 60) — вычислять дату с л е дующего дня до тех пор, пока она не станет равна второй (более поздней) из указанных дат. Однако если расстояние между двумя датам и велико (несколько лет), то т ак ая функция оказы вается очень неэффективной — к процедуре завтдень придется о б ращ аться много раз! Поищем более короткий путь. 147
д1
д2
4------ -— - 4 - — — гИ д ,
гд+1
-------------
|
—
гд+2------------------ гд+п
„
д Рис. 17. Схема нахож ден ия числа дней от даты д\ до даты д2.
Как можно было бы найти число дней между двумя датам и д \ и (32, представим графически (рис. 17). Из приведенной схемы видно, что искомый результат будет равен Д - д ' + д". Таким образом, необходимо вычислить три результата: 1) Д — число дней от н ачала д \ . г до н ачала д2.гд; 2) д' — число дней от н ачала года д \ . г д до даты