/
Author: Рубанцев В. Рубанцева Л.
Tags: информатика задачи по математике математическое программирование компьютерные технологии
ISBN: ㅤ
Year: 2026
Text
1
Валерий Рубанцев
Если бы у Диофанта был компьютер.
Решаем исторические задачи по математике
Все права защищены. Никакая часть этой книги не может быть воспроизведена в любой
форме без письменного разрешения правообладателей.
Автор книги не несёт ответственности за возможный вред от использования информации, составляющей содержание книги и приложений.
Copyright 2026 Валерий Рубанцев
Лилия Рубанцева
2
От автора
Железный конь идёт
на смену крестьянской лошадке.
Остап Бендер, Великий комбинатор
Когда я был маленьким, я тоже ходил в школу. Это было
давно, и на уроках математики мы решали задачки с помощью
логарифмической
линейки и таблиц Брадиса.
Компьютеры были тогда такие большие и дорогие, что
мы и мечтать о них не могли.
Сейчас компьютер есть у
каждого, кто его имеет, так
почему бы нам не взнуздать
железного друга конём и не привлечь его к решению занимательных исторических задач?
Цель этой книги: показать на занимательных примерах, как можно решать на компьютере исторические задачи – от самых древних до почти
современных.
Мы, естественно, будем решать только избранные задачи. Во-первых, исторических задач так много, что решать – не перерешать. Во-вторых, для
решения некоторых задач требуется сообразительность, а это свойство человеческого ума, а не компьютерного. И наконец, в-третьих, не каждую задачу можно решить на компьютере (пока!).
Все задачи в книге решаются не в общем виде, а конкретно, то есть с получением результата (ответа), поэтому они будут решены с помощью программ, написанных на вполне определённом языке программирования. Я
выбрал PascalABC.NET (дальше я называю его фамильярно паскалем), так
как его синтаксис лёгок для понимания, а возможностей у паскаля вполне
достаточно для решения любых занимательных математических задач. С не
меньшим успехом это можно сделать и на других языках программирования - на Питоне, Дельфи или Яве. Так как решение всех задач подробно
3
объясняется, то программы из книги без особого труда можно перевести
через дорогу или на любой современный язык программирования.
В книгу не включены чисто комбинаторные, логические, вероятностные и
графовые задачи. Они появятся на свет в других книгах, где им и место.
Решение задач мы начнём с глубокой древности. Это задачи, которые придумали ещё в Древнем Вавилоне, Египте, Индии, Персии, Греции, Риме и Китае. Да, уже много тысячелетий тому назад людям было интересно поломать голову, решая математические задачи. Большинство старинных задач
не очень сложны, что и понятно, но некоторые из них благополучно дожили
до наших времён и всё ещё вызывают интерес у любителей математики. Достаточно вспомнить изумительную задачу про кроликов и фазанов, которую до сих пор решают головоногие школяры, прилежно пересчитывая их
головы и ноги.
С развитием математики занимательные задачи стали появляться и во многих других странах. Вы познакомитесь с математическими жемчужинами из
разных стран: Франции, США, Англии, России, Дании, Турции, Чехии, Болгарии, Германии, Индии, Китая. Эти задачи более изощрённые и заковыристые, чем древние. Среди них вы найдёте немало таких, которые потребуют
от вас и смекалки, и систематического перебора. Следует отметить коварную датскую задачу о покупке свиней, английскую задачу про кусочки сахара, российскую задачу про стаю гусей и отменную литературную задачу
из рассказа Репетитор Антона Павловича Чехова.
Как вы уже знаете или только ещё узнаете, древнегреческий учёный Диофант прожил 84 года и за это время написал чёртову дюжину книг по арифметической математике. Несколько исторических задач придумал для нас
сам Диофант и его фанаты – про самого Диофанта. Но Диофант озадачил нас
ещё крепче диофантовыми уравнениями, на которых зиждутся многие занимательные задачи, которых пруд пруди в третьей главе этой книги.
Ключевая тема этой книги – решение занимательных исторических задач
на компьютере, которого у Диофанта в помине не было, поэтому он был умным и учёным. Все задачи решены на паскале, потому что это быстро и
удобно.
4
Эта книга не учебник и не пособие по решению задач или по программированию. Вы можете решать задачи по своему выбору любым удобным для вас
способом. Мои программы – не образец для подражания, а только отчёт о
том, как весело и задушевно я провёл время за компьютером, решая занимательные задачи. Чего я вам от всей души и желаю!
Книгу изрядно и наглядно украшает художественное творчество искусственного интеллекта. Если кому-то паче чаяния станет скучно решать программы на компьютере, тот вполне может увеселить себя на некоторое
время рассматриванием весёлых картинок и чесанием пяток подмышками.
Для кого эта книга
Занимательные задачи можно с успехом использовать на уроках информатики и математики – как эффективное средство для укрепления навыков
решения задач и программирования. Учителям информатики и математики эта книга может быть полезна и при подготовке интегрированных
уроков.
Решение занимательных задач интересно и само по себе, поэтому я надеюсь, что книга заинтересует любителей различного рода задач и головоломок.
Так как в книге предложен способ решения задач на компьютере, то начинающие программисты и любители программирования найдут в ней немало полезных упражнений и советов для себя.
И наконец, родители, которые хотели бы привлечь детей к программированию и тем самым развить их логическое, алгоритмическое мышление,
должны обратить самое пристальное внимание на эту книгу.
Автор
5
Условные обозначения, принятые в книге:
Дополнение или замечание
Требование или указание
Исходный код:
// вычисляем число горстей:
function Solve() := 7 * 7 * 7 * 7 * 7;
begin
Writeln(' Египетские кошки');
Writeln;
var числоГорстей := Solve();
// печатаем ответ:
var otvet := $' Число горстей = {числоГорстей}';
Writeln(otvet);
Writeln;
end.
Задание для самостоятельного решения
Заголовок проекта:
Проект …
Исходные коды всех проектов находятся в папке _Projects
6
Оглавление
Если бы у Диофанта был компьютер. .................................. 2
Решаем исторические задачи по математике .......................... 2
От автора ............................................................... 3
Оглавление ............................................................. 7
Глава #1. Практикум по вычислительным задачам .............. 12
Проект Тест по арифметике ................................................................................ 17
Проект Вычитаем ..................................................................................................... 20
Проект Умножаем .................................................................................................... 21
Проект Градусник .................................................................................................... 22
Проект Кошки-мышки ............................................................................................ 27
Проект Число шахматных партий ...................................................................... 29
Проект Какая разница? ........................................................................................... 30
Проект Складывание бумаги ................................................................................ 32
Проект Какая разница для любознательных .................................................. 39
Проект Три цифры - раз ........................................................................................ 41
Проект Три цифры - два ....................................................................................... 43
Проект Гугол ............................................................................................................. 48
Проект Вычисление сложных арифметических выражений .................... 49
Проект Громоздкое выражение ........................................................................... 51
Проект Вспомогательные переменные ............................................................ 53
Умножение чисел a5 x b5...................................................................................... 57
Проект Умножение чисел, близких к 100 ........................................................ 58
Проект Умножение близких чисел .................................................................... 62
Восемь восьмёрок для любознательных .......................................................... 64
Проект Игра 24 для любознательных .............................................................. 69
Проект Четыре четвёрки для любознательных ............................................. 72
Проект Solve this POWER Math Exercise LIKE a Genius .............................. 75
Проект Can you solve this in 10 SECONDS? – Calculate powers ................... 76
Проект Какое число больше? | Попробуйте решить ................................... 78
Проект Harvard University Admission Interview Trick .................................. 79
Проект Harvard University Admission Interview Trick l 99.9% Failed ...... 80
7
Вычисления по формулам .................................................................................... 81
Проект Периметр квадрата ................................................................................... 82
Проект Длина окружности .................................................................................... 84
Проект Площадь круга ........................................................................................... 86
Проект Площадь прямоугольника ..................................................................... 87
Обобщение ................................................................................................................. 89
Задания для самостоятельного решения ......................................................... 89
Глава #2. Решаем исторические задачи ........................... 92
Проект Египетские кошки ..................................................................................... 94
Проект Египетские кошки 2 ................................................................................. 97
Проект Египетские кошки 9 ................................................................................. 99
Проект Вавилонские ладони .............................................................................. 103
Проект Греческие мешконосы ........................................................................... 105
Проект Индийские пчёлы ................................................................................... 109
Проект Шахматное число.....................................................................................116
Проект Китайские кролики и фазаны ..............................................................119
Проект Лошади и пастухи .................................................................................. 125
Проект Вьетнамские буйволы ........................................................................... 127
Проект Индийские обезьяны .............................................................................. 134
Проект Индийские обезьяны 2 .......................................................................... 140
Проект Жизнь Демохара ...................................................................................... 146
Проект Индийское число .................................................................................... 152
Проект Пифагорейское число .......................................................................... 156
Проект Эпитафия Диофанта ...............................................................................161
Проект Диофантово число ................................................................................. 165
Проект Задача Диофанта II-10 ......................................................................... 168
Проект Индийский храм ...................................................................................... 175
Проект Египетские коровы................................................................................. 179
Проект Арабские голуби ..................................................................................... 183
Проект Персидские яблоки ................................................................................ 186
Проект Римские адвокаты ....................................................................................191
Проект Гаусс ........................................................................................................... 196
Проект Кахунский папирус ............................................................................... 200
Проект Берлинский папирус ............................................................................. 204
8
Проект Индийские квадраты ............................................................................. 208
Проект Вавилонские квадраты и кубы ............................................................ 214
Проект Китайские купцы ................................................................................... 220
Проект Китайские воры ...................................................................................... 224
Проект Задача Суань Шу ..................................................................................... 229
Проект Китайские бараны .................................................................................. 233
Проект Китайские цыпочки .............................................................................. 234
Проект Задача Парамадисвары ........................................................................ 236
Проект Задача Бхаскара Акариа ....................................................................... 238
Проект Из жизни Дефурнеля ............................................................................. 241
Проект Чисто американская задача ................................................................. 243
Проект Чисто французская задача ................................................................... 248
Проект Американские цыпочки ....................................................................... 253
Проект Американское наследство .................................................................. 256
Проект Английский юмор ................................................................................. 263
Проект Русские яблоки ....................................................................................... 267
Проект Американские яблоки........................................................................... 272
Проект Свинская задача ...................................................................................... 278
Проект Турецкие долгожители ....................................................................... 285
Проект Винные бочки .......................................................................................... 289
Проект Чешские сливы ....................................................................................... 295
Проект Французский покупатель ..................................................................... 299
Проект Болгарский парикмахер ...................................................................... 304
Проект Болгарские сливы ................................................................................. 306
Проект Индийские рупии.................................................................................... 310
Проект Русские гуси ............................................................................................. 315
Проект Задача Ризе .............................................................................................. 320
Проект Немецкий вопрос .................................................................................. 324
Проект Задача Михаэля Штифеля .................................................................. 326
Проект Китайская арифметика ......................................................................... 329
Проект Вторая задача Михаэля Штифеля ..................................................... 332
Проект Третья задача Михаэля Штифеля ..................................................... 334
Проект Задача Этьена Безу ................................................................................ 336
Проект Кому сколько лет? ................................................................................. 339
Проект Ноги и головы ......................................................................................... 341
9
Проект Репетитор ................................................................................................. 345
Проект Насос Эдисона ........................................................................................ 350
Проект Задача Рачинского ................................................................................. 353
Проект Задача Рачинского для любознательных ....................................... 357
Задания для самостоятельного решения ...................................................... 359
Глава #3. Диофантовы уравнения ............................... 362
Проект На ферме .................................................................................................. 366
Проект Кролики и фазаны ................................................................................. 367
Проект Решите систему уравнений ................................................................. 370
Проект Сооружение для лаборатории ............................................................ 372
Проект И такие есть числа 3 ............................................................................. 375
Проект И такие есть числа 4 ............................................................................. 378
Проект Ящики ........................................................................................................ 380
Проект Путёвки ..................................................................................................... 383
Проект На базаре................................................................................................... 385
Проект Дедушка и внучка ................................................................................... 387
Проект Сколько у мамы дочерей и сыновей? ............................................. 388
Проект Счётные палочки ................................................................................... 390
Проект Три сестры на рынке/Продажа кур .................................................. 392
Проект Парикмахер ............................................................................................. 397
Проект Фломастеры и карандаши .................................................................... 398
Проект Плитка на полу ...................................................................................... 400
Проект Мартышка и кокосовые орехи ............................................................ 401
Проект Мартышка и кокосовые орехи 2 ....................................................... 405
Проект Сказки Шехерезады ............................................................................... 407
Проект Пирожки ................................................................................................... 408
Проект Картины...................................................................................................... 410
Проект Осьминоги и морские звёзды ..............................................................411
Проект Куры и кролики ....................................................................................... 413
Проект Пяти- и двухрублёвки ........................................................................... 415
Проект Покупка свитера ..................................................................................... 416
Проект Монеты на планете С ............................................................................ 420
Проект Дяди и тёти .............................................................................................. 422
Проект Шлюбзики и шпегльморгеры ............................................................ 424
10
Проект Мыши ......................................................................................................... 426
Проект Ревизия магазина ................................................................................... 428
Проект Покупка марок/Покупка почтовых марок .................................... 430
Проект Покупка фруктов.................................................................................... 432
Проект Два числа и четыре действия ............................................................ 433
Проект Какой прямоугольник? ......................................................................... 435
Проект Два двузначных числа .......................................................................... 438
Проект Пифагоровы тройки чисел/Пифагоровы числа ......................... 441
Великая теорема Ферма....................................................................................... 445
Проект Сумма кубов ............................................................................................. 446
Проект Неопределенное уравнение третьей степени .............................. 452
Проект Bogenschießen .......................................................................................... 456
Задания для самостоятельного решения ...................................................... 459
Литература ........................................................ 461
11
Глава #1. Практикум по вычислительным
задачам
12
Говорят, что числа правят миром.
Нет, они только показывают, как правят миром.
Иоганн Гёте
Детский компьютер
Как вы знаете из уроков математики, чисел бесконечно много, но их можно
разбить на отдельные подмножества по тем или иным признакам.
Самые первые числа, которые придумали ещё первобытные люди, называются натуральными. Они используются для подсчёта различных предметов, например яблок или палочек, на которых вы и сами учились считать в
первом классе.
Папа спрашивает у сына:
- Скажи, сколько будет, если к трём грушам прибавить ещё две груши?
Сын отвечает:
- Не знаю, папа, мы в школе решаем задачи только
про яблоки!
Множество натуральных чисел обозначается большой латинской буквой N, поэтому само множество можно записать так: N = {1, 2, 3, ...}. Иногда
к множеству натуральных чисел относят и нуль (отсутствие предметов вообще): N0 = {0, 1, 2, 3, ...}. Множество натуральных чисел является подмножеством всех чисел и также бесконечно.
13
Если к натуральным числам добавить отрицательные числа (и нуль), то
получится множество целых чисел. Оно обозначается большой латинской
буквой Z = {... -2, -1, 0, 1, 2, ...}. Нетрудно догадаться, что и целых чисел бесконечно много.
В арифметике обычно используют именно целые числа, но встречаются
алгебраические и геометрические задачи, которые невозможно решить
без дробных чисел.
Рациональные числа можно представить в виде простой (обыкновенной)
дроби:
m/n
где:
•
m - целое число;
• n - натуральное число, не равное нулю (вы, конечно, помните, что на
нуль делить нельзя!).
Множество рациональных чисел обозначается буквой Q. Если знаменатель дроби равен 1, то вся дробь равна числителю, то есть целому числу n.
Таким образом, все целые числа являются в то же время и рациональными
(множество целых чисел - это подмножество рациональных). Но не наоборот!
Рациональные числа можно представить также в виде конечной десятичной дроби (1/2 = 0,5) или бесконечной периодической десятичной
дроби (1/7 = 0,1428571...).
Иррациональные числа не могут быть представлены в виде простой
дроби (а также в виде конечной или бесконечной десятичной периодической дроби). Таким образом, иррациональным числом называют любое
число, представимое в виде бесконечной непериодической десятичной
дроби. Примером такой дроби служит корень квадратный из двойки. Иррациональность этого числа была известна уже древним математикам, которые доказали несоизмеримость стороны и диагонали квадрата.
14
Иррациональные числа обозначают буквой I.
Множество действительных, или вещественных чисел объединяет множества рациональных и иррациональных чисел. Их принято наглядно
представлять в виде точек на числовой прямой.
Множество действительных чисел обозначают буквой R (от их латинского
названия numerus realis).
К иррациональным числам относятся знаменитые числа - π (пи, отношение длины окружности к диаметру) и е (основание натуральных логарифмов).
В паскале имеется много числовых типов, но большинство из них используется редко. Чаще других в программах на паскале встречаются такие числовые типы данных: integer, int64, real/double, decimal и BigInteger.
На следующей странице вы можете снять учебный стресс, выполнив (если желаете – на время) забавный числовой
тест.
15
Последовательно найдите числа от 1 до 100!
16
С помощью даже простых арифметических действий можно решать интересные задачи. Давайте попробуем!
Проект Тест по арифметике
Исходный код программы находится в файле Тест по арифметике.pas.
Арифметика – она и в Африке арифметика, поэтому мы решим несколько
задач из книги Get Ready! for Standardized Tests Math Grade 3:
Задачи, конечно, очень простые, но они вполне годятся для закрепления
навыков по вычислению выражений, которые встречаются практически в
каждой программе.
Итак, задания первого теста:
17
322 + 409 + 786 + 250
16 + 27 + 87 +34
456 + 985
3626 + 7597
4.36 + 8.98
Как вы видите, все они на сложение. Числа во всех примерах, кроме последнего, - целые. Следовательно, сумма также будет целым числом. Когда нет
веских оснований, для целочисленного результата нужно выбирать тип
integer. В последнем примере оба числа, а значит и сумма имеют тип
double.
Чтобы узнать сумму чисел, можно записать выражение в процедуре печати,
но обычно результат вычислений используют в программе, поэтому мы
присвоим его переменной, значение которой затем напечатаем.
Поскольку никаких сложностей с определением типа результата мы не видим, то можем использовать неявно типизированные переменные:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var sum1 := 322 + 409 + 786 + 250;
var sum2 := 16 + 27 + 87 + 34;
var sum3 := 456 + 985;
var sum4 := 3626 + 7597;
var sum5 := 4.36 + 8.98;
println($'
println($'
println($'
println($'
println($'
println;
end;
322 + 409 + 786 + 250 = {sum1}');
16 + 27 + 87 + 34 = {sum2}');
456 + 985 = {sum3}');
3626 + 7597 = {sum4}');
4.36 + 8.98 = {sum5}');
begin
Writeln(' Тест по арифметике');
Writeln(' Get Ready! for Standardized Tests Math Grade 3');
Writeln;
18
Solve();
end.
Все задачи для третьего класса компьютер решил верно:
Тест по арифметике
Get Ready! for Standardized Tests Math Grade 3
322 + 409 + 786 + 250 = 1767
16 + 27 + 87 + 34 = 164
456 + 985 = 1441
3626 + 7597 = 11223
4.36 + 8.98 = 13.34
Обращает на себя внимание отсутствие примера на сложение целых чисел
с дробными, поэтому мы переделаем ещё один пример из этой книги под
свои нужды:
3626 + 7.597
Понятно, что сумма имеет тип real/double. Это легко проверить, если установить курсор на идентификаторе переменной:
var sum6 := 3626 + 7.597;
println($' 3626 + 7.597 = {sum6}');
На этом первый тест можно считать законченным:
3626 + 7.597 = 3633.597
19
Проект Вычитаем
Исходный код программы находится в файле Вычитаем.pas.
Теперь давайте решим несколько примеров на вычитание, которые для
людей труднее, чем на сложение, а компьютеру – всё едино:
600 – 286
724 − 382
3957 - 9004
23.60 − 9.57
9004 – 3.957
В этих примерах нет ничего нового для нас по сравнению с предыдущей
порцией задач:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var sub1 := 600 - 286;
var sub2 := 724 - 382;
var sub3 := 3957 - 9004;
var sub4 := 23.60 - 9.57;
var sub5 := 9004 - 3.957;
println($'
println($'
println($'
println($'
println($'
println;
end;
600 – 286 = {sub1}');
724 − 382 = {sub2}');
3957 - 9004 = {sub3}');
23.60 − 9.57 = {sub4}');
9004 – 3.957 = {sub5}');
begin
Writeln(' Вычитаем');
Writeln(' Get Ready! for Standardized Tests Math Grade 3');
Writeln;
20
Solve();
end.
Вычитаем
Get Ready! for Standardized Tests Math Grade 3
600 – 286 = 314
724 − 382 = 342
3957 - 9004 = -5047
23.60 − 9.57 = 14.03
9004 – 3.957 = 9000.043
Проект Умножаем
Исходный код программы находится в файле Умножаем.pas.
В книге я нашёл всего один пример на умножение, достойный нашего внимания, поэтому остальные мы «смастерим» из примеров на вычитание:
6 * 537
724 * 382
3957 * 9004
23.60 * 9.57
9004 * 3.957
И опять же достаточно слегка переделать предыдущий проект:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var mul1 := 6 * 537;
var mul2 := 724 * 382;
var mul3 := 3957 * 9004;
21
var mul4 := 23.60 * 9.57;
var mul5 := 9004 * 3.957;
println($'
println($'
println($'
println($'
println($'
println;
end;
6 * 537 = {mul1}');
724 * 382 = {mul2}');
3957 * 9004 = {mul3}');
23.60 * 9.57 = {mul4}');
9004 * 3.957 = {mul5}');
begin
Writeln(' Умножаем');
Writeln(' Get Ready! for Standardized Tests Math Grade 3');
Writeln;
Solve();
end.
Умножаем
Get Ready! for Standardized Tests Math Grade 3
6 * 537 = 3222
724 * 382 = 276568
3957 * 9004 = 35628828
23.60 * 9.57 = 225.852
9004 * 3.957 = 35628.828
Проект Градусник
Исходный код программы находится в файле Градусник.pas и ГрадусникГраф.pas.
Самые интересные примеры, конечно, на деление, но в тестовой книге они
лишком просты даже для компьютера, поэтому мы решим практически
важную задачу:
22
Мы привыкли измерять температуру в градусах Цельсия, а американцы
для этого используют градусы Фаренгейта.
Если известна температура по Цельсию, то для пересчёта температуры в
градусы Фаренгейта можно воспользоваться формулой:
F = C * 9 / 5 + 32
(1)
А вот формула для обратного пересчёта:
C = (F - 32) * 5 / 9
(2)
Температура может выражаться и целыми, и вещественными числами.
Предположим, что мы имеет конкретное значение температуры в градусах
Цельсия. Тогда для вычисления температуры по Фаренгейту мы воспользуемся формулой (1).
Температура может быть представлена и целым, и дробным числом. К тому
же в формуле присутствует вещественное деление /, поэтому результат вычислений – число типа double.
Задавать, конечно, лучше не произвольную температуру в градусах Цельсия, а вполне определённую. Например, температуру замерзания и кипения
воды, а также нормальную температуру человеческого тела. Дополнительно мы узнаем, при какой температуре градусы Цельсия и Фаренгейта
сравняются:
// ПРОГРАММА ДЛЯ ПЕРЕСЧЁТА
// ГРАДУСОВ ЦЕЛЬСИЯ В ГРАДУСЫ ФАРЕНГЕЙТА
// И НАОБОРОТ
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var c := 0;
var f := c * 9 / 5 + 32;
println($' Градусы Цельсия:
{c}');
23
println($' Градусы Фаренгейта: {f}');
println;
c := 100;
f := c * 9 / 5 + 32;
println($' Градусы Цельсия:
{c}');
println($' Градусы Фаренгейта: {f}');
println;
var cd := -40.0;
f := cd * 9 / 5 + 32;
println($' Градусы Цельсия:
{cd}');
println($' Градусы Фаренгейта: {f}');
println;
cd := 36.6;
f := cd * 9 / 5 + 32;
println($' Градусы Цельсия:
{cd}');
println($' Градусы Фаренгейта: {f}');
println;
// 451 градус по Фаренгейту:
var far := 451;
var cel := (far - 32) * 5 / 9;
println($' Градусы Фаренгейта: {far}');
println($' Градусы Цельсия:
{cel}');
println;
end;
begin
Writeln(' Градусник');
Writeln;
Solve();
end.
А вот и ответы на наши вопросы:
Градусник
Градусы Цельсия:
0
Градусы Фаренгейта: 32
24
Градусы Цельсия:
100
Градусы Фаренгейта: 212
Градусы Цельсия:
-40
Градусы Фаренгейта: -40
Градусы Цельсия:
36.6
Градусы Фаренгейта: 97.88
Градусы Фаренгейта: 451
Градусы Цельсия:
232.777777777778
Особенно забавно, что температура тела по Фаренгейту почти 100 градусов.
Очевидно, поэтому все американцы такие горячие парни…
Градусы Фаренгейта в градусы Цельсия нам переводить ни к чему, но одну
температуру в этих градусах должен знать каждый современный человек.
Как сейчас принято говорить, в культовом романе Рэя Бредбери 451 градус
по Фаренгейту, рассказывается о печальном будущем человечества, когда
все книги, заставляющие думать, будут сжигать. 451 градус по Фаренгейту
– это как раз температура воспламенения бумаги.
25
Что касается будущего, то оно уже наступило (роман был впервые издан в
1953 году, так что ждать пришлось недолго). Правда, для этого книги сжигать не пришлось – тут классик жанра ошибся…
А нам только и остаётся, что перевести градусы Фаренгейта в градусы Цельсия:
// 451 градус по Фаренгейту:
var far := 451;
var cel := (far - 32) * 5 / 9;
println($' Градусы Фаренгейта: {far}');
println($' Градусы Цельсия: {cel}');
println;
// 451 градус по Фаренгейту:
var far := 451;
var cel := (far - 32) * 5 / 9;
println($' Градусы Фаренгейта: {far}');
println($' Градусы Цельсия: {cel}');
println;
Хорошо и наглядно показывает взаимоотношения градусов график.
26
Проект Кошки-мышки
Исходный код программы находится в файле Кошки-мышки.pas.
Александр Альбов в интересной и познавательной книге От абака до кубита. История математических символов приводит такую задачу из папируса Райнда:
В семи домах сидят по семи кошек, и каждая поймала семь мышей. Сколько
всего мышей они изловили?
Автор приводит «современный способ решения»: семь возвести в третью
степень. Со школьных времён мы помним квадрат семи - 49; осталось умножить 49 на 7 и получить 343. Египтянам приходилось семь раз сложить 7,
зафиксировать результат 49 и семь раз сложить 49.
27
Египтянам было несладко, а
вот современным школьникам значительно легче.
Если они выучили таблицу
умножения…
А с паскалем нам и вычислять ничего не нужно. Достаточно записать арифметическое выражение и
запустить программу, чтобы получить ответ:
begin
WriteLn(' Кошки-мышки');
WriteLn;
println(7 * 7 * 7);
println;
end.
Кошки-мышки
343
Функция Power возводит первый аргумент в степень, заданную вторым аргументом. Александр Альбов предлагает нам именно этот способ. Возведение в степень в математике записывают так: 73. В паскале и в других языках
программирования для этого используют функции:
println(Power(7, 3));
В паскале имеется замечательная операция возведения в степень две звёздочки, которой пользоваться гораздо удобнее, чем функцией Power:
println(7 ** 3);
28
Проект Число шахматных партий
Исходный код программы находится в файле Число шахматных партий.pas.
Ещё одна задача, тесно связанная с шахматами.
В книге Перельмана Занимательная алгебра рассматривается вопрос о приблизительном подсчёте всех возможных
шахматных партий. При этом автор пользуется способом
бельгийского математика М.Крайчика, изложенным в
книге Математика игр и математические развлечения.
Я не буду пересказывать содержание книги Перельмана, а
сразу оглашу результат:
(20 x 20)5 x (30 x 30)35
Дальше идут трудоёмкие вычисления, которые нам не
нужны, потому что для паскаля эти вычисления – детская задача:
begin
WriteLn(' Число шахматных партий');
WriteLn;
var n1 := (Biginteger(20) * 20) ** 5;
var n2 := (Biginteger(30) * 30) **35;
var n := n1 * n2;
println(n);
println;
end.
Число шахматных партий
2563231237113079399747145713751909376000000000000000000000000000000000000
00000000000000000000000000000000000000000000
29
Это число точное, хотя число партий приблизительное.
В книге Перельмана приводится более грубая оценка: 2 х 10116.
Если вы не поленитесь и пересчитаете все цифры в полученном числе, то их
окажется 117 штук. То есть результат в книге Перельмана верный, но
округлённый.
Только не подумайте, что я воспользовался своим же советом и добросовестно пересчитал цифры. Паскаль и здесь всё сделает за нас, если его правильно попросить:
var nlen := n.ToString.Length;
println(nlen)
Число шахматных партий
117
Проект Какая разница?
Исходный код программы находится в файле Какая разница.pas.
Бен Орлин в книге Math with Bad Drawings. Illuminating the Ideas That Shape
Our Reality приводит показательный пример математического взгляда на
вещи.
Для людей, далёких от математики, выражения x2 и 2x отличаются только
переменой мест буквы х и цифры 2, то есть незначительно. А математики
знают, что разница между этими выражениями огромная.
30
Если х = 10, то значение первого выражения равно:
begin
WriteLn(' Какая разница');
WriteLn;
println(10 ** 2);
println;
end.
Какая разница
100
А второго? – Гораздо больше:
println(2 ** 10);
31
1024
Про быстрое возрастание степеней числа 2 мы уже знаем по шахматной задаче. А Бен Орлин приводит подобный пример для х = 100:
println(100 ** 2);
println;
println(BigInteger(2) ** 100);
10000
1267650600228229401496703205376
Если первое число всё ещё относительно небольшое, то второе – огромное!
Если первое число можно сравнить с весом грузового автомобиля (в фунтах), перевозящего кирпичи, то второе число – это вес ста тысяч Земель.
Проект Складывание бумаги
Исходный код программы находится в файле Складывание бумаги.pas и Складывание бумаги 2.pas.
Как иногда удивительным образом стародавние задачи появляются в новом обличии! Казалось бы, вполне современная головоломка со
складыванием листа бумаги не имеет с шахматной задачей родственных связей, но это не
так. Обе поражающие воображение задачи основаны на свойствах показательной функции.
32
В 28-ом математическом выпуске Академии занимательных наук профессор
Круглов наглядно доказывает хомячку Циркулю, что лист бумаги формата
А4 нельзя сложить вдвое более 6 раз.
Но в Интернете можно найти ролик, в котором показано, как такой же лист
бумаги можно сложить 7 раз.
33
Фокус в том, что сначала листок нужно складывать по длинной стороне, а
не попеременно по длинной – по короткой.
Нашлись умельцы, которые взяли огромный лист бумаги и свернули его 9
раз.
Давайте разбираться, почему бумага так упорно не желает складываться
вдвое!
Так как мы не проводим натурные испытания, то удовольствуемся простой
математической моделью процесса складывания бумаги вдоль и поперёк.
Она несущественно упрощает этот процесс, зато описывается элементарной формулой:
d = a · 2n
d – общая толщина сложенного листа бумаги
а – толщина самого листа
n – число складываний
34
В ролике Разрушители легенд. Сложить бумагу больше 7 раз, который вы
найдёте на Ютубе, разрушительная тройка самозабвенно пытается крепко
смять бумагу более 7 раз.
Возьмём достаточно тонкую бумагу толщиной a = 0.1 мм. В начале эксперимента весь «свёрток» имеет такую же толщину:
##
function Solve(a:real; n: integer):= a * 2 ** n;
WriteLn(' Складывание бумаги');
WriteLn;
println(' ' + Solve(0.1, 0));
println;
Складывание бумаги
0.1
35
Пока всё нормально.
После трёх складываний толщина сложенного листа бумаги всё ещё
меньше 1 миллиметра:
println(' ' + Solve(0.1, 1));
println(' ' + Solve(0.1, 2));
println(' ' + Solve(0.1, 3));
0.2
0.4
0.8
Но при последующих складываниях толщина листа стремительно растёт:
println('
println('
println('
println;
println('
println('
println('
println;
println('
println('
println('
println;
println('
println('
println;
' + Solve(0.1, 4));
' + Solve(0.1, 5));
' + Solve(0.1, 6));
' + Solve(0.1, 7));
' + Solve(0.1, 8));
' + Solve(0.1, 9));
' + Solve(0.1, 10));
' + Solve(0.1, 11));
' + Solve(0.1, 12));
' + Solve(0.1, 13));
' + Solve(0.1, 14));
1.6
3.2
6.4
12.8
25.6
51.2
36
102.4
204.8
409.6
819.2
1638.4
После 9 складываний толщина достигнет 5 сантиметров, а после 12 – почти
410. Теперь легко представить размеры исходного листа бумаги, чтобы его
можно было сложить пополам при такой толщине.
Если конечная площадь верхней поверхности листа 40 квадратных сантиметров, то исходный лист при 12 складываниях должен быть в 4096 раз
больше, то есть 163840 квадратных сантиметров, или 16,4 квадратных
метра. На самом деле площадь исходного листа должна быть значительно
больше, поскольку мы складываем не отдельные листы стопкой, а единственный лист, часть которого образует боковые поверхности. Также мы
предполагаем, что лист размером 40 х 80 сантиметров толщиной 40 сантиметров ещё можно сложить вдвое.
Но если 12 раз сложить вдвое огромный лист тонкой (!) бумаги ещё вполне
возможно, то дальше толщина листа измеряется метрами:
println(' ' + Solve(0.1, 15));
println(' ' + Solve(0.1, 16));
println(' ' + Solve(0.1, 17));
println;
println(' ' + Solve(0.1, 18));
println(' ' + Solve(0.1, 19));
println(' ' + Solve(0.1, 20));
println;
3276.8
6553.6
13107.2
26214.4
37
52428.8
104857.6
Здесь мы наблюдаем ту же самую картину, что и при выкладывании зёрен
на шахматную доску. Толщина листа бумаги растёт так быстро, что даже 15
складываний проделать не удастся.
В книге Вальтера Литцмана Великаны и карлики в мире чисел, пятое издание
которой вышло в Лейпциге в 1953 году, эксперимент со складыванием бумаги приводится как пример быстрого роста показательной функции. При
складывании листа бумаги толщиной 1/10 мм 40 раз высота «стопки» превысит 100 000 километров!
При желании охоты вы можете складывать бумагу стопками в интерактивном режиме:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var tolshinaLista := 0.1;
var nSklad := 0;
while True do begin
Write (' Число складываний: ');
Read(nSklad);
if (nSklad < 0) then
break;
var tolshina := tolshinaLista * Power(2, nSklad);
Writeln ($' Общая толщина: {tolshina}');
Writeln ();
end;
Writeln;
end;
begin
Writeln(' Складывание бумаги');
Writeln;
Solve();
end.
Занятие это полезное и необременительное:
38
Складывание бумаги
Число складываний: 11
Общая толщина: 204.8
Число складываний:
Проект Какая разница для любознательных
Исходный код программы находится в файле Какая разница для любознательных.pas.
Числа хороши для людей с обострённым абстрактным мышлением. Поэтому мы прибегнем к построению графиков, чтобы добавить наглядности
к нашим рассуждениям.
Нас интересуют две функции:
1. Экспоненциальная: g(x) = 2ˣ
2. Квадратичная: f(x) = x²
Мы убедились, что при больших значениях x экспоненциальная функция
существенно больше.
Я не буду утомлять вас исходным кодом программы для построения графиков функций. Давайте сразу обратимся к самим графикам (Рис. 1).
Из графиков хорошо видно:
• При малых x (x < 2) значения близки
• При x = 2 и x = 4 функции равны
• При x > 4 экспонента обгоняет квадратичную функцию
Такое поведение функций необходимо учитывать в алгоритмах!
Экспоненциальный рост (2ˣ) приводит к огромному перебору значений.
39
Квадратичный рост (x²) вполне годится даже для больших переборов.
• Если алгоритм работает за O(n²), то при n=1000 потребуется примерно
1 000 000 операций
• Если алгоритм работает за O(2ⁿ), то при n=1000 потребуется примерно
1.07e+301 операций, что совершенно неприемлемо!
Рис. 1
В этой книге нам придётся основательно
заниматься перебором, поэтому важно
сразу оценить затраты времени на перебор, чтобы принять обоснованное решение – оптимизировать алгоритм, или и
так сойдёт.
Знакомые нам функции дают такие рекомендации:
40
• Алгоритмы с O(n²) работают приемлемо до 10 000 элементов
• Алгоритмы с O(2ⁿ) становятся непригодными уже при n > 20-30
• Поиск с перебором O(2ⁿ) не годится для больших данных
• Квадратичные алгоритмы с O(n²) работают медленно
• Лучшие алгоритмы имеют сложность O(n log n) или O(n)
===============================================
Размер данных |
O(n²) |
O(2ⁿ)
----------------------------------------------1 |
0.1 |
0.0
10 |
10.0 |
10.2
100 |
1,000.0 | Астрономическое
1,000 |
100,000.0 | Астрономическое
Проект Три цифры - раз
Исходный код программы находится в файле Три цифры - раз.pas.
Яков Перельман в книге Занимательная арифметика
предлагает решить Задачу №58:
Какое самое большое число можно написать тремя
цифрами, не употребляя никаких знаков действий?
В качестве цифр хочется использовать три девятки – и
этот выбор верный. Второй шаг – правильно составить
из этих девятое число.
Например, так:
##
WriteLn(' Три цифры - раз');
WriteLn;
41
println(BigInteger(99) ** 9);
println;
println(BigInteger(9) ** 99);
println;
Три цифры - раз
913517247483640899
2951266543065275214875348022619773631435927251704383288606388463767694343
3478020332709411004889
Возможно, вы придумаете и другие способы для записи чисел, но всё равно
самое большое число, которое можно записать тремя девятками -9 в степени 9 в степени 9:
Чтобы легче было соображать, вычислим степень 99:
println(BigInteger(9) ** 9);
387420489
Тогда исходное выражение можно записать так:
9387420489 = ???
42
Если все предыдущие расчёты легко выполнить на паскале (что я и сделал),
то последнее число даже не пытайтесь вычислять! Оно состоит из
369 693 100 цифр. Если печатать по 3000 цифр на странице, потребуется
123 231 страница. Это примерно 246 книг по 500 страниц каждая. Время
чтения такого числа (1 цифра в секунду) составляет 11.7 лет. Размер файла
с этим числом (UTF-8): ~370 МБ. Число всех частиц в наблюдаемой Вселенной примерно равно 10^80. Наше число 9^9^9 ≈ 10^(3.7×10^8) - намного
больше!
Более подробно об этом числе вы можете прочитать в указанной выше
книге.
С появлением стрелочной нотации Кнута и гипероператоров можно получить ещё более огромные числа:
9↑↑3 = 9^9^9 ≈ 10^(3.7×10⁸), но 9↑↑↑3 - невообразимо больше.
Проект Три цифры - два
Исходный код программы находится в файле Три цифры - два.pas.
Эта задача нашла продолжение в другой книге Перельмана – Занимательная алгебра. Но здесь задачи проще.
В первой задаче требуется, не употребляя знаков действий, записать тремя двойками наибольшее число.
«Подвох» задачи заключается в невольном переносе решения задачи с девятками на задачу с двойками.
Но получается совсем небольшое число:
43
##
WriteLn(' Три цифры - два');
WriteLn;
println(2 ** 2 ** 2);
println;
Три цифры - два
16
Маловато будет!
Даже «простое» число 222 заметно больше.
Следующий претендент на лавры победителя – число 222:
println(22 ** 2);
println;
484
Но и оно сравнительно невелико.
44
А правильный ответ такой:
println(2 ** 22);
println;
4194304
Во второй задаче место двоек занимают тройки.
Здесь двухэтажная степень даёт хороший результат:
println(3 ** 3 ** 3);
println;
7625597484987
Но не лучший! А побеждает опять одноэтажная степень:
println(BigInteger(3) ** 33);
println;
5559060566555523
В этой задаче можно обойтись вообще без вычислений, если
заметить, что 3 в кубе – это 27, то есть показатель
степени в первом случае меньше, чем во втором (33).
Третья задача продолжает этот ряд, и теперь нужно составить наибольшее
число из трёх четвёрок.
45
Так как 44 = 256, то двухъярусная степень значительно больше одноярусной с показателем степени 44:
println(BigInteger(4) ** 44);
println;
println(BigInteger(4) ** 256);
println;
309485009821345068724781056
1340780792994259709957402499820584612747936582059239337772356144372176403
0073546976801874298166903427690031858186486050853753882811946569946433649
006084096
Читая книгу дальше, вы узнаете, почему для чисел 2 и 3 выгоднее одноярусная степень, а для больших двухъярусная.
История с тремя одинаковыми цифрами продолжилась в интересной математической книге для школьников.
46
На странице 30 дана для ручных вычислений вот такая задача.
Здесь авторы книги почему-то нарушили логику и вместо числа 44 употребили число 40. Впрочем, суть задачи от этого не пострадала:
println(40 ** 4);
println(BigInteger(4) ** 40);
println(BigInteger(4) ** 256);
println(4 * 4 * 4);
println((4 ** 4) ** 4);
println;
2560000
1208925819614629174706176
1340780792994259709957402499820584612747936582059239337772356144372176403
0073546976801874298166903427690031858186486050853753882811946569946433649
006084096
64
4294967296
47
Проект Гугол
Исходный код программы находится в файле Гугол.pas.
Число, которое изображается единицей с сотней нулей, называется гуголом:
10 000 000 000 000 000 000 000 000 000 000 000 000
000 000 000 000 000 000 000 000 000 000 000 000 000
000 000 000 000 000 000 000 000
Мы легко получим его в программе на паскале:
##
WriteLn(‚ Гугол‘);
WriteLn;
println(BigInteger(10) ** 100);
println;
Гугол
1000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000
Но интересно происхождение названия этого числа. Его придумали не математики, а племянник американского математика Эдварда Каснера в 1938
году. Племянника звали Милтон Сиротта, и тогда ему было 9 лет.
Эдвард Каснер прогуливался со своими племянниками по парку, и речь у
них зашла о числе, у которого 100 нулей. Число такое большое, что им нечего обозначить. Именно поэтому оно не имело собственного названия. Маленький Милтон предложил назвать его гуголом.
48
В 1940 году Эдвард Каснер и Джеймс Ньюмен написали книгу Mathematics
and the Imagination (Математика и воображение), в которой и рассказали об этом числе. Так
число получило своё имя!
По-английски название гугол пишется как
googol. Название известной поисковой машины
и компании Google произошло от названия этого
числа.
А правильное математическое название этого
числа:
десять дуотригинтиллионов или десять се-
дециллиардов.
По той же легенде, племянник придумал название гуголплекс (googolplex) для числа, у которого гугол нулей после единицы. Понятно, что
это название – просто курьёз, потому что в природе нет ничего, что можно было бы обозначить
таким числом.
Проект Вычисление сложных арифметических выражений
Исходный код программы находится в файле Вычисление сложных
арифметических выражений.pas.
Арифметические действия выполняются согласно их приоритетам. Старшинство скобок самое высокое. Затем идёт возведение в степень, а после
него умножение, деление и деление по модулю. В последнюю очередь
выполняют действия сложения и вычитания. Действия с одинаковым
приоритетом выполняются последовательно слева направо (кроме возведения в степень).
49
Понятно, что паскаль сумеет вычислить любое арифметическое выражение, которое задают в школе, поэтому придумаем для него задание сложнее:
(67 + 7 * 23) – 222 * 12
В программировании, как и в математике, не принято целиком вычислять
длинные, сложные выражения, поэтому будем вычислять по частям.
Для выражения в скобках (67 + 7 * 23) последовательность действий такая:
1. Двойку возводим в куб. Получаем 8
2. 7 умножаем на 8. Получаем 56
3. Находим сумму чисел 67 и 56. Получаем 123
Всё верно:
##
WriteLn(' Вычисление сложных арифметических выражений');
WriteLn;
println(67 + 7 * 2 ** 3);
println;
Вычисление сложных арифметических выражений
123
Вычисляем выражение 222 * 12:
1. Возводим 22 в квадрат. Получаем 484
2. Умножаем 484 на 12. Получаем 5808
Тоже всё верно:
println(22 ** 2 * 12);
50
println;
5808
Теперь можно вычислить и всё выражение целиком:
println(123 - 5808);
println;
-5685
Компьютер – хороший помощник в математике, но не всегда он бывает под
рукой. А в некоторых случаях при расчётах вполне можно обойтись и без
него. Если знать некоторые математические трюки и хитрости. Например,
произведение некоторых чисел можно более простым способом, чем
обычно.
Проект Громоздкое выражение
Исходный код программы находится в файле Громоздкое выражение.pas.
А сейчас мы решим задачу, абсолютно оторванную от жизни. Ни в какой реальной программе вы не столкнётесь с таким выражением:
51
Я нашёл его в книге Практикум программирования на Turbo Pascal.
Тип результата здесь очевиден – это double. Но эта задача хороша тем, что
показывает, как использовать при вычислении сложных выражений вспомогательные переменные.
Никогда не вычисляйте длинные выражения в одну строку! Написать её без
ошибок очень трудно, а найти ошибки – ещё труднее. Разбивайте выражение на составные части, находите промежуточные значения и сохраняйте
их во вспомогательных переменных.
В предложенном примере числитель довольно прост, поэтому мы можем
целиком вычислить его:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var a := (7 - 6.35) / 6.5 + 9.9;
Знаменатель разумно разбить на 2 части:
var b := 1.2 / 36 + 1.2 / 0.25 - (1 + 5 / 16);
var c := 7 + 1 / 24;
Теперь мы без труда решим задачу:
var res := a / (b / c);
println($' Ответ: {res}');
println;
end;
begin
Writeln(' Громоздкое выражение');
Writeln;
Solve();
end.
52
Судя по ответу, числа для выражения взяты не с потолка, а тщательно подобраны:
Громоздкое выражение
Ответ: 20
Проект Вспомогательные переменные
Исходный код программы находится в файле Вспомогательные переменные.pas.
Очень часто вспомогательную переменную используют при обмене значениями двух переменных.
Пусть это будут переменные a = 1 и b = 2. После обмена их значения должны
стать такими: a = 2, b = 1.
Понятно, что если мы совершим обмен наивным способом:
a := b;
b := a;
то обе переменные получат значение переменной b, поскольку переменная
а потеряет своё значение после первого присваивания. Это значит, что перед этим его нужно сохранить во вспомогательной (или временной,
temporal; отсюда обычное имя для таких переменных - tmp).
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var a := 1;
var b := 2;
println($' a = {a}
b = {b}');
53
// вспомогательная переменная:
var tmp := a;
// обмениваем значения переменных a и b:
a := b;
b := tmp;
println($' a = {a} b = {b}');
println;
end;
begin
Writeln(' Вспомогательные переменные');
Writeln;
Solve();
end.
Со вспомогательной переменной обмен выполнен верно:
Вспомогательные переменные
a = 1
a = 2
b = 2
b = 1
Использование кортежей упрощает задачу по обмену значениями:
procedure Solve2();
begin
var a := 1;
var b := 2;
println($' a = {a}
(a,b) := (b,a);
println($' a = {a}
println;
end;
b = {b}');
b = {b}');
Более сложная обменная задача: циклически обменять значения трёх переменных.
54
На первый взгляд может показаться, что здесь не обойтись без двух вспомогательных переменных, но на самом деле достаточно и одной:
procedure Swap3();
begin
var a := 1;
var b := 2;
var c := 3;
println($' a = {a}
b = {b}
c = {c}');
// вспомогательная переменная:
var tmpa := a;
// обмениваем значения переменных a, b и с:
a := c;
c := b;
b := tmpa;
println($' a = {a} b = {b} c = {c}');
println;
end;
Чтобы не загромождать основную программу, мы совершим обмен в отдельной процедуре Swap3 (swap – обмен), а в основной программе только
вызовем её:
begin
Writeln(' Вспомогательные переменные');
Writeln;
//Solve();
//Solve2();
Swap3();
end.
И второй обмен исполнен без обмана:
Вспомогательные переменные
a = 1
a = 3
b = 2
b = 1
c = 3
c = 2
55
И здесь кортежи показали себя во всей красе:
procedure Solve3();
begin
var a := 1;
var b := 2;
var c := 3;
println($' a = {a}
b = {b}
(a,b,c) := (c,a,b);
println($' a = {a} b = {b}
println;
end;
c = {c}');
c = {c}');
Ещё одна известная задача со вспомогательными переменными - возведение в степень с наименьшим числом умножений.
Опять выносим все действия в процедуру Power. Значение переменной a
можно задать любым, потому что оно нас в этой задаче не интересует.
Во вспомогательную переменную tmpa помещаем a2. После первого умножения переменной tmpa на себя её значение станет равным a4, а после второго – а8:
procedure Power();
begin
var a := 11;
// вспомогательная переменная:
var tmpa := a*a;
// вычисляем 8-ю степень:
tmpa *= tmpa;
tmpa *= tmpa;
println($' a = {a}
end;
a8 = {tmpa}');
Итак, нам понадобилось всего 3 умножения, чтобы возвести а в восьмую
степень:
56
Вспомогательные переменные
a = 11
a8 = 214358881
Если значение переменной сохранять не нужно, то можно обойтись и без
вспомогательной переменной:
a *= a;
a *= a;
a *= a;
При наивном решении задачи потребовалось бы 7 умножений:
var tmpa := a*a*a*a*a*a*a*a;
Некоторые числа можно с лёгкой элегантностью умножать прямо в уме!
Умножение чисел a5 x b5
Посмотрите на таблицу умножения двузначных чисел, оканчивающихся на 5:
15 x 15 = 225
15 x 25 = 375
15 x 35 = 525
15 x 45 = 675
15 x 55 = 825
15 x 65 = 975
15 x 75 = 1125
15 x 85 = 1275
25 x 25 = 625
25 x 35 = 875
25 x 45 = 1125
25 x 55 = 1375
25 x 65 = 1625
25 x 75 = 1875
25 x 85 = 2125
35 x 35 = 1225
35 x 45 = 1575
35 x 55 = 1925
35 x 65 = 2275
35 x 75 = 2625
35 x 85 = 2975
57
45 x 45 = 2025
45 x 55 = 2475
45 x 65 = 2925
45 x 75 = 3375
45 x 85 = 3825
55 x 55 = 3025
55 x 65 = 3575
55 x 75 = 4125
55 x 85 = 4675
75 x 75 = 5625
75 x 85 = 6375
85 x 85 = 7225
65 x 65 = 4225
65 x 75 = 4875
65 x 85 = 5525
Пусть a – число десятков первого числа, а b – число десятков второго числа.
Тогда:
- если сумма десятков чётная, то произведение закачивается на 25, а
число сотен = a x b + (a + b) : 2
- если сумма десятков нечётная, то произведение закачивается на 75, а
число сотен = a x b + (a + b – 1) : 2
Сначала вам этот способ умножения может показаться сложным, но небольшая тренировка – и вы сможете легко перемножать такие числа в уме.
Проект Умножение чисел, близких к 100
Исходный код программы находится в файле Умножение чисел,
близких к 100.pas.
Если оба сомножителя отличаются от 100 не более чем на 10, то их легко
перемножить даже в уме.
Рассмотрим примеры.
1. Оба числа меньше 100.
93 х 98
58
Число 93 на 7 меньше 100, а число 98 – на 2. Разности нужно записать со
знаком минус так:
93 -07
98 -02
Последние 2 цифры результата равны произведению чисел 2 и 7:
2 х 7 = 14
Чтобы найти первые 2 цифры, нужно из числа 93 вычесть 2 или из числа 98
вычесть 7. Разность будет одинаковой - 91, поэтому выбирайте тот способ,
который вам больше нравится. При нахождении разности уменьшаемое –
это один из сомножителей, а вычитаемое – то число, которое находится по
диагонали от него.
Итак, произведение – это 4-значное число, первые 2 цифры которого – 91, а
последние 2 – 14:
93 х 98 = 9114
Новый способ умножения проверяем на компьютере:
##
WriteLn(' Умножение чисел, близких к 100');
WriteLn;
println(93 * 98);
println;
59
Умножение чисел, близких к 100
9114
Результат сходится.
2. Оба числа больше 100.
109 х 101
Первый сомножитель на 9 больше 100, а второй – на 1. Записываем эти разности со знаком плюс:
109 + 09
101 + 01
Последние 2 цифры результата равны произведению разностей:
9 х 1 = 09
Первые 3 цифры результата равны 109 + 1 или 101 + 9 = 110:
109 х 101 = 11009
Проверка на компьютере показывает, что вычисления верные:
println(109 * 101);
println;
11009
Этот способ работает и тогда, когда сомножители отличаются от 100
больше, чем на 10. Но тогда придётся перемножать двузначные числа:
81 x 91 →
60
81 -19
91 -09
Последние 2 цифры произведения - 71:
19 х 9 = 171
Так как при перемножении разностей получилось 3-значное число, то первая цифра (1) – это перенос в разряд сотен.
Первые 2 цифры:
81 – 9 = 91 – 19 = 72
Записываем результат, добавляя к сотням 1:
81 x 91 = 7371
Компьютер подтверждает правильность решения:
println(81 * 91);
println;
7371
3. Одно число меньше 100, а второе - больше.
95 х 104
Первый сомножитель на 5 меньше 100, а второй – на 4 больше. Первую разность записываем с минусом, а вторую – плюсом:
95 -05
104 +04
Произведение разностей отрицательное:
61
-5 х 4 = -20
Поэтому мы должны занять 1 из разряда сотен, и тогда последние 2 цифры
равны:
100 – 20 = 80
Первые 2 цифры вычисляем, как обычно, но не забываем вычесть 1:
95 + 4 – 1 = 104 – 5 – 1 = 98
Окончательный результат:
95 х 104 = 9880
Проверяем на компьютере:
println(95 * 104);
println;
9880
И опять всё посчитано без ошибок!
Эти способы можно использовать, когда оба сомножителя близки к 1000,
10000 и так далее.
Проект Умножение близких чисел
Исходный код программы находится в файле Умножение близких
чисел.pas.
62
Если сумма единиц двух сомножителей равна 10, а остальные цифры одинаковы, то произведение легко найти.
Пусть нам нужно найти такое произведение:
48 х 42
Последние 2 цифры результата равны произведению последних цифр
сомножителей:
8 х 2 = 16
Первые 2 цифры равны произведению первой цифры на число, которое на
1 больше, то есть:
4 х (4 + 1) = 20
Значит,
48 х 42 = 2016
Проверяем на компьютере:
##
WriteLn(' Умножение близких чисел');
WriteLn;
println(48 * 42);
println;
Умножение близких чисел
2016
Более сложный пример:
296 х 294
63
Последние 2 цифры:
6 х 4 = 24
Первые цифры равны:
29 х (29 + 1) = 870
Пример решён:
296 х 294 = 87024
Обязательная проверка на компьютере:
println(296 * 294);
println;
87024
Как и в любом другом деле, при таких вычислениях нужна тренировка, но
зато потом вы сможете удивлять своим мастерством одноклассников, друзей и родителей!
Восемь восьмёрок для любознательных
Исходный код программы находится в файлах Восемь восьмёрок.pas,
и Восемь восьмёрок 2.pas.
Самая известная восьмёрка – это женский праздник Восьмое марта. Менее
известны велосипедные восьмёрки, крендель и лежачая восьмёрка – знак
бесконечности.
64
С 19-го века известна головоломка: как из восьми восьмёрок и знаков сложения составить выражение, значение которого равно 1000?
Решение этой головоломки давно не секрет: 888 + 88 + 8 + 8 + 8 = 1000.
Интересное наблюдение: 8 = 1000 в двоичной системе счисления! Получаем
вот такое курьёзное решение:
810 = 10002
Других «нормальных» решений мы не наблюдаем, но, возможно, они есть.
Пусть у нас есть последовательность из восьми восьмёрок – 88888888. Мы
должны разбить её на группы и поставить между ними знак плюс.
Известному решению (888, 88, 8, 8, 8) соответствует разбиение на длины
[3, 2, 1, 1, 1].
В программе мы:
65
• генерируем все композиции числа 8 (списки длин групп)
• для каждой композиции строим выражение: группы из восьмёрок
превращаем в числа и складываем
• если сумма равна 1000, то сохраняем в списке, а затем печатаем на
экране:
function Solve: List<string>;
begin
Result := new List<string>;
for var mask := 0 to 127 do begin // 2^7 - 1
var expr := '';
var sum := 0;
var current := 0;
// первая цифра всегда 8:
current := 8;
for var i := 1 to 7 do begin
if ((mask shr (i-1)) and 1) = 1 then begin
// если стоит плюс, добавляем текущее число к сумме
sum += current;
expr += current.ToString + ' + ';
// начинаем новое число:
current := 8;
end
else begin
// если плюса нет, добавляем цифру к текущему числу:
current := current * 10 + 8;
end;
end;
// добавляем последнее число
sum += current;
expr += current.ToString;
if sum = 1000 then
Result.Add(expr);
end;
end;
begin
66
WriteLn(' Восемь восьмёрок');
WriteLn;
var solutions := Solve;
Writeln(' Решений: ', solutions.Count);
foreach var s in solutions do
Writeln(' ' + s);
end.
И вот что у нас получилось:
Восемь восьмёрок
Решений: 20
8 + 8 + 8 + 88 + 888
8 + 8 + 88 + 8 + 888
8 + 88 + 8 + 8 + 888
88 + 8 + 8 + 8 + 888
8 + 8 + 8 + 888 + 88
8 + 8 + 888 + 8 + 88
8 + 888 + 8 + 8 + 88
888 + 8 + 8 + 8 + 88
8 + 8 + 88 + 888 + 8
8 + 88 + 8 + 888 + 8
88 + 8 + 8 + 888 + 8
8 + 8 + 888 + 88 + 8
8 + 888 + 8 + 88 + 8
888 + 8 + 8 + 88 + 8
8 + 88 + 888 + 8 + 8
88 + 8 + 888 + 8 + 8
8 + 888 + 88 + 8 + 8
888 + 8 + 88 + 8 + 8
88 + 888 + 8 + 8 + 8
888 + 88 + 8 + 8 + 8
Если не обращать внимания на последовательность чисел, то решение
единственное.
Вторая программа находит такие решения:
67
Восемь восьмёрок 2
Решений: 3
8 + 8 + 8 + 88 + 888
8 + 8 * 8 - 8 - 8 + 888
8 / 8 + 8 + 8 * 8 * 8 - 88
Известны более хитроумные решения этой задачи, но с нарушениями правил:
(8 + 8 + 8) * 8 * 8 - 8 - 8 - 8 = 1000
8888 / 8.888 ≈ 1000
Приложив руки к голове, мы найдём ещё пару-тройку «неправильных решений»:
(8 + 8) * 8 * 8 - 8 - 8 - 8 = 1000
(8888 - 888) / 8 = 1000
8 * (8 * 8 + 8 * 8) - 8 - 8 - 8 = 1000
(888 + 8) / 8 + 888 = 1000
(8 + 8) * 8 - 8 - 8 + 888 = 1000
Идя по этому тернистому пути дальше, мы найдём несколько решений для
девяти девяток:
999 + 999/999 = 1000
999 + 99/99 + 9 – 9
Для девяти единиц:
1111 - 111 + 1 – 1= 1000
(1111 - 111) * (1 + 1 - 1)
Я надеюсь, что вы проверяете эти равенства!
68
Проект Игра 24 для любознательных
Исходный код программы находится в файле Игра 24.pas
Игра 24 – это известная математическая головоломка.
Даны 4 числа - обычно от 1 до 9, - одинаковые или разные. Используя операции + - × ÷ и скобки, нужно получить 24. Каждое число используется однократно.
Пример для чисел 1, 2, 3, 4:
(1 + 2 + 3) × 4 = 24
1 × 2 × 3 × 4 = 24
Решения для четырёх одинаковых цифр:
((3 * 3) * 3) - 3 = 24
(3 * (3 * 3)) - 3 = 24
4 + (4 + (4 * 4)) = 24
(4 + 4) + (4 * 4) = 24
(4 + (4 * 4)) + 4 = 24
4 + ((4 * 4) + 4) = 24
((4 * 4) + 4) + 4 = 24
(4 * 4) + (4 + 4) = 24
(5 * 5) - (5 / 5) = 24
((6 + 6) + 6) + 6
(6 + (6 + 6)) + 6
6 + ((6 + 6) + 6)
6 + (6 + (6 + 6))
(6 + 6) + (6 + 6)
(6 * 6) - (6 + 6)
((6 * 6) - 6) - 6
=
=
=
=
=
=
=
24
24
24
24
24
24
24
69
Программа Игра 24.pas поможет вам найти решения для заданных четвёрок чисел.
Некоторые задачи для упражнений в смекалке:
Числа: 6, 7, 8, 9
Решений: 8
(8 / (9 - 7)) * 6
8 / ((9 - 7) / 6)
6 * (8 / (9 - 7))
(6 * 8) / (9 - 7)
8 * (6 / (9 - 7))
(8 * 6) / (9 - 7)
(6 / (9 - 7)) * 8
6 / ((9 - 7) / 8)
=
=
=
=
=
=
=
=
24
24
24
24
24
24
24
24
Числа: 4, 5, 8, 8
Решений: 6
4 * (5 + (8 / 8))
(5 - (8 / 4)) * 8
4 * ((8 / 8) + 5)
((8 / 8) + 5) * 4
(5 + (8 / 8)) * 4
8 * (5 - (8 / 4))
=
=
=
=
=
=
24
24
24
24
24
24
Числа: 1, 1, 6, 9
Решений: 4
6 + ((1 + 1) * 9)
(9 * (1 + 1)) + 6
((1 + 1) * 9) + 6
6 + (9 * (1 + 1))
=
=
=
=
24
24
24
24
Числа: 3, 3, 6, 6
Решений: 4
3 * ((6 / 3) + 6)
3 * (6 + (6 / 3))
((6 / 3) + 6) * 3
(6 + (6 / 3)) * 3
=
=
=
=
24
24
24
24
70
Числа: 4, 7,
Решений: 4
(7 - 4) * (1
(7 - 4) * (7
(7 + 1) * (7
(1 + 7) * (7
1, 7
+
+
-
7)
1)
4)
4)
=
=
=
=
24
24
24
24
Числа: 5, 3, 3, 7
Решений: 4
3 * ((5 * 3) - 7)
3 * ((3 * 5) - 7)
((3 * 5) - 7) * 3
((5 * 3) - 7) * 3
=
=
=
=
24
24
24
24
Числа: 5, 9,
Решений: 4
(9 - 5) * (7
(5 - 9) * (1
(1 - 7) * (5
(7 - 1) * (9
7, 1
=
=
=
=
24
24
24
24
Числа: 9, 6,
Решений: 4
(9 - 6) * (7
(9 - 6) * (1
(7 + 1) * (9
(1 + 7) * (9
7, 1
=
=
=
=
24
24
24
24
-
+
+
-
1)
7)
9)
5)
1)
7)
6)
6)
Числа: 7, 3, 7, 9
Решений: 2
3 * (9 - (7 / 7)) = 24
(9 - (7 / 7)) * 3 = 24
Числа: 9, 8, 9, 4
Решений: 2
8 * (4 - (9 / 9)) = 24
(4 - (9 / 9)) * 8 = 24
71
Числа: 4, 10, 4, 10
Решений: 1
((10 * 10) - 4) / 4 = 24
Числа: 5, 10, 5, 10
Решений: 1
(5 * 5) - (10 / 10) = 24
Проект Четыре четвёрки для любознательных
Исходный код программы находится в файлах Четыре четвёрки.pas.
Четыре четырки, две растопырки.
Загадка
Четыре четвёрки (Four fours puzzle) тоже известная математическая головоломка.
72
Для каждого целого числа n, начиная с 0 или 1, найти выражение ровно из
четырёх цифр 4, которое равно n, используя определённый набор математических операций.
Если использовать только арифметические операции + - × ÷, то непрерывный ряд заканчивается на десятке:
0
1
2
3
4
5
6
7
8
9
=
=
=
=
=
=
=
=
=
=
(4-(4+(4-4)))
(4÷((4-4)+4))
(4-((4+4)÷4))
((4+(4+4))÷4)
(((4-4)×4)+4)
(((4×4)+4)÷4)
(4+((4+4)÷4))
(4+(4-(4÷4)))
(4-((4-4)-4))
((4+(4÷4))+4)
Добавим к ним факториал, корень, конкатенацию и десятичную точку.
С ними мы доберёмся до числа 72:
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
(4÷(4-(4-.4)))
((4÷4)+(4÷.4))
(4×(4-(4÷4)))
(4+((4-.4)÷.4))
((4×(4-.4))-.4)
((4×4)-(4÷4))
(4+(4+(4+4)))
((4×4)+(4÷4))
(4+(4+(4÷.4)))
((4+(4-.4))÷.4)
(4×(4+(4÷4)))
((4+(4+.4))÷.4)
(4+(sqrt(4)+(4×4)))
((4÷(.4×.4))-sqrt(4))
(4+(4+(4×4)))
((4+(4+sqrt(4)))÷.4)
((4×4)+(4÷.4))
(sqrt(4)+(4÷(.4×.4)))
((4×(4+4))-4)
(4+(4÷(.4×.4)))
((4+(4+4))÷.4)
((4+(sqrt(4)÷.4)!)÷4)
((4×4)+(4×4))
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
((.4+(4×4))÷.4)
(sqrt(4)+(4×(4÷.4)))
((sqrt(4)×4!)-(sqrt(4)÷.4))
(4+(4×(4÷.4)))
((sqrt(4)+(4×4))÷.4)
(((4!-4)÷.4)-4)
((sqrt(4)×4!)-(4÷4))
(4×(4+(4+4)))
((4!-(4+.4))÷.4)
((4+(4×4))÷.4)
((.4-(4-4!))÷.4)
((4!÷.4)-(4+4))
(((4!-sqrt(4))÷.4)-sqrt(4))
(4-((4-4!)÷.4))
(((4!-.4)÷.4)-4)
(4×(4+(4÷.4)))
(((.4+4!)÷.4)-4)
((4!-(.4+.4))÷.4)
((4!÷.4)-(4÷4))
((4×(4×4))-4)
((4÷4)+(4!÷.4))
((4×(4×4))-sqrt(4))
(4-((.4-4!)÷.4))
73
33 = (4!+((4-.4)÷.4))
34 = (sqrt(4)+(4×(4+4)))
35 = ((4+(4÷.4))÷.4)
36 = (4+(4×(4+4)))
37 = (((.4+4!)÷.4)-4!)
38 = ((4×(4÷.4))-sqrt(4))
39 = (((4×4)-.4)÷.4)
40 = (4×(4+(4+sqrt(4))))
64
65
66
67
68
69
70
71
72
=
=
=
=
=
=
=
=
=
((4+4)×(4+4))
(4+((.4+4!)÷.4))
(sqrt(4)+(4×(4×4)))
(sqrt(4)+((sqrt(4)+4!)÷.4))
(4+(4×(4×4)))
((4-(.4-4!))÷.4)
((4÷.4)+(4!÷.4))
((4+(.4+4!))÷.4)
(4×(sqrt(4)+(4×4)))
Приобщаем к решению операции floor и ceil, а также периодические
дроби и добираемся до двух сотен. Можно пойти дальше, но это уже чрезмерная нагрузка на мозговой аппарат.
116 = (4÷4)(4×4)
73 = ⌈((4×4)4×.4~)⌉
117 = (⌈(4+.4)⌉√4÷.4~)
74 = ⌊(4-(.4-4))⌋4
118 = (4!+(4÷.4~)4)
75 = ⌈((.4~×4√4)×4)⌉
119 = ⌈(4√4×√((4+4)))⌉
76 = (4×⌊(44×.4~)⌋)
120 = (4(4+4)÷.4)
77 = ⌈(⌊(4-.4)⌋4÷.4~)⌉
121 = ⌊(⌈(.4+4)⌉4÷.4~)⌋
78 = ((4+4)√4-4)
122 = (4+(4+4))√4
79 = ⌈((.4~×44)×4)⌉
123 = ⌈(4(4÷.4~)÷.4)⌉
80 = ((4+4)4-4)
124 = ((4+4)+4)4
81 = (4+4)(4÷4)
125 = ⌈(44×√((4+4)))⌉
82 = ((4+4)4-√4)
126 = (⌊(4-.4)⌋×4√4)
83 = ⌊((4+4)4-.4)⌋
127 = ⌈(.4~×(4+4!)4)⌉
84 = (4+4)⌊(4+.4)⌋
128 = ((4+4)×(4×4))
85 = ⌈(.4+(4+4)4)⌉
129 = ((4√4÷.4)+4!)
86 = (4√4+44)
130 = (⌈(4+.4)⌉√4÷.4)
87 = ⌈((4÷(.4~-.4))-4)⌉
131 = (⌈(.4+4)⌉4!÷4)
88 = (44+44)
89 = (4+4)(4÷.4~)
132 = (44×⌊(4-.4)⌋)
90 = (4(4-4)÷.4~)
133 = ⌈(√(4÷.4)×4√4)⌉
91 = (4÷.4~)(4÷4)
134 = ((4÷.4~)+4)4
92 = ((44×√4)+4)
135 = (⌈(4+.4)⌉4÷.4)
93 = ⌊((4÷.4~)4-.4)⌋
136 = (4×⌊(4-.4)⌋4)
94 = ((4-.4)÷.4)4
137 = ⌊(4!4÷(.4~×4))⌋
95 = ((44÷.4~)-4)
138 = ((4!×4)+4√4)
96 = (√4×4(4+4))
139 = ⌊(√(4÷.4)×44)⌋
97 = ⌊(√444×.4)⌋
140 = ((4×4!)+44)
98 = ((4÷.4~)4+4)
141 = ⌊(44!÷⌊(4-.4)⌋)⌋
99 = ((4+.4)÷(.4~-.4))
142 = (4÷4)4√4
100 = ((44-4)÷.4)
143 = ⌊(((√4+4)×4!)-.4)⌋
101 = (4÷.4)(4÷4)
144 = (4÷4)44
102 = ((4÷.4)4-√4)
145 = ⌊(√44×√4√4)⌋
103 = ((44÷.4~)+4)
146 = ((4!÷(.4×.4))-4)
104 = (4(4×4)÷4)
147
=
⌊((4!105 = ⌈(.4+(4÷.4)4)⌉
.4)÷(.4×.4))⌋
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
⌈((4-.4)×44)⌉
(4×(44-4))
(4×4)(4÷4)
((4×4)4-√4)
(4×4)⌊(4-.4)⌋
(4×4(4÷4))
⌈((4×4)4+.4)⌉
(4×4)(√4+4)
⌈(4(4×4)×.4)⌉
(4×4)(4+4)
(4×4)(4÷.4~)
⌈(.4×4√44)⌉
⌊(.4×4(4+4!))⌋
((4×44)-4)
(4+⌊(.4×44!)⌋)
⌊(.4×44)⌋4
⌈(4×(44-.4))⌉
(4⌊(.4+4)⌋×4)
⌊(.4×444)⌋
⌈(.4×444)⌉
((((4+√4))!-4)÷4)
(4+(44×4))
⌈((4+4)÷(.4~-.4))⌉
⌈(44×.4)⌉√4
⌈(4(4÷.4)×.4~)⌉
⌈(44×.4)⌉4
⌈(.4~×4(4×4))⌉
⌊((.4~+4)×4√4)⌋
⌈(4√4×(.4~+4))⌉
⌊(4√44×.4~)⌋
((4+4)4÷.4~)
⌊(.4~×(44!+4))⌋
⌈(4(4!+4)×.4~)⌉
74
106
107
108
109
110
111
112
113
114
115
=
=
=
=
=
=
=
=
=
=
((44÷.4)-4)
⌈((4×√44)×4)⌉
((4÷.4)4+4)
((44-.4)÷.4)
(4÷4)(4÷.4)
(444÷4)
(44÷4)√4
⌈(⌈(44+.4)⌉÷.4)⌉
(44÷4)4
((4+4√4)÷.4)
148
149
150
151
152
153
154
155
156
157
158
=
=
=
=
=
=
=
=
=
=
=
(((√4+4)×4!)+4)
⌊((4-.4~)×4√4)⌋
(√44÷(.4×.4))
⌊((4-.4)×4√4)⌋
((4√4-4)×4)
((44+4!)÷.4~)
⌊((4×4)-.4)⌋4
((√4+4)√4÷.4)
⌊(44×(4-.4~))⌋
⌈(44×(4-.4~))⌉
⌊(44×(4-.4))⌋
192
193
194
195
196
197
198
199
200
=
=
=
=
=
=
=
=
=
(4(4+4)×4)
⌊((.4+4)×44)⌋
⌊(44×.4~)⌋4
⌊((4+.4~)×44)⌋
(4×4(4÷.4~))
⌊(.4~×444)⌋
⌈(444×.4~)⌉
⌈(√4÷(.4~÷44))⌉
(4!4-44)
Проект Solve this POWER Math Exercise LIKE a Genius
Исходный код программы находится в файлах Solve this POWER Math
Exercise LIKE a Genius.pas.
Иногда и в Интернете случаются и встречаются интересные задачи на вычисление выражений. Естественным образом подразумевается, что решение должно прийти в голову, а затем и в руки. Вот наглядный пример таких
задач.
https://www.youtube.com/watch?v=B2nii9eiC60
Попробуйте или попытайтесь решить эту задачу самостоятельно, для проверки мы решим её на паскале:
75
##
WriteLn(' Solve this POWER Math Exercise LIKE a Genius');
WriteLn;
println(6 ** 12 / 12 ** 6);
println;
Solve this POWER Math Exercise LIKE a Genius
729
Вот и вся задачка!
Проект Can you solve this in 10 SECONDS? – Calculate powers
Исходный код программы находится в файлах Can you solve this in 10
SECONDS.pas.
И тут же другая красивая задачка.
https://www.youtube.com/watch?v=mMq2sWM9wCA
76
Сгоряча можно подумать, что в результате останется двойка в первой степени, то есть просто двойка. Но неожиданно оказалось, что останется гораздо больше – 299:
##
WriteLn(' Can you solve this in 10 SECONDS? – Calculate powers');
WriteLn;
println(BigInteger(2) ** 100 println(BigInteger(2) ** 99);
println;
BigInteger(2) ** 99);
Can you solve this in 10 SECONDS? – Calculate powers
633825300114114700748351602688
633825300114114700748351602688
Вывод такой: математика, как, впрочем, и химия, - это наука!
77
Проект Какое число больше? | Попробуйте решить
Исходный код программы находится в файлах Какое число
больше.pas.
Как это решить в уме, ума не приложу.
https://www.youtube.com/watch?v=polKZG5iiuA
Чтобы не прикладывать не только ум, но и руки, подсовываем задачу паскалю. Никаких или: степень больше:
##
WriteLn(' Какое число больше? | Попробуйте решить');
WriteLn;
println(1.005 ** 200);
println;
Какое число больше? | Попробуйте решить
2.71151712292929
78
Проект Harvard University Admission Interview Trick
Исходный код программы находится в файлах Harvard University
Admission Interview Trick.pas.
Или вот тебе задачка.
https://www.youtube.com/watch?v=3qE19RX25ko&list=PLxN8w5HpPYR9RCxEYxWMw1tmhcZ07B3Q&index=2
Это нужно вычислить в своём уме, без калькулятора. Но с трюком! Наш
трюк такой:
##
WriteLn(' Harvard University Admission Interview Trick');
WriteLn;
println((23*25*27*29 + 16).Sqrt);
println;
Harvard University Admission Interview Trick
671
Нас не перетрюкачишь!
79
Проект Harvard University Admission Interview Trick l 99.9%
Failed
Исходный код программы находится в файлах Harvard University Admission Interview Trick 2.pas.
Занимаясь такой математикой, невольно станешь Копперфильдом!
https://www.youtube.com/watch?v=IQ30b5eQhYs&list=PLxN8w5HpPYR9RCxEYxWMw1tmhcZ07B3Q&index=3
##
WriteLn(' Harvard University Admission Interview Trick l 99.9% Failed');
WriteLn;
println(991026973 ** (1/3));
println;
Harvard University Admission Interview Trick l 99.9% Failed
80
997
Казалось бы ура, но нет: с задачей нужно справиться умственным трудом!
Наука – колкая штука!
Вычисления по формулам
Недостаток рассмотренных нами задач в том, что они решаются «однократно», а пользователь не имеет возможности изменить входные данные.
Но пример с градусами Фаренгейта и Цельсия показывает, что было бы совсем неплохо вводить данные в работающей программе и получать от неё
результаты вычислений.
Такие действия называются вычислениями по формулам.
81
Проект Периметр квадрата
Исходный код программы находится в файле Периметр квадрата.pas.
Так как у квадрата 4 стороны одинаковой длины, то, зная длину стороны,
мы найдём периметр квадрата, умножив её на 4.
Легко определить, что переменные для хранения длины сторон и периметра квадрата должны быть одного типа. Можно поддаться искушению и
назначить им целый тип integer, но длина может выражаться и вещественными числами. Поэтому правильный тип данных для этой задачи – double.
Как программисты мы можем сразу присвоить переменной size (длина стороны) нужное значение, запустить программу и прочитать ответ в Окне вывода. А пользователь программы должен задать длину квадрата в работающей программе, когда эта переменная ему недоступна.
Для ввода данных паскаль имеет следующие функции:
ReadInteger: integer;
ReadInteger(prompt: string): integer;
ReadlnReal: real;
ReadlnReal(prompt: string): real;
В отличие он нас, пользователь может и не знать, что он должен сделать.
Мы должны сообщить ему ожидаемые действия, поэтому применяем функцию ReadlnReal(prompt: string).
Эта функция присваивает введённое значение переменной size так, что оно
становится равно числу пользователя.
Затем мы находим периметр квадрата и печатаем его на экране:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
82
begin
while True do begin
// сторона квадрата:
var size := ReadlnReal(' Введите длину стороны квадрата: ');
var perimeter := size * 4;
println($' Периметр квадрата = {perimeter}');
println;
end;
end;
begin
Writeln(' Периметр квадрата');
Writeln;
Solve();
end.
Дойдя до функции ReadlnReal, запущенная программа приостановится, и
под Окном вывода появится текстовое поле Ввод данных:. В нём нужно
набрать число – длину стороны и нажать либо кнопку Ввести, либо клавишу ВВОД:
Получив данные, программа продолжит работу, ненужное теперь текстовое
поле исчезнет с экрана, а в Окне вывода появится строка, сообщающая вычисленный периметр квадрата:
Периметр квадрата
Введите длину стороны квадрата: 10
Периметр квадрата = 40
83
Для проверки правильности работы программы сначала нужно вводить такие значения, чтобы можно было в уме найти периметр и сравнить правильный результат с компьютерным. Иначе вы можете получить неверный
периметр. Например, для стороны 10 периметр должен равняться 40, что
мы и видим в Окне вывода.
Так как мы предусмотрели ввод и дробных чисел (с плавающей точкой), то
давайте узнаем, чему равен периметр квадрата, сторона которого (приближённо) равна π:
Введите длину стороны квадрата:
Периметр квадрата = 12.5663704
3.1415926
С тем же успехом вы можете запустить программу без связи с оболочкой,
чтобы она выполнялась в Консольном окне:
Проект Длина окружности
Исходный код программы находится в файле Длина окружности.pas.
Раз уж мы упомянули добрым словом число π, то давайте упомянем его ещё
раз и вычислим длину окружности по её радиусу. Как вы помните, она
равна:
84
2 * PI * r
В паскале имеется специальная константа PI, так что вам
не нужно заучивать число π наизусть.
Код новой программы легко получить из «старой», заменив длину стороны
радиусом:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
while True do begin
// радиус окружности:
var r := ReadlnReal(' Введите радиус окружности:');
var len := 2 * PI * r;
println($' Длина окружности = {len}');
println;
end;
end;
begin
Writeln(' Длина окружности');
Writeln;
Solve();
end.
Сначала испытываем программу на низких оборотах, чтобы проверить её
работоспособность. При радиусе, равном 5, длина окружности должны равняться 10π, что мы и читаем в Окне вывода:
Длина окружности
Введите радиус окружности: 5
Длина окружности = 31.4159265358979
85
А дальше можете вводить любые (допустимые!) значения – программа вас
не подведёт:
Введите радиус окружности: 3.1415926
Длина окружности = 19.7392084654641
Проект Площадь круга
Исходный код программы находится в файле Площадь круга.pas.
Давайте продолжим геометрические вычисления и научим программу
находить площадь круга, которая равна:
PI * r*r
Возведение в квадрат заменяйте умножением.
Переделка предыдущей программы заняла несколько секунд – и мы можем
находить площадь круга по его радиусу:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
while True do begin
// радиус окружности:
var r := ReadlnReal(' Введите радиус окружности:');
var area := PI * r * r;
println($' Площадь круга = {area}');
println;
end;
86
end;
begin
Writeln(' Площадь круга');
Writeln;
Solve();
end.
Проводим обязательную проверку программы. При радиусе в 10 единиц
площадь круга должна быть равна 100π. Это число мы и видим на экране:
Площадь круга
Введите радиус окружности: 10
Площадь круга = 314.159265358979
Программа работает верно, и вы можете использовать её по прямому назначению:
Введите радиус окружности: 3.1415926
Площадь круга = 31.0062756224797
Проект Площадь прямоугольника
Исходный код программы находится в файле Площадь прямоугольника.pas.
Прямоугольник отличается от квадрата только тем, что длина его сторон
может быть различна.
Функция ReadlnReal2 позволяет вводить два числа в одной строке, разделяя их пробелом.
87
Если мы обозначим буквой a первую сторону прямоугольника, а буквой b –
вторую, то его площадь будет равна:
a*b
Программу для вычисления площади прямоугольника легко написать,
слегка переработав программу для вычисления периметра квадрата:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
while True do begin
// стороны прямоугольника:
var (a,b) := ReadlnReal2(' Введите длину сторон прямоугольника:');
var area := a * b;
println($' Площадь прямоугольника = {area}');
println;
end;
end;
begin
Writeln(' Площадь прямоугольника');
Writeln;
Solve();
end.
Тест наша программа проходит успешно:
Площадь прямоугольника
Введите длину сторон прямоугольника: 10 100
Площадь прямоугольника = 1000
Теперь вы сможете вычислить площадь любого прямоугольника:
Введите длину сторон прямоугольника: 111.111 222.222
88
Площадь прямоугольника = 24691.308642
Обобщение
1. С помощью паскаля можно легко решить любые вычислительные задачи.
Это сделать гораздо проще, чем на бумаге. При этом результат всегда получится верным, потому что паскаль не делает ошибок (а вот вы вполне можете наделать ошибок и на компьютере!).
2. Не нужно решать на компьютере все задачи подряд. Важна тренировка и
в «бумажных», и в умственных упражнениях. В таких случаях правильнее
использовать компьютер для проверки своих действий.
3. Трудоёмкие вычисления, конечно, лучше сразу выполнять на компьютере. Так вы сможете решить множество интересных математических задач, которые при ручном решении превращаются в мучение и отвращают
от математики. Всё-таки математика – это красивая наука для ума, а не тяжкий труд для рук.
Задания для самостоятельного решения
Задача #837
Математическая шкатулка
Вычислите сумму наибольших однозначного, двузначного, трёхзначного и четырёхзначного чисел.
Ответ: 9 + 99 + 999 + 9999
89
Задача #3.1
Удивительный мир чисел, стр. 42
Число 32
Десятичная запись куба этого числа начинается самим числом, а запись
его пятой степени оканчивается цифрами данного числа:
323 = 32768
325 = 33554432.
Проверьте эти равенства. Степени записывайте как произведения:
323 → 32 * 32 * 32
Задача #10
Математическая шкатулка
Найдите возможно быстрее, какое частное и какой остаток получатся
при делении числа 1 ⦁ 2 ⦁ 3 ⦁ 4 ⦁ 5 ⦁ 6 + 1 на 5.
Ответ: Частное равно 2 ⦁ 3 ⦁ 4 ⦁ 6 = 144, остаток равен 1.
Задачи #45, 46, 47, 58
Математическая шкатулка
Сколько суток составляет миллион минут?
Сколько лет составляет миллион часов?
Сколько столетий составляет миллион дней? Прошёл ли с начала
нашего летосчисления миллион дней?
Сколько столетий в миллиарде минут?
90
Задачи #70, 71, 72, 74, 75
Математическая шкатулка
Земля при своём движении вокруг Солнца проходит путь в 936 250 000
км в год. Какое расстояние проходит Земля за 1 сутки? (Считайте год
в среднем равным 365, 25 суток.)
Скорость света в вакууме ≈ 3,00 ⦁ 105 км/с. Какое расстояние проходит
свет в течение года?
Расстояние от Земли до звезды Проксима Центавра свет проходит за 4⅓
года. Сколько километров до этой звезды?
В астрономии для выражения расстояний во вселенной используются
единицы: парсек = 3,26 световых года и мегапарсек = 1 000 000 парсеков.
Выразите эти единицы в километрах.
Тончайшая паутиновая нить, если бы её протянуть по земному экватору,
длина которого ≈ 40 060 км, имела бы массу 660 г. Какую массу имела
бы такая нить, протянутая на расстояние в один мегапарсек?
91
Глава #2. Решаем исторические задачи
92
История математики насчитывает более 6
тысяч лет! Конечно, занимательные задачи появились не вдруг и не сразу, ведь
нашим далёким предкам ещё нужно было
научиться считать на пальцах и придумать
цифры для записи чисел.
Но самым древним «рукописям» - глиняным табличкам и папирусным свиткам –
больше 4,5 тысяч лет, а мы находим на них
не только «учебные» арифметические задачи, в которых требуется преобразовать
какое-либо выражение или вычислить его
значение, но и вполне занимательные задачи, не имеющие прямого практического
значения.
Главное отличие занимательных задач от
учебных заключается в том, что никто не
заставляет их решать. Они настолько интересны сами по себе, что их непременно хочется решить.
В этой главе собраны древние и старые занимательные задачи, и я надеюсь,
что они заинтересуют вас точно так же, как и любителей математики много
столетий и тысячелетий тому назад.
93
Проект Египетские кошки
Исходный код программы находится в файле Египетские кошки.pas.
Древние египтяне, в отличие от нынешних, кошек
любили и боготворили. Для них существовали кошачьи кладбища. Так же древние египтяне любили
арифметику.
В этой задаче они умело сочетали оба пристрастия – к
кошкам и к задачам.
У древних египтян, уже в отличие от современных нас, компьютеров не
было, поэтому задача не казалась им настолько лёгкой, как мы.
Число горстей находим перемножением пяти семёрок:
94
// вычисляем число горстей:
function Solve() := 7 * 7 * 7 * 7 * 7;
begin
Writeln(' Египетские кошки');
Writeln;
var числоГорстей := Solve();
// печатаем ответ:
var otvet := $' Число горстей = {числоГорстей}';
Writeln(otvet);
Writeln;
end.
И раз уж у нас есть и имеется в наличии компьютер, то запускаем программу
и получаем ответ в готовом виде. Переводим древнегреческие горсти в килограммы и получаем больше тонны килограммов - 1350. Ну и как тут не
полюбить кошек?!!
Египетские кошки
Число горстей = 16807
Такие задачи можно решать горстями!
Если процедура (или функция) очень короткая, то для её описания можно
пользоваться упрощённым синтаксисом:
// РЕШАЕМ ЗАДАЧУ
procedure Solve5 := Writeln($' Число горстей зерна равно: {7 * 7 * 7 * 7
* 7}');
Исключительно из любви к кошкам и уважения к древним египтянам давайте решим задачу неведомым ни кошкам, ни египтянам способом!
Поскольку текущее значение переменной числоГорстей снова умножается
на семёрку, то весь процесс можно выполнить в цикле loop.
95
function Solve2(): integer;
begin
var числоГорстей := 1;
loop 5 do
числоГорстей *= 7;
Result := числоГорстей;
end;
Запускаем программу – кошки справились со своей задачей и в цикле!
Цикл loop можно легко заменить другими циклами. Это вам упражнение и
домашнее наказание.
Если вы прилежно посещали школу и усердно изучали математику, то произведение пяти семёрок должно напомнить вам, что его можно представить
как семь в пятой степени. Семёрка – это основание степени, пятёрка – показатель, а всё выражение в целом - степень.
В паскале имеется функция Power, которая вычисляет заданную степень
заданного числа. Само число – это первый аргумент в функции Power, а второй аргумент – это показатель степени.
Power(основание, показатель) → степень
Английское слово power переводится как степень, что полностью совпадает
с русской математикой.
Важно учесть, что параметры метода и возвращаемое значение имеют не
целый, а вещественный тип real. Однако компилятор достаточно умный,
поэтому функция Power возвращает правильное точное значение.
При желании или без оного вы можете воспользоваться использованием
метода расширения IPower для целых чисел:
uses MathExtensions;
// вычисляем число горстей:
96
function Solve4() := 7.IPower(5);
Проект Египетские кошки 2
Исходный код программы находится в файле Египетские кошки
2.pas.
Мы уже неспешно, но успешно решили задачу про египетских кошек.
В известной книге A Brief History of Mathematics for Curious Minds есть фотография папируса Ринда с египетской задачей про кошек и мышек. В книге
условие задачи приводится на английском языке, но мы без труда переведём задачу на русский язык, а затем и на язык паскаля.
Обратите внимание, что в этой задаче нужно найти число всех упоминаемых в задаче объектов, а не только число зёрен в будущем урожае.
97
Решение задачи приведено в книге, так что нам нужно только вбить его в
нашу программу.
Легко догадаться, что степени семёрки мы вычислим в цикле for. В задаче
упоминаются пять различных объектов — дома, кошки, мышки, зёрна в
колосе и урожай от них.
procedure Solve();
begin
// степень семёрки:
var num := 1;
// число объектов:
var res := 0;
for var i := 1 to 5 do begin
num *= 7;
res += num;
end;
// печатаем ответ:
var otvet := $' Число объектов = {res}';
Writeln(otvet);
end;
begin
Writeln(' Египетские кошки 2');
Writeln(' A Brief History of Mathematics for Curious Minds');
Solve();
Writeln;
end.
Поскольку ответ нам известен из книги, то мы с радостным удовольствием
получаем от нашей программы подтверждение правильности нашего решения.
Египетские кошки 2
A Brief History of Mathematics for Curious Minds
Число объектов = 19607
98
Проект Египетские кошки 9
Исходный код программы находится в файле Египетские кошки
9.pas.
Давайте на время прервём папирусные экзерсисы и обратим наше внимание на ютубовский ролик Math Olympiad Exponential Problem | Germany | Can
you solve this?
А задача такова, что нужно вычислить значение выражения со степенями
девятки, но без калькулятора.
Поскольку компьютер — это не калькулятор, то мы нехотя, но безукоризненно выполним это условие.
По названию ролика мы можем предположить, что эта задача предлагалась
на немецкой олимпиаде по математике. Весьма чудно и странно, что немцы
не придумали ничего лучше, как заменить семёрки девятками, чтобы облегчить решение задачи.
99
Человек, не обременённый склерозом и провалами в памяти, легко вспомнит вторую задачу про кошек и мышек из папируса Ринда.
Решение этой задачи в точности до цифири совпадает с папирусным:
procedure Solve();
begin
// степень девятки:
var num := 1;
// сумма:
var res := 0;
for var i := 1 to 5 do begin
num *= 9;
res += num;
end;
// печатаем ответ:
var otvet := $' Сумма = {res}';
Writeln(otvet);
end;
begin
100
Writeln(' Египетские кошки 9');
Writeln;
Solve();
Writeln;
end.
Запустите программу и прочитайте ответ, который полностью совпадает с
роликовым.
Египетские кошки 9
Сумма = 66429
Обратите внимание, что калькулятор нам не понадобился.
101
Вот недаром говорят, что новое — это хорошо забытое старое. А плохо забытое старое — это не новое.
Но всё же подумайте, почему немцы схитрили и заменили семёрку именно
девяткой.
102
Проект Вавилонские ладони
Исходный код программы находится в файле Вавилонские ладони.pas.
Ханс Вусинг в книге 6000 Jahre Mathematik: Eine Kulturgeschichtliche Zeitreise
прямо на обложке торжественно утверждает, что математике аж 6 тысяч
лет! Так ли это, мы не знаем, но достоверно известно, что задаче Вавилонские ладони около двух тысяч лет.
Внимательно смотрим на неё обоими глазами и читаем на запоминание и
долгую память условие вавилонской задачи.
По-вавилонски задача решается очень просто и остроумно. Три четверти
ширины равны 10 минус 7, то есть ровнёхонько 3 ладони, поэтому вся ширина равна 4 ладоням.
Но мы не какие-то там дремучие древние вавилоняне – у нас есть компьютер!
103
Совершенно очевидно, что длина не меньше одной ладони, но не больше 10
ладоней. Длина выражается целыми числами, которые мы перебираем в
цикле for.
Ширина не больше длины и кратна четырём. Опять же в цикле for перебираем все возможные значения ширины.
По текущим значениям длины и ширины проверяем выполнение условий
задачи:
• Длина и четверть ширины вместе составляют 7 ладоней
• И длина и ширина вместе – 10 ладоней
Если оба условия выполнятся одновременно, то мы cплющим на глиняной
табличке ответ на задачу и пошлём наш горячий привет древним вавилонянам с просьбой: пожалуйста, не изобретайте компьютер!
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
for var length := 1 to 10 do
for var width := 0 to length step 4 do begin
// проверяем условия задачи:
if (length + width / 4 = 7) and (length + width = 10) then begin
// печатаем ответ:
WriteLn($' Длина = {length}');
WriteLn($' Ширина = {width}');
//exit;
end;
end;
end;
begin
Writeln(' Вавилонские ладони');
Writeln(' Увлекательная математика. Задача АЭ2');
Writeln;
Solve();
Writeln;
end.
104
Компьютерное решение полностью совпадает с решением в уме, поэтому
мы можем ему доверять:
Вавилонские ладони
Длина = 6
Ширина = 4
Не жалея ладоней, аплодируем древним вавилонянам и самим себе за сноровку, изобретательность и смекалку.
Красота решения – страшная сила, которая в наших руках и в ладонях.
Проект Греческие мешконосы
Исходный код программы находится в файле Греческие мешконосы.pas.
Идём дальше по историческому пути развития прогресса, чтобы решить задачу со смешным названием Греческие мешконосы.
Тут можно было бы подумать, что речь пойдёт о греках с носами, похожими
на мешки под глазами.
Но это грубая историческая и фейсовая ошибка. Греческие мешконосы – это
осёл и мул с нормальными носами, но с грузными мешками по бокам и на
спине. Эта животноводческая задача приписывается Евклиду. Задаче около
двух тысяч трёхсот лет, поэтому пора её решать.
Эту задачу я извлёк на свет из книги Математический фольклор под литерой Д8.
105
Её можно найти и в книге Увлекательная математика. Античные этюды.
Задача 7:
106
Однажды мула и осла нагрузили зерном. По дороге мул сказал ослу:
- Если бы ты уступил мне одну меру своего груза, то я нёс бы вдвое
больше зерна, чем ты. А если бы я уступил тебе одну меру своего груза,
то мы оба несли бы зерна поровну.
По скольку мер зерна нёс мул и сколько - осёл?
И осёл, и мул несли не менее 1 мешка (даже не меньше двух). Здравый смысл
нам подсказывает, что мул нёс не более 100 мешков (наверняка гораздо
меньше). Сколько мешков нёс осёл, нам знать необязательно, потому что
мы будем добавлять ему мешки в бесконечном цикле while:
procedure Solve();
begin
var осёл := 1;
while True do begin
for var мул := 1 to 100 do begin
По ходу дела проверяем условие задачи, чтобы не выглядеть ослами.
Если и когда эти условия дружно сбудутся, мы успешно и удачно отрапортуется на экране дисплейного монитора:
// условия задачи:
var uslovie1 := мул + 1 = 2 * (осёл - 1);
var uslovie2 := мул - 1 = осёл + 1;
// проверяем условие задачи:
if (uslovie1 and uslovie2) then begin
// печатаем ответ:
WriteLn($' Осёл нёс: {осёл}');
WriteLn($' Мул нёс: {мул}');
Writeln;
exit;
end;
end;
осёл += 1;
end;
end;
107
begin
Writeln(' Греческие мешконосы');
Writeln(' Математический фольклор. Задача Д8');
Writeln;
Solve();
Writeln;
end.
Задачу можно решить и с двумя циклами for, если задать разумный предел
в первом цикле for:
procedure Solve2();
begin
for var осёл := 1 to 1000 do begin
for var мул := 1 to 100 do begin
// условия задачи:
var uslovie1 := мул + 1 = 2 * (осёл - 1);
var uslovie2 := мул - 1 = осёл + 1;
// проверяем условие задачи:
if (uslovie1 and uslovie2) then begin
// печатаем ответ:
WriteLn($' Осёл нёс: {осёл}');
WriteLn($' Мул нёс: {мул}');
Writeln;
exit;
end;
end;
end;
end;
Нежданно-негаданно оказалось, что мул нёс 7 мешков, а осёл – только 5. И
кто после этого конфуза осёл?
Ответ на задачу:
Греческие мешконосы
Математический фольклор. Задача Д8
108
Осёл нёс: 5
Мул нёс: 7
Тяжкий груз в килограммах оказался не столь велик, ежели считать мешками.
Если не хочешь всю жизнь таскать мешки, то не будь ослом
Проект Индийские пчёлы
Исходный код программы находится в файле Индийские пчёлы.pas
и Индийские пчёлы граф.pas.
Задачу Индийские пчёлы придумали в
Древней Индии в то же время, что и задачу
про длину и ширину в Древнем Вавилоне.
Эту задачу, как и многие другие, вы можете
найти в книге Иоханнеса Лемана Увлекательная математика.
Читайте условие задачи и вникайте прямо
в её суть!
Для нас важно, что число пчёл целое и, вероятно, небольшое, поэтому метод грубой
силы здесь вполне уместен.
109
В задаче речь идёт о половине пчёл, это значит, что число пчёл чётное, то
есть делится на 2.
Также от нашего взора не укрылась дробь восемь девятых, которая подсказывает нам, что число пчёл также должно делиться на 9.
Поскольку число пчёл одновременно делится на 2 и на 9, то оно делится на
18. Это важный вывод из нашего знакомства с пчелиной задачей!
В отличие от предыдущих задач верхняя граница диапазона изменения значений переменной bee нам неизвестна. В этом случае решение ищут в бесконечном цикле. Давайте обратимся к циклу while:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
Перед началом бесконечного цикла нужно присвоить «искомой» переменной, начальное значение:
110
// число пчёл:
var bee := 0;
while True do begin
По текущему значению этой переменной мы можем вычислить квадратный
корень из половины числа пчёл:
// квадратный корень из половины пчёл:
var sqrtbee := Sqrt(bee / 2);
«Правильный» бесконечный цикл должен закончиться, когда выполнится
какое-то условие, записанное внутри цикла. Для выхода из цикла можно
использовать оператор break:
// проверяем условия задачи:
if (sqrtbee + bee * 8 / 9 + 2 = bee) then begin
// печатаем ответ:
var otvet := $' Пчёл было: {bee}';
WriteLn(otvet);
break;
end;
Но условие выполнится, только если внутри цикла изменяются значения
переменных (или единственной переменной), входящих в это условие. В
этой задаче, в конце цикла мы увеличиваем значение переменной bee. Если
вы забудете это сделать, то цикл while «никогда» не закончится.
Если условие в теле цикла while записано правильно, то решение будет
найдено в результате перебора всех возможных значений для этой переменной. В этой задаче значения переменной цикла изменяются от нуля и до
– пока не выполнится условие задачи.
Индийцы не были бы индийцами, если бы не заморочили нам голову пчёлами! Поэтому очень внимательно читаем задачу, чтобы правильно записать условие прекращения цикла в нашу программу.
111
А из задачи следует, что сумма квадратного корня из половины числа пчёл,
восьми девятых из числа пчёл и ещё двух пчёл равна числу пчёл. Записать
это условие на паскале несложно. Главное – быть внимательным и не упустить, что у кружащейся пчелы была жужжащая подружка.
Цикл while не изменяет значение переменной bee. Мы увеличиваем значение этой переменной самостоятельно. При этом не забываем, что число
пчёл кратно восемнадцати:
// добавляем новых пчёл:
bee += 18;
end;
end;
И вот все пчёлки пересчитаны. Их было семьдесят две:
Индийские пчёлы
Иоханнес Леман. Увлекательная математика. Задача АЭ3
Пчёл было: 72
Алгебраическое решение, приведённое в книге Увлекательная математика, значительно сложнее переборного!
Как указывает автор этой книги, 4000 лет назад математика была своеобразным видом спорта. Устраивались состязания по решению сложных задач, на которых присутствовали многочисленные зрители.
Пчелиная задача взята из учебного пособия по проведению математических конкурсов, причём в оригинале она изложена в стихотворной форме.
Мы решили задачу Индийские пчёлы самым простым перебором в цикле
while. Но, как говаривал медоед Винни-Пух, это были неправильные пчёлы,
а правильные пчёлы должны быть функциональными, тогда и мёд будет
правильным, и решение тоже.
112
Повторно читаем весьма длинное и туманное условие задачи, одновременно и параллельно смекая на ум, как удачно решить её в функциональном стиле.
Так как число пчёл – целое, то в рое должно быть столько пчёл, чтобы их
число нацело делилось и на 2, и на 9, то есть на 18.
Метод Step создаёт бесконечную последовательность, начиная с нуля и с
шагом 18.
// РЕШАЕМ ЗАДАЧУ «Индийские пчёлы» функционально
procedure Solve2();
begin
var n := 0.Step(18)
Метод SkipWhile пропускает мимо себя все числа, которые не удовлетворяют условию задачи.
.SkipWhile(n -> (n / 2).Sqrt() + 8 * n / 9.0 + 2 <> n)
Метод расширения First останавливает производство и выдачу натуральных чисел, как только в последовательности встретится первое число, подходящее под условие задачи.
.First;
Тут и сказочке конец, и мы смело, не опасаясь жалостных укусов, показываем результаты подсчёта пчёл прямо на экране прямо монитора:
// печатаем ответ:
Writeln($' Пчёл было: {n}');
Writeln;
end;
113
Осторожно, чтобы пчёлы не разлетелись в разные стороны, запускаем программу.
Весь пчелиный рой стройно пересчитываем поголовно и пожално. Их оказалось, как это было и раньше, ровно 72 полосатые штучки:
Индийские пчёлы
Иоханнес Леман. Увлекательная математика. Задача АЭ3
Пчёл было: 72
Пчёл было: 72
Считать пчёл графически ещё интереснее!
114
115
Проект Шахматное число
Исходный код программы находится в файле Шахматное число.pas.
В этом проекте мы решим легендарную задачу про изобретателя шахмат,
которого правитель Индии, царь Шерам решил отблагодарить за интересную игру и предложил ему самому назначить цену.
И легенда, и задача встречаются во многих книгах по занимательной математике. Например, вы можете прочитать о ней в книге Литцмана Великаны
и карлики в мире чисел, на странице 26.
116
Если отбросить «мелкие подробности», то нам нужно найти сумму чисел:
20 + 21 + 22 + 23 + 24 + … + 263
Эти числа образуют геометрическую прогрессию, сумма которой равна 264 –
1.
Когда результат вычислений предполагается огромным, используйте тип
BigInteger, который может хранить целые числа любой длины. В данном
случае мы можем прямо вычислить число два в степени 64:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// тип BigInteger:
var res := BigInteger.Pow(2, 64);
// вычитаем единицу:
res -= 1;
// печатаем ответ:
var otvet := $' Шахматное число = {res}';
WriteLn(otvet);
end;
117
begin
Writeln(' Шахматное число');
Writeln(' Литцман. Великаны и карлики в мире чисел, стр. 26');
Writeln;
Solve();
Writeln;
end.
Мы получили число хоть и большое, но непонятное:
Шахматное число
Литцман. Великаны и карлики в мире чисел, стр. 26
Шахматное число = 18446744073709551615
Давайте переведём зёрна в тонны. Если принять, что зёрнышко весит 65
миллиграммов, то общий вес составит около одного и двух десятых триллиона тонн. Это больше, чем собрало человечество за всю свою историю.
Вот почему так важно знать математику!
Я надеюсь, что мощные вычисления доставили вам не меньше удовольствия,
чем число зёрен на шахматной доске!
Особенно вам должны запасть в душу
огромные числа типа BigInteger. Программисты предпочитают всё большое
- и числа, и мышку, и самого себя родного!
118
Проект Китайские кролики и фазаны
Исходный код программы находится в файле Китайские кролики и
фазаны.pas.
Одна из самых известных исторических занимательных задач – про кроликов и фазанов – была известна ещё в Древнем Китае. В трактате Киу-чанг –
Девять отделов арифметики, - написанном в 2637 году до нашей эры, имеется такая задача:
Эту задачу я нашёл в журнале Наука и жизнь, №3 за 1963, стр. 38, задача 6.
На сайте http://festival.1september.ru/articles/569698/ вы найдёте эту же задачу с подробным разбором её решения:
Сегодня на уроке мы вновь встретимся с вами с хорошо известной вам задачей про фазанов и кроликов (задача выводится на доску “В клетке
119
находятся фазаны и кролики. Известно, что у них 35 голов и 94 ноги.
Узнайте число фазанов и число кроликов”), но если раньше мы её решали
арифметическим способом, то сегодня будем её решать с помощью уравнений и даже системы уравнений.
Поскольку ни число голов, ни число ног в программе не изменяется, то мы
сохраним их в константах: ГОЛОВ и НОГ:
const
// общее число голов:
ГОЛОВ = 35;
// общее число ног:
НОГ = 94;
Максимальное число кроликов находим делением общего числа ног на 4 (у
кролика 4 ноги, или лапы):
// макс. число кроликов:
МАКС_КРОЛИКОВ = НОГ div 4;
Минимальное число кроликов равняется нулю – тогда все головы и ноги
принадлежат только фазанам. Теперь мы должны перебрать все возможные числа для поголовья кроликов в цикле for. Ясно, что переменная цикла
i должна изменяться в диапазоне 0.. МАКС_КРОЛИКОВ. Число фазанов мы
найдём, вычтя из общего числа голов число кроликов, то есть i.
У i кроликов i*4 ноги, а у фазанов - фазанов * 2 ног. Когда сумма ног совпадёт с требуемой (НОГ), мы напечатаем ответ.
В целом исходный код программы может быть таким:
const
// общее число голов:
ГОЛОВ = 35;
// общее число ног:
НОГ = 94;
120
// макс. число кроликов:
МАКС_КРОЛИКОВ = НОГ div 4;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// решаем задачу:
for var кроликов := 0 to МАКС_КРОЛИКОВ do begin
// число фазанов:
var фазанов := ГОЛОВ - кроликов;
// печатаем ответ:
if (фазанов * 2 + кроликов * 4 = НОГ) then begin
WriteLn($' Кроликов: {кроликов}');
WriteLn($' Фазанов: {фазанов}');
end;
end;
end;
begin
Writeln(' Китайские кролики и фазаны');
Writeln(' Наука и жизнь, №3 за 1963, стр. 38, задача 6');
Writeln;
Solve();
Writeln;
end.
Запускаем программу и читаем ответ на задачу: кроликов было 12, а фазанов – 23.
Китайские кролики и фазаны
Наука и жизнь, №3 за 1963, стр. 38, задача 6
Кроликов: 12
Фазанов: 23
Эту древнюю задачу до сих пор решают в средней школе:
У фазанов и кроликов 62 ноги и 19 голов.
121
Сколько фазанов и кроликов?
Достаточно изменить значения констант – и ответ получен!
const
// общее число голов:
ГОЛОВ = 19;//35;
// общее число ног:
НОГ = 62;//94;
Китайские кролики и фазаны
Наука и жизнь, №3 за 1963, стр. 38, задача 6
Кроликов: 12
Фазанов: 7
А вот как решали задачу про кроликов и фазанов сами китайцы:
Если бы в клетке были одни фазаны, то число ног было бы 70, а не 94. Следовательно, 24 лишних ноги принадлежат кроликам, по две на каждого. Кроликов – 12, фазанов – 23.
Иногда хочется быть древним китайцем!
122
Совершенно очевидно, что все подобные задачи решаются одинаково –
нужно только подставить значения константам. Исходя из этого мы можем
написать универсальную функцию, которая принимает число ног и голов:
procedure SolveGeneral(heads, legs: integer);
begin
WriteLn($' Голов: {heads} Ног: {legs}');
// Проверяем возможность решения
if legs mod 2 <> 0 then begin
WriteLn($' Ошибка: общее число ног должно быть чётным!');
exit;
end;
if legs < heads * 2 then begin
WriteLn($' Ошибка: слишком мало ног (минимум {heads * 2})');
exit;
end;
if legs > heads * 4 then begin
WriteLn($' Ошибка: слишком много ног (максимум {heads * 4})');
exit;
end;
// Решаем систему уравнений:
// rabbits + pheasants = heads
// 4*rabbits + 2*pheasants = legs
//
//
//
//
//
Из первого уравнения: pheasants = heads - rabbits
Подставляем во второе: 4r + 2(h - r) = legs
4r + 2h - 2r = legs
2r = legs - 2h
r = (legs - 2h) / 2
var rabbits := (legs - 2 * heads) div 2;
var pheasants := heads - rabbits;
// Проверка
if (rabbits * 4 + pheasants * 2 = legs) and
(rabbits >= 0) and
(pheasants >= 0) then begin
WriteLn($' Кроликов: {rabbits}');
WriteLn($' Фазанов: {pheasants}');
123
WriteLn($' Проверка: {rabbits}?4 + {pheasants}?2 = {rabbits * 4 +
pheasants * 2}');
end
else
WriteLn($' Решение не найдено')
end;
Теперь мы можем вмиг решить все задачи про головоногих домашних животных:
begin
Writeln(' Китайские кролики и фазаны');
Writeln(' Наука и жизнь, №3 за 1963, стр. 38, задача 6');
Writeln;
Solve();
Writeln;
SolveGeneral(35, 94); Writeln;
SolveGeneral(19, 62); Writeln;
SolveGeneral(6, 20);
Writeln;
SolveGeneral(35, 100); Writeln;
SolveGeneral(15, 42); Writeln;
end.
Китайские кролики и фазаны
Наука и жизнь, №3 за 1963, стр. 38, задача 6
Кроликов: 12
Фазанов: 7
Голов: 35 Ног: 94
Кроликов: 12
Фазанов: 23
Проверка: 12×4 + 23×2 = 94
Голов: 19 Ног: 62
Кроликов: 12
Фазанов: 7
Проверка: 12×4 + 7×2 = 62
Голов: 6
Ног: 20
124
Кроликов: 4
Фазанов: 2
Проверка: 4×4 + 2×2 = 20
Голов: 35 Ног: 100
Кроликов: 15
Фазанов: 20
Проверка: 15×4 + 20×2 = 100
Голов: 15 Ног: 42
Кроликов: 6
Фазанов: 9
Проверка: 6×4 + 9×2 = 42
Проект Лошади и пастухи
Исходный код программы находится в файле Лошади и пастухи.pas.
Задача исключительно на повторение и закрепление в уме пройденного
мимо себя материала:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var пастухи in Range(0, 26) do begin
var лошади := 26 - пастухи;
if пастухи * 2 + лошади * 4 = 82 then
WriteLn($' Пастухи: = {пастухи} лошади: {лошади}');
end;
Writeln;
end;
begin
WriteLn(' Лошади и пастухи');
WriteLn;
125
Solve();
end.
Вот их сколько было, пока не убыло:
Лошади и пастухи
Пастухи: = 11
лошади: 15
126
Проект Вьетнамские буйволы
Исходный код программы находится в файле Вьетнамские буйволы.pas.
Решаем очень старую вьетнамскую задачу, которую старики-разбойникирисоводы любили задавать подрастающему поколению. Условие задачи довольно длинное для запоминания и понимания. Зато и вопрос не короче.
А если короче, то нам нужно и предстоит в точности пересчитать всех буйволов, разбитых по категориям, которые съели 100 охапок сена. Они съели
бы и больше, но больше им не дали, но дали и не меньше, чем больше.
Эта задача легко решается в компьютерном уме простым перебором в трёх
циклах, которые можно выбрать по своему усмотрению.
127
Понятно, что буйволов каждого вида не меньше нуля и не больше - 100 поделить на число съедаемых охапок сена. Некоторую нервозность вносят
старые буйволы, которых следует считать тройками.
Когда общее число буйволов и съедаемых охапок сена сравняется с сотней,
мы напечатаем ответ:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var Check: (integer,integer,integer) -> boolean := (a,b,c) -> (a + b +
c = 100) and (a * 5 + b * 3 + c div 3 = 100);
for var buffaloS := 0 to 100 div 5 do
for var buffaloL := 0 to 100 div 3 do
for var buffaloO := 0 to 100 step 3 do
if Check(buffaloS, buffaloL, buffaloO) then begin
WriteLn($' Стоящих молодых буйволов: {buffaloS}');
WriteLn($' Лежащих молодых буйволов: {buffaloL}');
WriteLn($' Старых буйволов:
{buffaloO}');
WriteLn;
end;
end;
begin
Writeln(' Вьетнамские буйволы');
Writeln(' Математический фольклор. Задача Д10');
Writeln;
Solve(); Writeln;
end.
Задача имеет 4 решения, что вполне могло поставить вьетнамских недорослей в тупик:
Вьетнамские буйволы
Математический фольклор. Задача Д10
Стоящих молодых буйволов: 0
Лежащих молодых буйволов: 25
Старых буйволов:
75
128
Стоящих молодых буйволов: 4
Лежащих молодых буйволов: 18
Старых буйволов:
78
Стоящих молодых буйволов: 8
Лежащих молодых буйволов: 11
Старых буйволов:
81
Стоящих молодых буйволов: 12
Лежащих молодых буйволов: 4
Старых буйволов:
84
Условие задачи можно проверить и в самой процедуре:
// РЕШАЕМ ЗАДАЧУ
procedure SolveA();
begin
for var buffaloS := 0 to
for var buffaloL := 0
for var bO := 0 to
begin
var buffaloO :=
100 div 5 do
to 100 div 3 do
100 div 3 do
bO * 3;
129
if ((buffaloS
(buffaloS
begin
WriteLn($'
WriteLn($'
WriteLn($'
Writeln;
end
end;
Writeln;
end;
+ buffaloL + buffaloO = 100) and
* 5 + buffaloL * 3 + buffaloO div 3 = 100)) then
Стоящих молодых буйволов: {buffaloS}');
Лежащих молодых буйволов: {buffaloL}');
Старых буйволов:
{buffaloO}');
Давайте пересчитаем буйволов функционально.
Чтобы упростить проверку в методе Where, пишем функцию Check, которая проверяет условие задачи, переводя его с вьетнамского языка на паскальский:
// проверяем условие задачи:
function Check(a, b, c : integer) := (a + b + c = 100) and (a * 5 + b * 3
+ c div 3 = 100);
Методы Range действуют, как обычные циклы for, то есть последовательно
выдают все возможные комбинации поголовья буйволов. Метод Where отфильтровывает только такие группировки буйволов, которые удовлетворяют условию задачи. А метод Select создаёт из буйволов кортеж:
// РЕШАЕМ ЗАДАЧУ
procedure Solve2();
begin
var res := Range(0, 100 div 5)
.SelectMany(buffaloS ->
Range(0, 100 div 3)
.SelectMany(buffaloL ->
Range(0, 100, 3)
.Where(buffaloO -> Check(buffaloS, buffaloL, buffaloO))
.Select(buffaloO -> (buffaloS, buffaloL, buffaloO))
)
);
WriteLn(' Найдено комбинаций: ', res.Count);
130
res.Take(5).Println;
WriteLn;
Все кортежи мы распечатываем в цикле foreach, чем и добиваемся полной
и окончательной победы над буйволами и буйвольской задачей в целом:
foreach var b
WriteLn($'
WriteLn($'
WriteLn($'
WriteLn;
end;
end;
in res do begin
Стоящих молодых буйволов: {b.Item1}');
Лежащих молодых буйволов: {b.Item2}');
Старых буйволов:
{b.Item3}');
begin
Writeln(' Вьетнамские буйволы');
Writeln(' Математический фольклор. Задача Д10');
Writeln;
Solve(); Writeln;
Solve2(); Writeln;
end.
В функциональном стиле можно решить задачу иначе – с помощью метода
Cartesian, который возвращает декартово произведение двух последовательностей:
// РЕШАЕМ ЗАДАЧУ
procedure Solve3();
begin
// буйволы:
var sqBuffaloS := Range(0, 100 div 5);
var sqBuffaloSL := sqBuffaloS.Cartesian(Range(0, 100 div 3));
var cart3 := sqBuffaloSL.Cartesian(Range(0, 100 div 3));
// решаем задачу:
foreach var t in cart3 do
begin
var buffaloS := t.Item1.Item1;
var buffaloL := t.Item1.Item2;
var bO := t.Item2;
131
var buffaloO := bO * 3;
if ((buffaloS + buffaloL + buffaloO = 100) and
(buffaloS * 5 + buffaloL * 3 +
buffaloO div 3 = 100)) then
begin
Println($' Стоящих молодых буйволов: {buffaloS}');
Println($' Лежащих молодых буйволов: {buffaloL}');
Println($' Старых буйволов
: {buffaloO}');
Println;
end
end;
end;
Не зря же мы считали буйволов, чтобы вот так, просто уйти и сразу расстаться с ними раз и навсегда! Давайте представим себе вполне возможную
и вероятную картину будущего, когда на смену людям придут более разумные животные, которые будут успешно пересчитывать людей в циклах на
компьютере.
После близкого знакомства с людьми я не очень надеюсь на прочих обезьян
как на разумную замену людей в творческих процессах. Тут нужно и следует
поискать более разумных и трудоспособных животных, которые будут послушно работать за охапку сена в день.
И тут наш выбор неизменно должен упасть на тех самых вьетнамских и прочих буйволов, которые идеально подходят как для размножения себе подобных, так и для написания компьютерных программ. Здесь ведь что
важно? – Программисты-буйволы уже с рогами, поэтому проблем в семейной жизни не будет, а она сильно отвлекает от работы и пагубно влияет на
результативность и чистоту кода от багов.
132
133
Проект Индийские обезьяны
Исходный код программы находится в файле Индийские обезьяны.pas, Индийские обезьяны граф.pas и Индийские обезьяны
граф2.pas.
Две очень старые индийские задачи про обезьян. Подобные задачи были известны в Индии ещё в XII веке.
Исключительно внимательно читаем условие задачи!
Эта задача легко решается с помощью квадратного уравнения. Но это в
Индии – там тепло. А нам сподручнее решить задачу с помощью перебора.
Однако легко заметить, что верхняя граница поголовья обезьян нам неизвестна, поэтому применяем бесконечный цикл while:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
134
var vars := 0;
var monkey := 0;
while True do begin
Любой бесконечный цикл должен закончиться, когда выполнится некоторое условие. Тогда мы напечатаем ответ и прервём цикл оператором break.
В данном случае в условие должно входить текущее число обезьян, то есть
переменная monkey:
// квадрат пятой части обезьян без трёх:
if (monkey/5 - 3).Power + 1 = monkey then
begin
// печатаем ответ:
var otvet := $' Обезьян было: {monkey}';
WriteLn(otvet);
vars += 1;
if (vars = 2) then break;
end;
Оператор цикла while не изменяет самостоятельно значение переменной, в
отличие от цикла for, так что нам нужно самим позаботиться об этом:
monkey += 1;
end;
end;
begin
Writeln(' Индийские обезьяны');
Writeln(' Математический фольклор. Задача Д10');
Writeln;
Solve(); Writeln;
end.
Обезьяны пересчитаны вчистую:
Индийские обезьяны
135
Математический фольклор. Задача Д10
Обезьян было: 5
Обезьян было: 50
Поскольку всё в мире должно быть гармонично и функционально, то в этой
программе мы управимся с обезьянками чисто конкретно и функционально.
К счастью и облегчению для нас условие задачи короткое и удобоваримое
для мозга. Но всё равно читаем и сразу включаем функциональное мировоззрение и миропонимание.
С математической точки зрения, обезьяны ничем не отличаются от пчёл.
Поэтому решаем обезьян по аналогии с пчёлками.
136
В методе Step учитываем, что число обезьян кратно 5:
uses MathExtensions;
// РЕШАЕМ ЗАДАЧУ «Индийские обезьяны» функционально
procedure Solve2();
begin
var res := 0.Step(5)
Метод Where пропускает через себя все числа, которые удовлетворяют
условию задачи:
.Where(n -> (n / 5 - 3).Power + 1 = n)
А метод расширения Take останавливает метод Step, как только в последовательности встретится второе число, подходящее под условие задачи.
.Take(2);
Публикуем отчёт о переписи обезьяньего стада на экране:
// печатаем ответ:
foreach var m in res do begin
var otvet := $' Обезьян было: {m}';
WriteLn(otvet);
end;
Writeln;
end;
Запускаем программу. Как ни считай и ни пересчитывай обезьянье поголовье, всё одно их оказывается ровно пятачок и полтинник:
Индийские обезьяны
137
Математический фольклор. Задача Д10
Обезьян было: 5
Обезьян было: 50
Обезьян было: 5
Обезьян было: 50
Есть и теплится ещё надежда, что, если обезьяну посадить за компьютер, то
она сильно поумнеет и станет программистом. Но обезьяны не настолько
глупы, чтобы работать, когда (можно) есть бананы.
Тут приходит в голову и на ум – отобрать бананы у обезьян, чтобы они работали за еду и по этой причине происходили бы в программистов. Но не
тут-то было! Обезьяны, лишённые бананов, не только не работают, но и активно бузят против работодателей.
На обезьян надежды нет, поэтому программисты пилят искусственный интеллект, как Шура Балаганов пилил гирю, украденную у Корейко. И с тем же
успехом!
Задачу можно решить и графически.
На картинке хорошо видно, что обезьянье уравнение имеет 2 корня – 5 и
50. Чисто математически оба корневища и есть ответ на задачу.
138
С новым модулем Coords график получается и того лучше.
139
Проект Индийские обезьяны 2
Исходный код программы находится в файле Индийские обезьяны
2.pas и Индийские обезьяны 2 граф.pas.
В этом проекте мы решим задачу Д12 из книги Математический фольклор.
Условие задачи очень короткое, но нужно правильно понять и оценить его.
Задача решается с помощью квадратного уравнения, у которого 2 действительных корня. При внимательном чтении условия задачи это очевидно, поскольку там прямо говорится о квадрате некоторой части обезьяньего стада.
Нам нужно найти оба корня. Для этого в бесконечном цикле while считаем
найденные решения:
140
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// число решений:
var solutions := 0;
var monkey := 0;
Из условия задачи следует, что число обезьян, которые играют в роще, делится на восемь:
// число обезьян в роще кратно восьми:
while True do begin
Находим восьмую часть обезьян:
// восьмая часть обезьян:
var m8 := monkey div 8;
Теперь мы легко составим условие в операторе if по тексту задачи: если к
квадрату восьмой части обезьян добавить ещё 12 обезьян, то мы получим
общее число обезьян:
// условие задачи:
if (m8 * m8 + 12 = monkey) then begin
Печатаем и считаем найденные решения:
// печатаем ответ:
var otvet := $' Обезьян было: {monkey}';
WriteLn(otvet);
// считаем решения:
solutions += 1;
141
Поскольку цикл бесконечный, то его следует прервать, когда будут
найдены два решения задачи:
// все решения найдены:
if (solutions = 2) then
break;
end;
monkey += 8
end;
end;
begin
Writeln(' Индийские обезьяны 2');
Writeln(' Математический фольклор. Задача Д12');
Writeln;
Solve();
Writeln;
end.
Читаем ответ на обезьянью задачу:
Индийские обезьяны 2
Математический фольклор. Задача Д12
Обезьян было: 16
Обезьян было: 48
А теперь давайте попересчитаем обезьян функционально. Тут, конечно,
нужно и необходимо вспомнить условие задачи⬆.
Длинное условие задачи проверяем в функции Check.
procedure Solve2();
begin
// условие задачи:
var Сheck: (int) -> bool := n -> (n / 8).Power(2) + 12 = n;
142
Число обезьян нам неизвестно, поэтому используем метод Step с аргументом 8, который генерирует бесконечную последовательность целых чисел,
кратных восьми, начиная с нуля.
// решаем задачу:
var res := 0.Step(8)
Метод расширения Where не без труда, но при помощи функции Check проверяет условие задачи и отправляет годные числа в переменную res.
.Where(Сheck)
Поскольку метод Step сам по себе не остановится, то мы добавляем в хвост
методу расширения Where метод расширения Take. Как только в результирующей последовательности появится второй элемент, метод Take прервёт дальнейшую генерацию чисел.
.Take(2);
Тут же, по свежим следам печатаем на
экране результат счёта и подсчёта обезьян:
// печатаем ответ:
foreach var n in res do
WriteLn($' Обезьян было: {n}');
Writeln;
end;
Запускаем программу. Как и раньше и до того, обезьян было 16 или 48:
Индийские обезьяны 2
Математический фольклор. Задача Д12
143
Обезьян было: 16
Обезьян было: 48
Обезьян было: 16
Обезьян было: 48
И ещё одно решение – для любителей разнообразия:
procedure Solve3();
begin
// условие задачи:
var f: integer -> boolean :=
Write($' Обезьян было: ');
n -> n - (n div 8) * (n div 8) = 12;
// решаем задачу:
var res := 0.Step(8)
.Where(f)
.Take(2)
.Print;
Writeln;
end;
Индийские обезьяны 2
Математический фольклор. Задача Д12
Обезьян было: 16
Обезьян было: 48
Обезьян было: 16
Обезьян было: 48
Обезьян было: 16 48
Мы знаем, что график квадратичной функции – это наикрасивейшая парабола, поэтому решаем задачу графически. Для солидности и красоты восприятия и понимания решения проводим ось абсцисс.
144
И ось, и сама парабола получились и вышли отлично! А также хорошо и ясно
видно, что парабола пересекает горизонтальную ось в точках с координатами 16 и 48, а это и есть полный и правильный ответ на задачу.
145
Проект Жизнь Демохара
Исходный код программы находится в файле Жизнь Демохара.pas и
Жизнь Демохара граф.pas.
Множество занимательных задач связано с вычислением возраста. Одна из
первых появилась в Греции в конце IV века:
Для нас это задача Д9 из книги Математический Фольклор.
Демохар прожил не меньше 13 лет (наверняка гораздо дольше, но перебор
всё равно будет небольшим, поэтому мы можем удовлетвориться и этим
явно заниженным возрастом).
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
146
В цикле for добавляем по одному году к жизни Демохара. Если условие задачи выполнится, то мы напечатаем ответ. В противном случае цикл for
благополучно, но безрезультатно закончится.
// ищем возраст в цикле:
for var let := 13 to 999 do begin
// условие задачи:
if (let / 4 + let / 5 + let / 3 + 13 = let) then begin
// печатаем ответ:
Writeln($' Демохар прожил {let} лет');
Writeln;
Эта задача имеет единственное решение, поэтому цикл for можно прервать, как только будет найдено первое решение.
Но иногда бывает полезно проверить весь диапазон значений переменной
цикла, чтобы выявить и другие решения. Случается, что задачи имеют побочные решения, которые упустил автор.
break;
end;
end;
end;
begin
Writeln(' Жизнь Демохара');
Writeln(' Математический фольклор. Задача Д9');
Writeln;
Solve();Writeln;
end.
Запускаем программу, которая с горечью и сожалением сообщает нам, что
Демохар прожил всего 60 лет:
Жизнь Демохара
Математический фольклор. Задача Д9
147
Демохар прожил 60 лет
После вавилонских глиняных задачек мы вполне можем предположить, что
возраст Демохара должен нацело делиться на 4, 5 и 3. Минимальное число,
удовлетворяющее этим требованиям, - 60. Следующее число – 120 – явно не
по Демохару.
Весьма странно, что эту задачу придумали не вавилоняне, а греки. Но нам
знакомство с вавилонянами пошло на пользу, и мы «сделали» задачу исключительно в своём уме!
Мы решили задачу Жизнь Демохара классическим способом, а теперь ударим по Демохару самым убойным способом – функциональным!
Несмотря на на, ещё раз читаем условие задачи.
Математически задача решается с помощью линейного уравнения, у которого всего 1 корень. Новые объяснения здесь излишни, поскольку
148
подобные задачи мы уже решали неоднократно и с успехом наперевес. На
том и порешим!
Длинное условие задачи проверяем в функции Check. В условии задачи
везде идёт делёж возраста на части, поэтому искусно делим возраст Демохара на вещественные литералы.
// условие задачи:
function Сheck(n: integer) := n / 4.0 + n / 5.0 + n / 3.0 + 13 = n;
// РЕШАЕМ ЗАДАЧУ «Жизнь Демохара» функционально
procedure Solve2();
begin
Возраст Демохара нам неизвестен, поэтому используем метод Step с аргументом 1, который генерирует бесконечную последовательность целых чисел, начиная с нуля:
// решаем задачу:
var let := 0.Step(1)
Метод расширения Where не без помощи функции Check проверяет условие задачи и отправляет годные числа в переменную res.
.Where(Сheck)
Метод расширения First остановит метод Step, как только в результирующей последовательности появится первый элемент.
.First;
Сразу, не отходя далеко от кода, печатаем на экране результат вычисления
возраста Демохара.
149
Writeln($' Демохар прожил {let} лет');
Writeln;
end;
Запускаем программу. Как ни крути-верти методами и способами решения
задач, а возраст Демохара не увеличился ни на один день. Как и допрежь,
прожил Демохар ровно 60 лет, чтобы не мучить нас сложными математическими вычислениями. Увы, математика здесь бессильна…
Жизнь Демохара
Математический фольклор. Задача Д9
Демохар прожил 60 лет
Демохар прожил 60 лет
Ещё одно функциональное решение этой задачи:
procedure Solve3();
begin
var v := 0.0;
var f: double -> boolean :=
n -> n / 4 + n / 5 + n / 3 + 13 = n;
Print(' Демохар прожил:');
v.Step(1)
.Where(f)
.Take(1)
.Println;
Writeln;
end;
Линейные уравнения легко решаются графически.
150
151
Проект Индийское число
Исходный код программы находится в файле Индийское число.pas и
Индийское число граф.pas.
Мы можем только посочувствовать древним индийцам, у которых не было
компьютеров, чтобы решать такие задачи.
А для компьютера эта задача очень простая – так же, как и для нас. Главное
- правильно записать условие задачи.
Как обычно, ищем задачное индийское число в цикле for, начиная с единицы. Верхняя граница нам неизвестна, поэтому можно было бы искать в
бесконечном цикле, но мы для безопасности ограничимся первой тысячей
чисел.
procedure Solve();
begin
152
// ищем возраст в цикле:
for var num := 1 to 1000 do begin
Внимательно читаем задачу и аккуратно записываем условие на языке паскаля:
// условие задачи:
if (num * 5 - num * 5 / 3) / 10 + num / 3 + num / 2 + num / 4 = 68
then begin
// печатаем ответ:
Writeln($' Число = {num}');
Writeln;
Поскольку условие задачи представляет собой линейное уравнение, то
она имеет единственное решение, поэтому в нужный момент мы прервём
цикл оператором break.
break;
end;
end;
end;
begin
Writeln(' Индийское число');
Writeln(' Математический фольклор. Задача Д7');
Writeln;
Solve();Writeln;
end.
Мы могли бы значительно сократить перебор, но в данном случае этого не
требуется:
Индийское число
Математический фольклор. Задача Д7
Число = 48
153
Задача Индийское число уже пала к нашим ногам в результате стремительного полного перебора в цикле for. Но как говорят не в нашем районе, нам
не мнётся, поэтому на этот раз мы решим задачу функционально.
Для освежевания памяти ещё раз, но внимательно и запоминательно читаем длинное и заковыристое условие задачи. Мы уже доказали и убедились, что математически задача представляет собой линейное уравнение
с единственным корнем. Искать другие корни бесполезно – даже с лопатой.
Функционально эта задача решается так же, как задача Жизнь Демохара.
Аккуратно записываем условие задачи в функцию Check. Здесь, конечно,
нужна сноровка, закалка, тренировка, но всё это у нас уже имеется в наличии и в полном достатке:
// условие задачи:
function Сheck(n: integer) := (n * 5 - n * 5 / 3) / 10 + n / 3 + n / 2 +
n / 4 = 68;
// РЕШАЕМ ЗАДАЧУ «Индийское число» функционально
procedure Solve2();
begin
var num := 1.Step(1)
.Where(n -> Сheck(n))
.First;
Writeln($' Число = {num}');
Writeln;
end;
Запускаем программу и вновь убеждаемся, что индийское число равно сорока восьми.
Индийское число
Математический фольклор. Задача Д7
Число = 48
154
Число = 48
Ещё одно решение задачи в функциональном стиле:
procedure Solve3();
begin
var m := 1;
var f: integer -> boolean := n -> (n * 5 - n * 5 div 3)
div 10 + n div 3 + n div 2 + n div 4 = 68;
Print(' Число равно:');
m.Step(1)
.Where(f)
.Take(1)
.Println;
Writeln;
end;
В функциональном стиле задачу можно решить и по-другому:
procedure Solve3();
begin
var m := 1.0;
var f: double -> boolean :=
Print(' Учеников было:');
m.Step(1)
.Where(f)
.Take(1)
.Println;
Writeln;
end;
n -> n / 2 + n / 4 + n / 7 + 3 = n;
Графически задача решается тоже изящно:
Сразу и хорошо видно, что график линейной функции – это прямая линия,
которая пересекает ось абсцисс в точке с горизонтальной координатой 48.
Это и есть ответ на индийскую задачу.
155
Проект Пифагорейское число
Исходный код программы находится в файле Пифагорейское
число.pas и Пифагорейское число граф.pas.
В этом проекте к нашим ногам падёт ещё одна старинная задача - Пифагорейское число. Я нашёл её в книге Математический фольклор, задача
Д4.
156
Эта задача напечатана и в книге Увлекательная математика. Античные
этюды. Задача 5. Пифагор Самосский (ок. 508-501 гг. до н.э.):
Поликрат (известный из баллады Шиллера тиран с острова Самос) однажды спросил на пиру у Пифагора, сколько у того учеников.
- Охотно скажу тебе, о Поликрат, - отвечал Пифагор. - Половина моих учеников изучает прекрасную математику, четверть исследует тайны вечной природы, седьмая часть молча упражняет силу духа, храня в сердце
учение. Добавь ещё к ним трёх юношей, из которых Теон превосходит прочих своими способностями.
Сколько учеников было у Пифагора?
Понятно, что, как эту историю ни рассказывай, а ответ будет один. И мы его
найдём!
Как обычно, ищем задачное пифагорейское число в разумно ограниченном
цикле for, начиная с единицы:
157
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// ищем число учеников в цикле:
for var ученик := 1 to 1000 do begin
Внимательно читаем задачу и аккуратно записываем условие на языке паскаля.
// условие задачи:
if (ученик / 2 + ученик / 4 + ученик / 7 + 3 = ученик) then begin
// печатаем ответ:
Writeln($' Учеников было: {ученик}');
Writeln;
Условие задачи представляет собой линейное уравнение, которое имеет
единственное решение, поэтому в нужный момент мы прервём цикл оператором break:
break;
end;
end;
end;
begin
Writeln(' Пифагорейское число');
Writeln(' Математический фольклор. Задача Д4');
Writeln;
Solve();Writeln;
end.
Если бы искомое число оказалось больше тысячи, то мы бы приподняли
верхнюю границу и продолжили поиски. Но в данном случае число очень
небольшое: у Пифагора было 28 учеников.
Пифагорейское число
158
Математический фольклор. Задача Д4
Учеников было: 28
Задача легко решается в уме, если учесть, что число учеников должно быть
кратно 4 и 7.
Весьма древнюю по старости задачу Пифагорейское число мы уже имели
смелость решить классическим способом – полным перебором в цикле for.
Под влиянием и воздействием моды на функциональное программирование мы дерзнём решить её функционально!
Внимательнейшим образом вчитываемся в условие задачи⬆, чтобы оценить предстоящие нам функциональные напряжения серого вещества
мозга.
Здесь для нас опять открывается математическая подоплёка этой задачи в
виде линейного уравнения, которое имеет один корень, или клубень.
Собственно решение этой задачи в точности повторяет функциональное
решение задачи Индийское число. Нам нужно только изменить условие в
функции Check, что не составляет ни малейших трудовых усилий:
// условие задачи:
function Сheck(n: integer) :=
n / 2.0 + n / 4.0 + n / 7.0 + 3 = n;
// РЕШАЕМ ЗАДАЧУ «Пифагорейское число» функционально
procedure Solve2();
begin
var n := 1.Step(1)
.Where(n -> Сheck(n))
.First;
// печатаем ответ:
Writeln($' Учеников было: {n}');
Writeln;
end;
begin
Writeln(' Пифагорейское число');
159
Writeln(' Математический фольклор. Задача Д4');
Writeln;
Solve();
Solve2();
end.
Запускаем программу и получаем от неё вполне ожидаемый ответ – у Пифагора было 28 учеников.
Пифагорейское число
Математический фольклор. Задача Д4
Учеников было: 28
Учеников было: 28
Графическое решение не оставляет сомнений в
правильности подсчёта учеников.
160
Проект Эпитафия Диофанта
Исходный код программы находится в файле Эпитафия Диофанта.pas.
В этом проекте мы докажем себе и людям, что не зря читаем математические книги, которые могут и озадачить!
Книга From 0 to Infinity in 26 Centuries. The Extraordinary Story of Maths на странице 49 может нас озадачить, но не поставить в тупик.
В книге нам предлагается задача про возраст Диофанта. Диофант — это
древнегреческий математик и отец алгебры. Он родил её без матери, как и
подобает настоящему мужчине.
161
Впрочем, ходят слухи, что алгебру придумали арабы, на что указывает и
название этого школьного предмета.
Задача красиво называется Эпитафия Диофанта и впервые упоминается в
книге головоломок древнегреческого философа Метродора, написанной в
шестом веке нашей эры.
В книге A Brief History of Mathematics. A Promenade through the Civilizations of
Our World, на странице 56 мы найдём не только эту задачу, но и линейное
уравнение для неё и даже ответ.
Ещё больше интересностей и полезностей ждёт нас в книге Ancient
Mathematics. History of Mathematics in Ancient Greece and Hellenism. На странице 350 мы найдём и эпитафию, и решение задачи, и ответ на неё. Также в
книге приводится интерпретация задачи, из которой вытекает другое решение, и соответственно другой ответ - 65 и одна треть года. Но обычно задача решается в целых числах, поэтому мы можем воспринимать это решение как курьёз.
Так что читать книги очень полезно!
Кто безмерно, но примерно владеет английским языком, тот может прочитать загадочную эпитафию прямо в книгах.
162
Но нас интересует не красивый литературный перевод эпитафии, а только
сугубая её суть, которая мягко выражается в следующих утверждениях:
Мы будем придерживаться классического решения задачи в целых числах.
В цикле for добавляем по одному году к возрасту Диофанта, начиная с единицы. Чтобы не рисковать с бесконечным циклом, вполне обосновано предположим, что Диофант прожил не более ста лет.
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// ищем возраст Диофанта в цикле:
for var x := 1 to 100 do begin
Пристально глядя на эпитафию, переписываем условие задачи на язык паскаля.
// проверяем условие задачи:
if (x / 6 + x / 12 + x / 7 + 5 + x / 2 + 4 = x) then begin
163
Когда условие выполнится, мы напечатаем ответ на экране и прервём цикл
оператором break.
// печатаем ответ:
Writeln($' Возраст Диофанта: {x}');
Writeln;
break;
end;
end;
end;
begin
Writeln(' Эпитафия Диофанта');
Writeln;
Solve();Writeln;
end.
Запускаем программу, которая сообщает нам, что Диофант прожил 84 года.
Эпитафия Диофанта
Возраст Диофанта: 84
Как это бывало уже не раз, в
условии задачи есть подсказки, которые позволяют
решить задачу быстро и в
своём уме. Если возраст Диофанта нацело делится на 7 и
12, то его возраст 84 года.
164
Проект Диофантово число
Исходный код программы находится в файле Диофантово число.pas.
Как мы прочно и уверенно это знаем, Диофант 84 года был греческим математиком. За это время он придумал диофантовы уравнения, а также несколько задачек на нашу голову. Более подробно о Диофанте читайте в Википедии — статья Диофант Александрийский. Там вы найдёте и Эпитафию
Диофанта в переводе на русский язык и решение задачи.
Первая задача этого (у)мудрёного грека называется Диофантово число. Я
отыскал её в книге Увлекательная математика под кодовым номером
АЭ14. И вот что он придумал:
Мы можем найти заданное число перебором в бесконечном цикле while,
начиная с единицы. Полный квадрат мы найдём умножением 200 (но не 5!)
на текущее значение переменной цикла. Для определения принадлежности
произведения к полным квадратам привлекаем функцию IsQuadrat:
165
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var num := 1;
// ищем число в цикле:
while True do begin
Теперь мы можем кратко и понятно записать условие задачи:
// проверяем условие задачи:
if ((num * 200).IsQuadrat() and
((num * 200).Sqrt() = 5 * num)) then begin
// печатаем ответ:
Writeln($' Число = {num}');
Writeln;
Когда условие выполнится, мы напечатаем ответ на экране и прервём цикл
оператором break:
break;
end;
num += 1
end;
end;
Запускаем программу и получаем немедленный ответ - число равно
восьми:
Диофантово число
Увлекательная математика. Задача АЭ14
Число = 8
Годится также число 0, но оно явно не древнегреческое.
166
Если не копировать полностью требование задачи, то условие можно записать проще:
if (5 * num = (num * 200).Sqrt())
Давайте для полного комплекта мы решим эту задачу функционально!
Как всегда и обычно, вчитываемся в условие задачи на предмет запоминания и осмысления.
Осмысления здесь потребуется немного. Нужно умножить искомое число на
5 или 200, а затем проверить, получился ли полный квадрат. Затем умножить искомое число на 200 или 5, а потом проверить, получился ли целый
квадратный корень из предыдущего полного квадрата. То есть есть 2 варианта проверки условия задачи.
Проверку условия задачи проводим в функции Check. Остальная часть решения задачи полностью повторяет наши недавние функциональные методы решения задач, поэтому просто копируем их из любого метода.
// условие задачи:
function Сheck(n: integer) :=
* 200).Sqrt());
((n * 200).IsQuadrat()) and ((n * 5) = (n
// РЕШАЕМ ЗАДАЧУ «Диофантово число» функционально
procedure Solve2();
begin
var n := 1.Step(1)
.Where(n -> Сheck(n))
.First;
// печатаем ответ:
Writeln($' Число = {n}');
Writeln;
end;
Запускаем программу. Как и раньше и должно быть, получаем ответ: Диофантово число равно восьми.
167
Диофантово число
Увлекательная математика. Задача АЭ14
Число = 8
Число = 8
Ещё одно функциональное решение этой задачи:
procedure Solve3();
begin
var m := 1;
var f: integer -> boolean :=
n -> (n * 200).IsQuadrat and
((n * 200).Sqrt = 5 * n);
Print(' Число =');
m.Step(1)
.Where(f)
.Take(1)
.Println;
Writeln;
end;
Проект Задача Диофанта II-10
Исходный код программы находится в файле Задача Диофанта II10.pas.
Древнегреческий математик Диофант написал несколько книг с математическими задачами, которые он сам и решил. Но его решения не всегда однозначны, поэтому мы можем решить их по-своему. В этом проекте мы разрешим и порешим задачу 10 из книги II. Вы можете найти её в книге Ancient
Mathematics. History of Mathematics in Ancient Greece and Hellenism, на странице 355. Там же есть и алгебраическое решение задачи.
168
Задача интересная, и мы за неё возьмёмся!
Поскольку нам нужно найти два числа, то самое простое решение состоит в
том, чтобы выписать два цикла for. Как мы узнали из книги, задача имеет,
по крайней мере, два решения, поэтому мы не можем прервать циклы,
найдя только первое решение.
Также здесь не годятся бесконечные циклы, но мы можем разумно ограничить поиски сверху. Большее число мы ищем, начиная с двойки, и учитываем, что второе число меньше первого.
Глядя вперёд, то есть в будущее, мы предусмотрительно обозначим разность квадратов целочисленной константой:
// РЕШАЕМ ЗАДАЧУ
const difference = 60;
procedure Solve();
begin
// ищем числа в циклах:
for var num1 := 2 to 1000 do
169
for var num2 := 1 to num1-1 do
Условие задачи крайне просто записывается на языке паскаля:
if (num1 * num1 - num2 * num2 = difference) then begin
Каждое найденное решение задачи, мы быстро, но аккуратно выписываем
на экране:
// печатаем ответ:
Writeln($' {num1}^2 - {num2}^2 = {difference}');
Writeln;
end;
end;
begin
Writeln(' Задача Диофанта II-10');
Writeln(' Увлекательная математика. Задача АЭ14');
Writeln;
Solve();
end.
Запускаем программу, которая находит два книжных решения. Других решений задача не имеет.
Задача Диофанта II-10
Увлекательная математика. Задача АЭ14
8^2 - 2^2 = 60
16^2 - 14^2 = 60
Умная мысля приходит опосля. Или вообще не приходит. Но в данном случае вполне умная мысля заключается в том, что мы легко обойдёмся одним
циклом, чтобы убить двух числовых зайцев!
170
В цикле for перебираем меньшие числа. Большее число равно сумме квадрата меньшего числа и разности. Но эта сумма также должна быть квадратом. У нас уже есть метод расширения для целых чисел IsQuadrat, который
легко проверит эту сумму.
В случае удачи мы напечатаем решение на экране.
Теперь мы можем проверять в цикле довольно большие числа, но с учётом
ограниченных возможностей типа int64:
uses MathExtensions;
procedure Solve2();
begin
// ищем числа в цикле:
for num2: int64 := 2 to 50000 do
if (num2 * num2 + difference).IsQuadrat() then begin
// печатаем ответ:
Writeln($' {Sqrt(num2 * num2 + difference)}^2 - {num2}^2 =
{difference}');
Writeln;
end;
end;
Запускаем программу, и она выдаёт нам те же самые два книжных решения
диофантовой задачи из времён глубокой древности.
Кто определяет константы, тот поступает мудро!
Безусловно, число 60 очень хорошо. Особенно для Диофанта, который
наверняка был знаком с шестидесятеричной системой счисления.
Мы без труда, то есть легко можем изменить в коде значение константы
difference и найти решения для других разностей. В свете недавних календарных событий нас должно заинтересовать число 2025.
Изменяем значение константы:
const difference = 2025;//60
171
Запускаем программу и находим новые диковинки для прошедшего года:
Задача Диофанта II-10
Увлекательная математика. Задача АЭ14
51^2 - 24^2 = 2025
53^2 - 28^2 = 2025
75^2 - 60^2 = 2025
117^2 - 108^2 = 2025
205^2 - 200^2 = 2025
339^2 - 336^2 = 2025
1013^2 - 1012^2 = 2025
Вы можете втиснуть в программу день своего рождения. А для жены или
для любимой девушки лучше использовать год её рождения. И ей будет
приятно, и вам, можно надеяться, тоже.
Но тут вас ждёт математический подвох - не для каждого числа задача
имеет решение. Тогда попробуйте пуститься на хитрость и задать возраст
подруги или день вашего знакомства. Памятных событий много, так что
нужное число непременно отыщется.
Как не раз шутили советские дикторы телевидения, вы будете смеяться, но
задача Диофанта благополучно дожила до нашего времени и живо бодрствует.
На ютубовском канале Mathe-Rätsel решали задачу, которая гораздо проще
той, что решал сам Диофант. Решение остроумное и простое. Для вычислений в уме вполне сгодится.
172
Но у нас уже есть программа для всех таких задачек, поэтому мы просто изменим значение константы difference и найдём единственное решение
этой задачи. Поскольку число 43 простое, то других решений нет.
Задача Диофанта II-10
Увлекательная математика. Задача АЭ14
22^2 - 21^2 = 43
Смекнув мозгой, вы легко докажете, что для всех простых разностей имеется единственное решение, которое легко находится тем же самым макаром, или макареной.
173
174
Проект Индийский храм
Исходный код программы находится в файле Индийский храм.pas.
Я почерпнул проблему Индийский храм из книги Математический фольклор, задача Д3.
Читайте внимательно условие задачи и крепко запоминайте!
Для вас должно быть очевидно, что алгебраически задача решается с помощью линейного уравнения. Это значит, что решение единственное, и его
можно найти в цикле.
В данном случае верхняя граница перебора нам неизвестна, поэтому применим бесконечный цикл. Поиски начнём с единицы:
// РЕШАЕМ ЗАДАЧУ
175
procedure Solve();
begin
// ищем число монет в бесконечном цикле:
var монет := 1;
while True do begin
Увеличиваем в бесконечном цикле while число монет первого посетителя
храма. Число монет остальных посетителей легко найти с помощью умножения. Когда общая сумма достигнет 132, цикл завершится:
// проверяем условие задачи:
if (монет + монет * 2 + монет * 2 * 3 + монет * 2 * 3 * 4 = 132)
then begin
// печатаем ответ:
Writeln($' Первый дал: {монет}');
Writeln;
break;
end;
монет += 1;
end;
end;
begin
Writeln(' Индийский храм');
Writeln(' Математический фольклор. Задача Д3');
Writeln;
Solve();
end.
Запускаем программу, и она быстро справляется с индийской задачей: первый посетитель дал 4 монеты.
Индийский храм
Математический фольклор. Задача Д3
Первый дал: 4
176
В Древней Греции такие задачи решали методом приведения к единице.
Он напоминает наш перебор, но умнее.
Действительно, если первый посетитель дал одну монету, то общая сумма
составит 33 монеты. Это в 4 раза меньше требуемой. Следовательно, во
столько же раз больше первый посетитель должен был дать монет. То есть
4.
Как хорошо, что у древних греков не было компьютеров!
Давайте решим задачу с помощью функции Range. В этом случае бесконечный цикл уже не годится, но совершенно очевидно, что первый посетитель
дал не более ста монет.
В метод расширения Where записываем условие задачи.
Тут нужно учесть, что функция Range возвращают последовательность
чисел. Но мы знаем, что ответ единственный, поэтому берём первый элемент:
procedure Solve2();
begin
var м := Range(1, 100)
.Where(монет -> монет + монет * 2 + монет * 2 * 3 + монет * 2
* 3 * 4 = 132);
// печатаем ответ:
Writeln($' Первый дал: {м.First}');
Writeln;
end;
Запускаем программу и получаем верный ответ:
Индийский храм
Математический фольклор. Задача Д3
Первый дал: 4
Первый дал: 4
177
Аналогично мы можем использовать одноимённый метод расширения:
procedure Solve3();
begin
var м := 100.Range()
.Where(монет -> монет + монет * 2 + монет * 2 * 3 + монет
* 2 * 3 * 4 = 132);
// печатаем ответ:
Writeln($' Первый дал: {м.First}');
Writeln;
end;
А с методом расширения Step мы можем создать бесконечный цикл. Но
здесь его нужно вовремя пресечь методом расширения First:
procedure Solve4();
begin
var м := 1.Step()
.Where(монет -> монет + монет * 2 + монет * 2 * 3 + монет * 2
* 3 * 4 = 132)
.First;
// печатаем ответ:
Writeln($' Первый дал: {м}');
Writeln;
end;
Люди, обладающие дальнозоркостью, часто впадают в разжирение, чтобы пузом отодвинуться
подальше от монитора и от тяжкой умственной работы головой.
С другой стороны, начальство поощряет разжирение, поскольку
разжиревший программист неохотно покидает насиженное место и тем самым дисциплинирует
коллектив к новым трудовым
успехам.
178
Проект Египетские коровы
Исходный код программы находится в файле Египетские коровы.pas.
179
Мы уже решили задачу Египетские кошки 2 из папируса Ринда (или Райнда).
Возраст папируса оценивается в 3700 лет. Он был найден англичанином
Риндом в конце девятнадцатого века.
Там есть для нас и другие задачи, и в этом проекте мы решим ещё одну задачу из этого папируса.
Русский перевод этой задачи вы можете найти в книге Увлекательная математика, задача АЭ8.
Задача легко решается и без перебора, поэтому мы применим компьютер
только из почтения к древности этой математической задачи.
В задаче дважды фигурируют трети от числа коров в стаде. И так как коровы паслись не консервированные, а цельные, то число голов в стаде
должно делиться без хвостатка на 9. Считаем коров в бесконечном цикле
while:
procedure Solve();
begin
var cow := 0;
180
while True do begin
if (cow * 2 / 3 * 1 / 3 = 70) then begin
// печатаем ответ:
var otvet := $' Коров было: {cow}';
Writeln(otvet);
break;
end;
cow += 9;
end;
end;
begin
Writeln(' Египетские коровы');
Writeln(' Увлекательная математика. Задача АЭ8');
Writeln;
Solve();
Writeln;
end.
Запускаем программу и выпускаем коров.
А коров оказалось триста пятнадцать
голов и хвостов:
Египетские коровы
Увлекательная математика. Задача АЭ8
Коров было: 315
Считаем коров функционально:
procedure Solve2();
begin
var cow := 0;
Write(' Коров было: ');
cow.Step(9)
.Where(cow -> cow * 2 / 3 * 1 / 3 = 70)
.Take(1)
181
.Println;
Writeln;
end;
begin
Writeln(' Египетские коровы');
Writeln(' Увлекательная математика. Задача АЭ8');
Writeln;
Solve(); Writeln;
Solve2();
end.
182
Проект Арабские голуби
Исходный код программы находится в файле Арабские голуби.pas.
В этом проекте мы урешаем навзничь задачу из арабских сказок тысячи и
одной ночи (а это была ночь четыреста пятьдесят восьмая), которые
были написаны много столетий тому назад.
Условие задачи довольно длинное и ветвистое, как и сами арабские сказки,
поэтому его нужно читать проникновенно – прямо в самую их глубинную и
голубиную суть.
Если вы так уже поступили или ещё только поступите, то эта задача непременно напомнит вам греческих мешконосов, которые недалеко ушли,
обременённые тяжёлыми мешками, возложенными на ни в чём неповинных домашних животных.
183
Поскольку никаких намёков на число голубей мы не имеем, то только первый цикл while можно сделать бесконечным. Во вложенном цикле for мы
разумно полагаем, что голубей было меньше тысячи:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var na := 0.0;
while True do begin
for var pod := 0 to 1000 do begin
Внутри вложенного цикла проверяем условия задачи:
// проверяем условие задачи:
if (pod - 1 = (na + pod) / 3) and
(na - 1 = pod + 1) then begin
Арабским сказкам вполне можно доверять, поэтому условия задачи непременно сбудутся в нашу пользу на экране монитора:
// печатаем ответ:
WriteLn($' На дереве:
{na}');
WriteLn($' Под деревом: {pod}');
Writeln;
exit;
end;
end;
na += 1;
end;
end;
begin
Writeln(' Арабские голуби');
Writeln(' Увлекательная математика. Задача АЭ15');
Writeln;
Solve();
end.
184
Запускаем программу. Голубей оказалось совсем немного:
Арабские голуби
Увлекательная математика. Задача АЭ15
На дереве:
7
Под деревом: 5
185
Проект Персидские яблоки
Исходный код программы находится в файле Персидские яблоки.pas.
Задача из старинной персидской легенды История Морадбальса, включённой в сборник сказок 1001 ночь. В ней мудрец задаёт девушке такую задачу:
Поучительная задача, актуальная и в наши дни: чтобы пройти через двери,
нужно делать взносы (или вклады).
Тут мы опять встречаемся с линейным уравнением, поэтому перебираем
число яблок в бесконечном цикле while, делимся ими со стражниками, отдавая каждому половину своих яблок, - до тех пор, пока в результате не
останется ровно десяток яблок:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
186
begin
// считаем яблоки:
var apple := 1;
while True do begin
// остаток яблок:
var ost := apple;
// делимся со стражниками:
loop 4 do
ost := ost div 2;
// проверяем условие задачи:
if (ost = 10) then begin
Writeln($' Яблок было: {apple}');
Writeln;
exit;
end;
apple += 1;
end;
end;
begin
Writeln(' Персидские яблоки');
Writeln(' Увлекательная математика. Задача АЭ16');
Writeln;
Solve();
end.
А девушка, видимо, не в первый раз промышляла яблоками, так как нарвала
их с большим запасом:
Персидские яблоки
Увлекательная математика. Задача АЭ16
Яблок было: 160
Задача легко решается в уме задом наперёд:
Если у женщины осталось 10 яблок, то до последнего стражника у неё
было 20 яблок. Рассуждая аналогично, получаем 40, 80 и 160 яблок. То
есть в начале этой фруктовой истории у женщины было 160 яблок.
187
procedure Solve2();
begin
// идём обратным путем:
// начинаем с 10 и умножаем на 2 четыре раза
var ost := 10;
loop 4 do
ost *= 2;
Writeln($' Яблок было: {ost}');
Writeln;
end;
Если сообразить умом, то задачу можно решить в функциональном стиле,
подсунув методу расширения Where условие задачи в «жадно сплюснутом
виде»:
procedure Solve3();
begin
// решаем задачу в функциональном стиле:
var a := 1000.Range()
.Where(apple -> apple/2/2/2/2 = 10)
.First();
Writeln($' Яблок было: {a}');
Writeln;
end;
begin
Writeln(' Персидские яблоки');
Writeln(' Увлекательная математика. Задача АЭ16');
Writeln;
Solve();
Solve2();
Solve3();
end.
Запуск программы прошёл успешно. Программа вышла на орбиту и работает в штатном режиме:
Персидские яблоки
Увлекательная математика. Задача АЭ16
188
Яблок было: 160
Яблок было: 160
Яблок было: 160
Решаем задачу ещё более функционально:
procedure Solve4();
begin
Write(' Яблок было: ');
1.Step()
.Where(apple -> apple / 2 / 2 / 2 / 2 = 10)
.Println();
Writeln;
end;
begin
Writeln(' Персидские яблоки');
Writeln(' Увлекательная математика. Задача АЭ16');
Writeln;
Solve();
Solve2();
Solve3();
Solve4();
end.
Персидские яблоки
Увлекательная математика. Задача АЭ16
Яблок было: 160
Яблок было: 160
Яблок было: 160
Яблок было: 160
189
190
Проект Римские адвокаты
Исходный код программы находится в файле Римские адвокаты.pas.
Крючкотворная римская задача из первого века до нашей эры:
Это задача АЭ13 из книги Увлекательная математика.
Мы полагаем, что задача решается в целых числах. Тогда легко найти долю
вдовы в цикле for. Совершенно очевидно, что она не может получить
больше 3500 динариев (и даже значительно меньше). Тогда сын получит
вдвое больше, а дочь вдвое меньше:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// ищем решение в цикле:
for var вдова := 0 to 3500 do begin
191
// доля сына:
var сын := 2 * вдова;
// доля дочери:
var дочь := вдова div 2;
// условие задачи:
if (вдова + сын + дочь = 3500) then begin
Когда условие задачи выполнится, мы напечатаем ответ на экране:
// печатаем ответ:
Writeln($' Вдове: {вдова}');
Writeln($' Сыну:
{сын}');
Writeln($' Дочери: {дочь}');
Writeln;
break;
end;
end;
end;
begin
Writeln(' Римские адвокаты');
Writeln(' Увлекательная математика. Задача АЭ13');
Writeln;
Solve();
end.
Всё – мы разделили наследство по-честному, то есть по древнеримским законам:
Римские адвокаты
Увлекательная математика. Задача АЭ13
Вдове: 1000
Сыну:
2000
Дочери: 500
Вот если бы наследство делили современные адвокаты, то плакали бы их
денежки горькими слезами!
192
Чисто для тренировки себя в программировании переписываем решение
задачи в функциональном стиле. Если вы внимательно и вдумчиво решали
предыдущую задачу про персидские яблоки, то затруднений при дележе
чужих денег возникнуть не должно.
procedure Solve2();
begin
// решаем задачу в функциональном стиле:
var a := 3500.Range()
.Where(вдова -> вдова + вдова * 2 + вдова / 2 = 3500)
.First();
// печатаем ответ:
// доля вдовы:
var вдова := a;
// доля сына:
var сын := 2 * вдова;
// доля дочери:
var дочь := вдова / 2;
Writeln($' Вдове: {вдова}');
Writeln($' Сыну:
{сын}');
Writeln($' Дочери: {дочь}');
Writeln;
end;
Успешный запуск программы на экран подтверждает, что, как деньги ни
дели, а всё равно мало!
Римские адвокаты
Увлекательная математика. Задача АЭ13
Вдове: 1000
Сыну:
2000
Дочери: 500
Вдове: 1000
Сыну:
2000
Дочери: 500
Используем для решения задачи метод расширения To:
193
procedure Solve3();
begin
// решаем задачу в функциональном стиле:
var a := 1.To(3500)
.Where(вдова -> вдова + вдова * 2 + вдова / 2 = 3500)
.First();
// доля вдовы:
var вдова := a;
// доля сына:
var сын := 2 * вдова;
// доля дочери:
var дочь := вдова / 2;
Writeln($' Вдове: {вдова}');
Writeln($' Сыну:
{сын}');
Writeln($' Дочери: {дочь}');
Writeln;
end;
И ещё одно решение в функциональном стиле:
procedure Solve4();
begin
var vdova := (0..3500)
.Where(vdova -> vdova + 2 * vdova {syn} + vdova div 2 {doch} = 3500)
.ElementAt(0);
// доля вдовы:
var вдова := vdova;
// доля сына:
var сын := 2 * vdova;
// доля дочери:
var дочь := vdova/ 2;
Writeln($' Вдове: {vdova}');
Writeln($' Сыну:
{сын}');
Writeln($' Дочери: {дочь}');
Writeln;
end;
begin
Writeln(' Римские адвокаты');
Writeln(' Увлекательная математика. Задача АЭ13');
Writeln;
Solve();
Solve2();
Solve3();
Solve4();
194
end.
195
Проект Гаусс
Исходный код программы находится в файле Гаусс.pas.
Задача 5 из книги Математическая шкатулка, страница 15, пересказывает
известный исторический анекдот про немецкого математика Карла Фридриха Гаусса, как Карлуша ловко обманул учителя, когда ещё учился в
школе.
Нетрудно в этом ряде чисел увидеть арифметическую прогрессию, начинающуюся с единицы и насчитывающую 100 членов. Разность арифметической прогрессии равна 1. Зная все параметры этого ряда, мы быстро
найдём его сумму.
Учитель, конечно, предполагал, что школяры примутся вычислять сумму
ряда последовательным прибавлением очередного члена ряда к текущей
сумме.
196
Давайте решим эту простую задачу на последовательности.
Функция Range генерирует заданную арифметическую прогрессию, а метод расширения для последовательностей Sum возвращает сумму всех элементов последовательности. В данном случае элементы последовательности в проверке не нуждаются, поэтому метод Where нам без надобности.
Всё решение уложилось в одну короткую строчку.
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var summa := Range(1, 100).Sum();
//var summa = RangeABS(1, 999, 2).Sum();
//
var summa = RangeABS(5, 100, 5).Sum();
Writeln($' Сумма ряда равна {summa}');
Writeln;
end;
begin
Writeln(' Гаусс');
Writeln(' Математичекая шкатулка, страница 15, задача 5');
Writeln;
Solve();
end.
Запускаем программу, которая выдаёт нам хитрость Карла Фридриховича
Гаусса – сумма чисел равна 5050.
Гаусс
Математичеcкая шкатулка, страница 15, задача 5
Сумма ряда равна 5050
Решите задачу и обычным способом – с циклом for.
197
Подставляя нужные значения в функцию Range, мы быстро найдём сумму
любой арифметической прогрессии. Например, давайте решим задачу 7.1
из книги Математическая шкатулка, страница 15.
procedure Solve();
begin
//var summa := Range(1, 100).Sum();
var summa := Range(1, 999, 2).Sum();
Writeln($' Сумма ряда равна {summa}');
Writeln;
end;
Гаусс
Математичеcкая шкатулка, страница 15, задача 5
Сумма ряда равна 250000
Задача 838 из книги Математическая шкатулка, страница 128:
И опять одна строка программы решает задачу:
procedure Solve();
begin
//var summa := Range(1, 100).Sum();
//var summa := Range(1, 999, 2).Sum();
var summa := Range(5, 100, 5).Sum();
198
Writeln($' Сумма ряда равна {summa}');
Writeln;
end;
Гаусс
Математичеcкая шкатулка, страница 15, задача 5
Сумма ряда равна 1050
Аналогично, то есть точно так же и даром вы можете решить любые задачи
на арифметические прогрессии. Здесь главное – не ошибиться с параметрами – начальным членом, числом элементов и разностью прогрессии.
199
Проект Кахунский папирус
Исходный код программы находится в файле Кахунский папирус.pas.
В этом проекте мы решим очередную папирусную задачу про дроби и квадраты чисел. Это очень старая задача из Кахунского папируса.
Русский перевод этой задачи смотрите в номере три журнала Наука и жизнь
за 1963 год, на странице 38.
Решение задачи очень простое. Главное – не запутаться в дробях.
Нам нужно найти два числа. Причём мы смело можем предполагать, что оба
числа - целые. Без лишних размышлений ищем числа в бесконечном цикле
while, начиная с единицы:
procedure Solve();
200
begin
var num1 := 1;
// ищем числа в бесконечном цикле:
while True do begin
Второе число находим из пропорции, данной в описании задачи:
// находим второе число:
var num2 := num1 / 2 * 3 / 2;
Условие задачи гласит, что сумма квадратов чисел должна равняться четырёмстам:
// проверяем условие задачи:
if (num1 * num1 + num2 * num2 = 400) then begin
Когда это условие непременно выполнится, мы напечатаем ответ на
экране:
// печатаем ответ:
Writeln($' Первое число = {num1}');
Writeln($' Второе число = {num2}');
Writeln();
break;
end;
num1 += 1;
end;
end;
begin
Writeln(' Кахунский папирус');
Writeln(' Наука и жизнь 1963, №3, стр. 38');
Writeln;
Solve();
Writeln;
end.
201
Поразмышляйте на досуге, почему эта задача имеет единственное решение.
А мы тут же запускаем программу, которая называет нам оба числа — 16 и
12:
Кахунский папирус
Наука и жизнь 1963, №3, стр. 38
Первое число = 16
Второе число = 12
Задача показывает, что древние математики не хуже нашего умели вычислять дроби и квадраты чисел!
Раскручиваем разворот папируса функционально:
// ПРОВЕРЯЕМ УСЛОВИЕ ЗАДАЧИ
function Uslovie(num1 : integer) : boolean;
begin
var num2 := num1 / 2 * 3 / 2;
Result := num1 * num1 + num2 * num2 = 400;
end;
procedure Solve2();
begin
var num1 := 1.Step
.Where(num1 -> Uslovie(num1))
.ElementAt(0);
// печатаем ответ:
Writeln($' Первое число = {num1}');
Writeln($' Второе число = {num1 / 2 * 3 / 2}');
Writeln();
end;
begin
Writeln(' Кахунский папирус');
Writeln(' Наука и жизнь 1963, №3, стр. 38');
202
Writeln;
Solve(); Writeln;
Solve2();
end.
203
Проект Берлинский папирус
Исходный код программы находится в файле Берлинский папирус.pas.
Ещё одна древневавилонская задача. На этот раз из Берлинского папируса:
Русский перевод этой задачи смотрите в номере три журнала Наука и жизнь
за тысячу девятьсот шестьдесят третий год, на странице 38, задача 2.
Нам нужно найти два числа. Мы можем предположить, что оба числа - целые. Ищем числа в бесконечном цикле while.
Так как стороны квадратов выражаются целыми числами, то длина стороны меньшего квадрата должна быть кратна трём.
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
204
var len := 0.0;
// ищем длину меньшего квадрата в бесконечном цикле:
while True do begin
Длину стороны большего квадрата находим из пропорции, данной в условии задачи:
// длина второй стороны:
var len2 := len * 4 / 3;
Условие задачи гласит, что сумма квадратов длин сторон должна равняться
сотне:
if (len * len + len2 * len2 = 100) then begin
Когда это условие выполнится, мы напечатаем ответ на экране:
// печатаем ответ:
Writeln($' Длина стороны меньшего квадрата = {len}');
Writeln($' Длина стороны большего квадрата = {len2}');
Writeln;
break;
end;
len += 3;
end;
end;
begin
Writeln(' Берлинский папирус');
Writeln(' Наука и жизнь 1963-03-38-2');
Writeln;
Solve(); Writeln;
end.
Запускаем программу, которая называет нам оба числа - 6 и 8.
205
Берлинский папирус
Наука и жизнь 1963-03-38-2
Длина стороны меньшего квадрата = 6
Длина стороны большего квадрата = 8
Мы можем скрутить папирус в рулон и функционально:
// ПРОВЕРЯЕМ УСЛОВИЕ ЗАДАЧИ
function Uslovie(len : integer) : boolean;
begin
// длина второй стороны:
var len2 := len * 4 / 3;
Result := len * len + len2 * len2 = 100;
end;
procedure Solve2();
begin
var len := 0.Step(3)
.Where(len -> Uslovie(len))
.ElementAt(0);
// печатаем ответ:
Writeln($' Длина стороны меньшего квадрата = {len}');
Writeln($' Длина стороны большего квадрата = {len * 4 / 3}');
Writeln; ;
end;
begin
Writeln(' Берлинский папирус');
Writeln(' Наука и жизнь 1963-03-38-2');
Writeln;
Solve();
Writeln;
Solve2();
end.
206
207
Проект Индийские квадраты
Исходный код программы находится в файле Индийские квадраты.pas.
Задачу Индийские квадраты вы легко и без труда найдёте в журнале Наука
и жизнь, номер 3 за 1963 год, задача 4 на странице 38.
Это натуральная индийская задача VIII века из «Бахшалийской» рукописной
арифметики.
Искомое число не меньше 11, а верхнюю границу мы установим в сотню.
Как обычно, ищем число в цикле for.
uses MathExtensions;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
208
begin
// ищем число в цикле:
for var num := 11 to 100 do begin
Нам нужно найти 2 числа. Одно число на 5 больше искомого, а второе на 11
меньше.
// квадратные числа:
var n5 := num + 5;
var n11 := num - 11;
По условию задачи, оба числа должны быть полными квадратами. На этот
случай у нас уже есть метод расширения IsQuadrat. С его помощью условие
задачи записывается коротко и ясно, а потому прекрасно:
// условие задачи:
if (n5.IsQuadrat()) and (n11.IsQuadrat()) then
Когда условие выполнится, мы напечатаем ответ на экране:
Writeln($' Число = {num}');
end;
Writeln;
end;
begin
Writeln(' Индийские квадраты');
Writeln(' Наука и жизнь, №3, 1963 год, задача 4, стр. 38');
Writeln;
Solve();
end.
Запускаем программу, которая находит два числа, удовлетворяющих условиям задачи:
Индийские квадраты
209
Наука и жизнь, №3, 1963 год, задача 4, стр. 38
Число = 11
Число = 20
При num = 11 мы получаем квадраты 16 и 0, а при num = 20 – 25 и 9.
Для расширения кругозора и укрепления навыков давайте решим задачу
более курьёзным способом.
Функция Range генерирует последовательность целых чисел. Передаём ей
числа - начальное и конечное. Она заменяет цикл for циклом foreach.
Остальной код можно оставить без изменений.
procedure Solve2();
begin
// диапазон значений для искомого числа:
var range := Range(11, 100);
foreach var num in range do begin
// квадратные числа:
var n5 := num + 5;
var n11 := num - 11;
// условие задачи:
if (n5.IsQuadrat()) and (n11.IsQuadrat()) then
Writeln($' Число = {num}');
end;
Writeln;
end;
Запускаем программу и получаем те же числа, что и раньше, то есть мы сработали на ура!
Эх, ухнем и тряхнём стариной, решаясь изощрённо-функционально решать
задачу Индийские квадраты, уже не однажды нами решённую другими способами. Решаем задачу в функциональном стиле:
procedure Solve3();
begin
// диапазон значений для искомого числа:
210
var num := Range(11, 100)
К счастью для нас, условие задачи простое и понятное, как и сами квадраты. Нам нужно найти, или отыскать, или вычислить число, которое превращается в полный квадрат при добавлении к нему пятёрки или вычитании одиннадцати:
.Where(n -> (n + 5).IsQuadrat() and (n - 11).IsQuadrat());
// печатаем ответ:
foreach var n in num do
Writeln($' Число = {n}');
Writeln;
end;
Мы можем изощриться и того больше!
Проверку условия задачи проводим в функции Check, аккуратно записывая
в неё условие задачи:
// условие задачи:
function Сheck(n: integer) := ((n + 5).IsQuadrat()) and ((n 11).IsQuadrat());
// РЕШАЕМ ЗАДАЧУ «Индийские квадраты» функционально
procedure Solve4();
begin
Начинаем поиски с одиннадцати, поскольку нам не нужны квадратные
корни из отрицательных чисел.
var res := 11.Step(1)
.Where(n -> Сheck(n))
Из предыдущего похвального опыта мы знаем, что задача имеет 2 решения, поэтому сообщаем это преприятнейшее известие методу расширения
Take.
211
.Take(2)
.ToArray;
// печатаем ответ:
foreach var n in res do
Writeln($' Число = {n}');
Writeln;
end;
Запускаем программу. И она выдаёт нам числа 11 и 20, которые, как нам уже
достоверно известно и ведомо, и есть правильный ответ на индийскую задачу.
Индийские квадраты
Наука и жизнь, №3, 1963 год, задача 4, стр. 38
Число = 11
Число = 20
Число = 11
Число = 20
Число = 11
Число = 20
Число = 11
Число = 20
Для любителей разнообразия – ещё одно функциональное решение задачи:
procedure Solve5();
begin
var m := 11;
var f: integer -> boolean :=
n -> (n + 5).IsQuadrat and
(n - 11).IsQuadrat;
Print(' Число =');
m.Step(1)
.Where(f)
.Take(2)
212
.Println;
Writeln;
end;
213
Проект Вавилонские квадраты и кубы
Исходный код программы находится в файле Вавилонские квадраты
и кубы.pas.
В этом проекте мы решим задачу Вавилонские квадраты и кубы. Русский
перевод этой задачи смотрите в номере три журнала Наука и жизнь за тысячу девятьсот шестьдесят третий год, на странице 38, задача 5.
В Древнем Вавилоне пользовались 60ричной системой счисления. Для записи
чисел в такой системе требуется 60 цифр,
но, с другой стороны, она очень удобна,
так число 60 нацело делится на 2, 3, 4, 5, 6,
10, 12, 15, 20, 30 и 60. Недаром 60-ричная
система счисления дожила до наших
дней. Например, время и углы мы измеряем именно в этой системе счисления.
Математические вычисления в те далёкие времена были непростым делом, поэтому нередко встречаются задачи,
в которых нужно просто вычислить значение каких-либо выражений.
Дальше мы займёмся проверкой утверждений, записанных на двух глиняных табличках Сенкере.
Так как вавилонские числа записаны в шестидесятиричной системе, в которой разряды разделены точками, то для их хранения мы определим строковые массивы. В них мы запишем числа, которые нам предстоит возвести
в квадрат и в куб.
Затем мы извлекаем пары строк из этих массивов и переводим их в числа.
Для перевода строк с шестидесятиричными числами в десятичные такой
функции в паскале, естественно, нет, так что нам предстоит написать её самостоятельно.
214
215
Функция Get60 получает строку с шестидесятиричным числом и разбивает
её на разряды:
// КОНВЕРТИРУЕМ СТРОКУ С 60-РИЧНЫМ ЧИСЛОМ В
// В ДЕСЯТИЧНОЕ ЧИСЛО
function Get60(s60 : string) : integer;
begin
var res := 0;
// разбиваем строку по точкам:
var str := s60.Split(['.']);
// конвертируем в десятичное
var n60 := Get60(s60);
В массиве строк str теперь хранятся подстроки с отдельными разрядами
шестидесятеричного числа.
Начиная с последнего разряда, умножаем десятичное число, соответствующее этому разряду на 60 в степени номера разряда: 0, 1, 2 и так далее. То
есть сначала на 1, затем на 60, затем на 3600 и так далее, пока разряды шестидесятеричного числа не закончатся:
// вычисляем десятичное число:
var k := 1;
for var i := str.Length - 1 downto 0 do begin
Частичные произведения аккумулируются в переменной res, которая и возвращается в вызывающую процедуру:
res += Convert.ToInt32(str[i]) * k;
k *= 60;
end;
Result := res;
end;
Второе число – десятичное, поэтому мы конвертируем строку в число с помощью метода ToInt32 класса Convert.
216
// квадрат:
var n := Convert.ToInt32(vars60[i + 1]);
var n2 := n * n;
Проверяем, равно ли шестидесятеричное число квадрату десятичного
числа.
var answer := '';
// проверяем условия:
if (n60 = n2) then
Печатаем результат проверки на экране:
answer := $' 60-ричное число {s60} ({n60}) равно {n} в квадрате
({n2})'
else
answer := $' 60-ричное число {s60} равно {n} в квадрате ({n2})';
Writeln(answer);
end;
end;
Вторую табличку проверяем так же, но сравниваем шестидесятеричные
числа с кубами десятичных чисел.
procedure Solve2();
begin
var vars60 := [ '2.5', '5',
'3.36', '6',
'5.43', '7',
'8.32', '8',
'1.8.16', '16',
'9.6.8', '32' ];
for var i := 0 to vars60.Length-1 step 2 do begin
// 60-ричное число:
var s60 := vars60[i];
// конвертируем в десятичное
217
var n60 := Get60(s60);
// куб:
var n := Convert.ToInt32(vars60[i + 1]);
var n3 := n * n * n;
var answer := '';
// проверяем условия:
if (n60 = n3) then
answer := $' 60-ричное число {s60} ({n60}) равно {n} в кубе
({n3})'
else
answer := $' 60-ричное число {s60} равно {n} в кубе ({n3})';
Writeln(answer);
end;
end;
begin
Writeln(' Вавилонские квадраты и кубы');
Writeln(' Наука и жизнь 1963-03-38-5');
Writeln;
Solve(); Writeln;
Solve2(); Writeln;
end.
Что касается собственно проверки табличных данных, то наша программа
показывает и доказывает, что древние вавилоняне не зря портили глину –
ни одной ошибки в их вычислениях мы не обнаружили!
Вавилонские квадраты и кубы
Наука и жизнь 1963-03-38-5
60-ричное
60-ричное
60-ричное
60-ричное
60-ричное
60-ричное
60-ричное
число
число
число
число
число
число
число
1.21 (81) равно 9 в квадрате (81)
2.1 (121) равно 11 в квадрате (121)
2.49 (169) равно 13 в квадрате (169)
3.45 (225) равно 15 в квадрате (225)
4.16 (256) равно 16 в квадрате (256)
25.21 (1521) равно 39 в квадрате (1521)
56.4 (3364) равно 58 в квадрате (3364)
60-ричное
60-ричное
60-ричное
60-ричное
60-ричное
число
число
число
число
число
2.5 (125) равно 5 в кубе (125)
3.36 (216) равно 6 в кубе (216)
5.43 (343) равно 7 в кубе (343)
8.32 (512) равно 8 в кубе (512)
1.8.16 (4096) равно 16 в кубе (4096)
218
60-ричное число 9.6.8 (32768) равно 32 в кубе (32768)
А это образчик современного глиняного искусства:
219
Назад – в будущее!
Проект Китайские купцы
Исходный код программы находится в файле Китайские купцы.pas.
Как показала жизнь, мы недалеко ушли в отрыв от китайской математики.
Нас тут же, по пятам настигла задача про китайских купцов из трактата Девять отделов искусства счёта.
Но к нам эта задача добралась не прямиком по великому китайскому математическому пути, а окольным путём - из книги Г.Н.Попова Сборник исторических задач по элементарной математике, изданной в 1938-ом году
(первое издание пришлось на 1932-ой год), где она получила номер 99.
220
Тут мы, конечно, должны поблагодарить Попова за перевод задачи с китайских иероглифов на русские буквы с сохранением математической интриги
в деталях.
Чтобы увидеть и рассмотреть эти детали, тонкости и подробности, тщательно и хорошенько вчитываемся и вгрызаемся в условие задачи.
Закончив грызню деталей, мы должны понять и осознать, что в задаче
скрываются два неизвестных – сами купцы в неведомом количестве и стоимость товара незнаемой цены. Это плохо, но не жуть!
Китайцы придумали эту задачу, когда ещё не было компьютеров, поэтому
она имеет красивое математическое решение. Но компьютеры испортили
всё! И теперь они рьяно покушаются на наш, человеческий интеллект. Не
потакая, но и не потворствуя чипам и дейлам, мы всё-таки, взяв тяжкий грех
и труд на душу, решим задачу на паскале.
Для поиска, отыскания и задержания двух разыскиваемых неизвестных нам
нужно привлечь 2 цикла. Первый цикл мы сделаем бесконечным – нам это
ничем пагубным и губительным не грозит. Со вторым циклом мы не можем
поступить столь неосторожно и опрометчиво, поэтому ограничим его
221
сверху разумным граничным пределом. Если это не поможет, то мы дадим
волю своим чувствам и волеизъявлению в сторону увеличения значения
переменной второго цикла.
Для проверки условия задачи мы вынуждены принуждением и волею судеб
составить пару уравнений, которые, с математической точки зрения, образуют систему, которая легко решается на раз-два. Для этого достаточно из
первого уравнения вычесть второе. Так мы найдём число купцов-удальцов.
Дальнейшие рассуждения продолжать просто неловко.
Но мы тут пишем компьютерные программы, которые завсегда пригодятся.
А в это время наши дедуктивные циклы уже нашли оба неизвестных и разоблачили их на экране в понятной и доходчивой форме:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var купцы := 0;
while True do begin
for var цена := 0 to 1000 do begin
// печатаем ответ:
if (8 * купцы = цена + 3) and (7 * купцы = цена - 4) then begin
Writeln($' Купцов: {купцы} Стоимость товара = {цена}');
Writeln;
exit;
end;
end;
купцы += 1;
end;
end;
begin
Writeln(' Китайские купцы');
Writeln(' Г.Н.Попов «Сборник исторических задач по элементарной математике».
Задача 99');
Writeln;
Solve();
end.
Запускаем программу на поиски неизвестных китайских купцов.
222
Совершенно неожиданно для древних китайцев мы насчитали семь купцов, которые порывались купить товаров на 53 каша. Но в голове у нас не
каша – победа будет наша!
Китайские купцы
Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 99
Купцов: 7
Стоимость товара = 53
223
Проект Китайские воры
Исходный код программы находится в файле Китайские воры.pas.
Вот по-настоящему детективная и дедуктивная история из китайской математики. До нас её донесла книга Попова Сборник исторических задач по
элементарной математике, где она пришнурована к делу номер 101.
Оригинальное дело о краже с похищением риса из бочек было расследовано
в трактате Девять отделов искусства счёта, который, в свою очередь, представляет собой комментарий, написанный в XIII веке, к трактату Таен лиишу, увидевшему свет в начале VIII века.
Мы не будем философствовать о природе вещей и людей, а сразу перейдём
к протоколу дознания, который, согласно китайским церемониям длинен и
запутан.
224
Для простоты понимания тех давних событий, давайте определимся с
остатками риса в бочках:
• В первой бочке риса осталось 1 го.
• Во второй бочке - 1 шинг 4 го = 14 го.
• И в третьей бочке - 1 го.
Следствие показало, что
• Первый вор использовал для кражи риса лопату и брал рис из первой
бочки.
• Второй вор употребил для этого дела башмак и брал рис из второй
бочки.
• Третий вор принёс из дома миску и брал рис из третьей бочки.
Оттуда же нам известны ёмкости воровских инструментов:
• Лопата первого вора 1 шинг 9 го = 19 го.
• Башмак второго вора 1 шинг 7 го = 17 го.
• Миска третьего вора 1 шинг 2 го = 12 го.
225
К счастью для нас, все бочки были одинаковой ёмкости. Истинная ёмкость
бочек нам неизвестна, поэтому вполне уместно обозначить её буквой х. Как
и всё прочее, мы будем измерять ёмкость бочек в го.
Рисоворы украли у рисоводов подручными инструментами целое число
раз, что намекает нам на поиск ёмкости бочек в бесконечном цикле.
Обозначим число воровских подходов буквами a, b и c.
Тогда по условию задачи мы можем составить такие уравнения:
• Для первой бочки: x -19a = 1.
• Для второй бочки: x -17b = 14.
• Для третьей бочки: x -12c = 1.
Откуда:
• a = (x – 1) / 19
• b = (x – 14) / 17
• c = (x – 1) / 12
При этом числа a, b и c должны быть целыми.
Целочисленность переменных a, b и c, которые сидели бы на трубе, если
бы им не пришла труба, - ключ к решению задачи. Поскольку при вычислении их значений мы обязаны делить на вещественные числа, то эти переменные также получат вещественные значения.
Ёмкость бочек с рисом, напротив, выражается целыми числами, что мы и
демонстрируем в бесконечном цикле while.
uses MathExtensions;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var x := 1;
while True do begin
var a := (x - 1) / 19.0;
var b := (x - 14) / 17.0;
226
var c := (x - 1) / 12.0;
Получив очередные вещественные значения и доказательства для переменных a, b и c, проверяем их на целочисленность:
// условие задачи:
var uslovie := a.IsInteger() and b.IsInteger() and c.IsInteger();
for var цена := 0 to 1000 do begin
Мы безрассудно и безоглядно уверены, что китайская задача без подвоха,
поэтому имеет решение. И вот, когда мы установим целочисленность всех
без исключения переменных a, b и c, то предъявим рисовым воришкам счёт
насчёт рисового счёта!
// печатаем ответ:
if (uslovie) then begin
Writeln($' Первый похитил: {a * 19} го');
Writeln($' Второй похитил: {b * 17} го');
Writeln($' Третий похитил: {c * 12} го');
Writeln;
exit;
end;
end;
x += 1;
end;
end;
begin
Writeln(' Китайские воры');
Writeln(' Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 101');
Writeln;
Solve();
end.
Запускаем программу для выявления математической подноготной этого
вопиющего случая кражи с похищением бочкового риса. Наша программа
227
выдаёт размеры краж в го, но вы можете перевести их в ши, тау и шинги,
если поимеете такое неожиданное желание.
Китайские воры
Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 101
Первый похитил: 3192 го
Второй похитил: 3179 го
Третий похитил: 3192 го
228
Проект Задача Суань Шу
Исходный код программы находится в файле Задача Суань Шу.pas.
В Китае бытуют и присутствуют собой не только рисовые воры, но и честные граждане - покупатели чая. Само английское название Китая – China –
переводится на русский язык как Чай? – На! Из этого следует, и мы можем
сделать вывод, что китайцы любят чай, чтобы его церемонно пить.
Если вам захочется больше узнать о чайных китайских церемониях и проникнуть в самую их глубь, то обязательно
посмотрите наицеремоннейший чайный номер из шоу
Уральских пельменей День сырка. И решите: любите ли вы
чай так же, как его любят китайцы.
Однако хватит впустую разводить чайные церемонии! Давайте-ка вернёмся
восвояси – прямо к задаче 109 из книги Попова Сборник исторических задач
по элементарной математике, которая называется Задача Суань Шу. Но
правильнее говорить Суань шу шу. Именно так называется самый древний
229
китайский математический трактат, который нам известен. А задача - одна
из древнейших в мире задач на системы линейных уравнений.
И тут я обязан призвать вас к внимательному прочтению текста задачи,
чтобы увидеть и распознать в нём вещественное число, которое препятствует непосредственному перебору значений. Этого можно избежать, если
заранее знать ответ на задачу, но это всё равно, что читать детектив задом
наперёд – ни интриги, ни любви...
Если вам это славно удалось, то вы должны были составить систему из
двух линейных уравнений:
3x + 6y = 27
12x + 4y = 56
Где x – это цена одного фунта первого сорта чая, а y – второго.
Эта система уравнений крайне легко и просто решается школьными методами, а для компьютера сгодится и нам пригодится метод Крамера, который тоже всем и повсюду известен и досягаем:
230
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// система уравнений:
// 3x + 6y = 27
// 12x + 4y = 56
// коэффициенты уравнений =>
// первое уравнение:
var a1 := 3.0;
var b1 := 6.0;
var c1 := 27.0;
// второе уравнение:
var a2 := 12.0;
var b2 := 4.0;
var c2 := 56.0;
// вычисляем определители по методу Крамера =>
// главный определитель:
var D := a1 * b2 - a2 * b1;
// определитель для x:
var Dx := c1 * b2 - c2 * b1;
// определитель для y:
var Dy := a1 * c2 - a2 * c1;
// решаем задачу:
var x := Dx / D;
var y := Dy / D;
// печатаем ответ:
Writeln($' Фунт первого сорта чая стоит {x} дяо.');
Writeln($' Фунт второго сорта чая стоит {y} дяо.');
end;
begin
Writeln(' Задача Суань Шу');
Writeln(' Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 109');
Writeln;
Solve();
end.
231
Запускаем программу, чтобы узнать почём фунт лиха и двух сортов китайского чая. Метод Крамера для борьбы с системами линейных уравнений отменно справляется с развеской китайского чая: фунт первого сорта чая
стоит 3 и 8 десятых дяо, а второго – 2 и 6 десятых. Пора к столу – пить чай,
который мы заслуженно заслужили. Но умоляю вас: никаких китайских церемоний!
Задача Суань Шу
Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 109
Фунт первого сорта чая стоит 3.8 дяо.
Фунт второго сорта чая стоит 2.6 дяо.
232
Проект Китайские бараны
Исходный код программы находится в файле Китайские бараны.pas.
Простая задача с юбилейным номером 100 из книги Попова:
Решаем систему уравнений методом Крамера:
##
// РЕШАЕМЗАДАЧУ
procedure Solve();
begin
// система уравнений:
// 5x + 2y = 10
// 2x + 8y = 8
// решаем методом Крамера:
var D := 5 * 8 - 2 * 2;
// 40 - 4 = 36
233
var Dx := 10 * 8 - 8 * 2;
var Dy := 5 * 8 - 2 * 10;
var x := Dx / D;
var y := Dy / D;
// 80 - 16 = 64
// 40 - 20 = 20
// 64/36 = 1.777...
// 20/36 = 0.555...
// печатаем ответ:
Writeln($' Вол стоит {Round(x, 3)} таэля');
Writeln($' Баран стоит {Round(y, 3)} таэля');
Writeln;
end;
Writeln(' Китайские бараны');
Writeln(' Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 100');
Writeln;
Solve();
Китайские бараны
Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 100
Вол стоит 1.778 таэля
Баран стоит 0.556 таэля
Это классическая задача на систему линейных уравнений, которая демонстрирует, что уже в древности математики умели решать такие системы,
хотя и делали это другими методами, чем мы сегодня.
Проект Китайские цыпочки
Исходный код программы находится в файле Китайские цыпочки.pas.
Старинная китайская задача про куро-петушков из книги Математика
Древнего Китая.
234
Обозначаем математическую цифирь внятными переменными:
// РЕШАЕМ ЗАДАЧУ
function Solve(): integer;
begin
var petuh := 5;
var kuritsa := 3;
var tsyplenok := 1/3;
var summa := 100;
var ptits := 100;
Проще всего решить задачу в трёх циклах. Но тут следует учесть, что стоимость цыплят в цянях выражается вещественным числом.
for var p := 0 to summa div petuh do
for var k := 0 to summa div kuritsa do
for var t := 0 to Round(summa/tsyplenok) do begin
if (p + k + t <> ptits) then
continue;
if (p * petuh + k * kuritsa + t * tsyplenok <> summa) then
235
continue;
Writeln($' Петухов: {p}
Куриц: {k} Цыплят: {t}');
end;
Writeln;
end;
begin
Writeln(' Китайские цыпочки');
Writeln(' Математика древнего Китая, сс.42-43');
Writeln(' Трактат Чжан Цю-цзяня. Задача 38');
Writeln;
Solve;
end.
Как это часто бывает с древними задачами, мы
получили несколько решений:
Китайские цыпочки
Математика древнего Китая, сс.42-43
Трактат Чжан Цю-цзяня. Задача 38
Петухов:
Петухов:
Петухов:
Петухов:
0 Куриц: 25
4 Куриц: 18
8 Куриц: 11
12 Куриц: 4
Цыплят:
Цыплят:
Цыплят:
Цыплят:
75
78
81
84
Проект Задача Парамадисвары
Исходный код программы находится в файле Задача Парамадисвары.
pas.
От древних китайских задач мы вполне естественным, эволюционным путём приходим к тоже древним, но индийским. Задача 122 из книги Попова
Сборник исторических задач по элементарной математике называется
236
Задача Парамадисвары. Парамадисвара – это старинный индийский математик, о котором нам известно только то, что он написал комментарии к
трактату Ариабхаттиам, откуда мы и получили себе сегодняшнюю закавыку.
Прочитав вскользь условие задачи, мы должны понять, что самое сложное
в ней – суметь выговорить имя автора. Всё остальное – сплошная арифметика. Любители математики и колких шуточек, сразу про себя, но для нас
отметят, что задача легко решается задом наперёд, то есть ретроспективно.
Что касается собственно задачи, то она послужит нам примером и уроком в
программировании. Поэтому мы будем решать её простым перебором:
##
uses MathExtensions;
// условие задачи:
function Uslovie(n: integer) := ((n * 3 / 5 + 6).Sqrt() - 1).Sqr = 4;
function Uslovie2(n: integer) := ((n * 3 / 5 + 6).Sqrt() - 1).Power = 4;
Writeln(' Задача Парамадисвары');
237
Writeln(' Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 122');
Writeln;
// решаем задачу:
var n := 1.Step()
.Where(Uslovie)
.Select(n -> n)
.FirstOrDefault();
// печатаем ответ:
Writeln($' Число = {n}');
Writeln;
Решение задачи заняло несколько наносекунд, а мы получили пятёрочку
по программированию:
Задача Парамадисвары
Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 122
Число = 5
Проект Задача Бхаскара Акариа
Исходный код программы находится в файле Задача Бхаскара Акариа.pas.
Эту индийскую задачу номер 131 из книги Попова Сборник исторических задач по элементарной математике придумал для нас через века индийский
математик Бхаскара по прозвищу Акариа (Ачарьи, 1114-1185), что значит
мудрец или учёный.
Его мудро-учёная задача таилась до поры до времени в трактате Венец астрономического умения. Но если мы внимательно, хотя бы и без телескопа,
взглянем на задачу, то не увидим в ней ничего астрономического или гастрономического – только голая, ничем не прикрытая арифметика.
238
Если мысленно, на ум догадаться, что искомое число - целое, то по дробям
сразу видно, что оно кратно двенадцати. После нескольких более или менее неудачных попыток это число будет безусловно тут же найдено. Сам
Бхаскара решил задачу без перебора, то есть в лоб, но на то он и мудрец!
Мы же не мудрствуя лукаво простым перебором чисел в цикле while отыщем староиндийское число, которое милостиво удовлетворит слегка ни к
чему замысловатое условие задачи:
##
// условие задачи:
function Uslovie(n: integer) := ((n * 5 - n * 5 div 3) div 10 +
n div 3 + n div 2 + n div 4) = 68;
Writeln('Задача Бхаскара Акариа');
Writeln('Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 131');
Writeln;
// решаем задачу с циклом:
var n := 1;
239
while not Uslovie(n) do
Inc(n);
// печатаем ответ:
Writeln('Число = ', n);
Writeln;
// решаем задачу с использованием методов расширения:
n := Range(1, MaxInt)
.First(Uslovie);
// печатаем ответ:
Writeln('Число = ', n);
Writeln;
Число оказалось невелико: 48 – это 12 умножить на 4, то есть любым разумным способом задачу можно решить даже не глядючи, то есть в своём собственном уме:
Задача Бхаскара Акариа
Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 131
Число = 48
Имея склонность к функциональному программированию, вы можете решить задачу иначе:
// решаем задачу функционально:
n := 1.Step
.Where(Uslovie)
.Select(n -> n)
.FirstOrDefault();
// печатаем ответ:
Writeln('Число = ', n);
Writeln;
240
Проект Из жизни Дефурнеля
Исходный код программы находится в файле Из жизни Дефурнеля.pas.
Задача 24 (26) из книги Удивительный мир чисел [КА86], страница 57:
Поскольку XVII век начинается с 1601 года, то мы можем в цикле while
начать перебор с предыдущего года и добавлять по 1 к текущему числу year
– году рождения Дефурнеля:
// РЕШАЕМ ЗАДАЧУ
procedure Solve;
begin
// год рождения Дефурнеля:
var year := 1600;
// годы рождения сыновей:
var son1 := 0;
var son2 := 0;
241
var son3 := 0;
var flg := False;
while (not flg) do begin
year += 1;
И так мы продолжаем итерировать Дефурнеля до тех пор, пока не выполнятся все условия задачи касательно года рождения каждого из трёх его сыновей. Все условия легко формируются при внимательном чтении текста
задачи:
// год рождения первого сына:
son1 := year + 19;
flg := son1 < 1701;
// год рождения второго сына:
son2 := son1 + 38;
flg := flg and (son2 >= 1701) and (son2 < 1801);
// год рождения третьего сына:
son3 := son2 + 63;
flg := flg and (son3 >= 1801) and (son3 < 1901);
end;
begin
Writeln(' Из жизни Дефурнеля');
Writeln(' Увлекательная математика. Задача АЭ15');
Writeln;
Solve;
end.
Когда год рождения Дефурнеля будет установлен, мы напечатаем полезную
и занимательную статистическую информацию на экране:
Из жизни Дефурнеля
Увлекательная математика. Задача АЭ15
Год
Год
Год
Год
рождения
рождения
рождения
рождения
Дефурнеля:
первого сына:
второго сына:
третьего сына:
1681
1700
1738
1801
242
Год рождения третьей жены:
Год первой женитьбы:
Год второй женитьбы:
Год третьей женитьбы:
Год смерти Дефурнеля:
Дефурнель прожил:
1781
1699
1737
1800
1809
128
Проект Чисто американская задача
Исходный код программы находится в файле Чисто американская задача.pas.
Когда имеешь дело с долларами, нужно быть крайне и предельно осторожным, чтобы не попасть на тарелочку с голубой каёмочкой. А посему внимательно и осмотрительно читаем условие задачи.
После внятной читки и основательной прочитки условия задачи, можно
приниматься и браться уж за гуж!
243
Решаем задачу с помощью двух циклов. В первом цикле изменяем число
долларов в заданном диапазоне, а во втором - число центов.
По условию задачи, у американского мужчины должно остаться столько
центов, сколько было долларов, а долларов – половина от прежних центов.
Замечаем, что число центов чётное, поэтому диапазон поисков – от нуля до
девяносто восьми через два. А долларов первоначально было от 0 до 99.
После лёгкой прогулки по магазину у него осталась половина денег, что мы
тщательно проверим. И если книжное условие выполнится, мы напечатаем
ответ на экране.
uses MathExtensions;
// условие задачи:
function Uslovie(c, d: int): bool;
begin
var c1 := d;
var d1 := c div 2;
Result := 100 * d + c = 2 * (100 * d1 + c1);
end;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// решаем задачу -->
// c - центы
// d - доллары
foreach var d in range(0, 99) do
foreach var c in range(0, 98, 2) do
if Uslovie(c, d) then begin
// печатаем ответ:
Writeln($' Долларов было: {d} | Центов было: {c}');
Writeln($' Долларов стало: {c / 2} | Центов стало: {d}');
end;
Writeln;
end;
Довольно забавно, но задача имеет 2 решения.
244
Первый ответ более правдоподобен: у американца не было ничего, и осталась половина от ничего. Но в книге приводится второй ответ.
Чисто американская задача
Математический фольклор. Задача Д45
Долларов
Долларов
Долларов
Долларов
было:
стало:
было:
стало:
0 | Центов было: 0
0 | Центов стало: 0
99 | Центов было: 98
49 | Центов стало: 99
Чисто американскую задачу следует решать чисто функционально:
procedure Solve2();
begin
var res :=
Range(0, 99)
// Доллары d
.SelectMany(d ->
Range(0, 98, 2)
// Центы c (чётные)
.Where(c -> Uslovie(c, d))
// Фильтруем по условию
.Select(c -> (c, d))
// Создаём пару (центы, доллары)
);
// печатаем ответ:
foreach var (c, d) in res do begin
Writeln($' Долларов было: {d} | Центов было: {c}');
Writeln($' Долларов стало: {c / 2} | Центов стало: {d}');
Writeln;
end;
end;
Решение уже хорошее, но излишне путанное. Чтобы его распутать, используем функцию Cartesian:
procedure Solve3();
begin
var dollars := Range(0, 99);
// Доллары: 0..99
var cents := Range(0, 98, 2);
// Центы: 0,2,4,...,98 (чётные)
var solutions := Cartesian(dollars, cents)
// Все пары (d, c)
.Where(dc -> Uslovie(dc.Item2, dc.Item1)) // pair = (dollar, cent)
.Select(dc -> (dc.Item1, dc.Item2));
245
// печатаем ответ:
if solutions.Any() then begin
Writeln(' Найдено решений: {solutions.Count()}');
foreach var (c, d) in solutions do begin
Writeln($' Долларов было: {d} | Центов было: {c}');
Writeln($' Долларов стало: {c div 2} | Центов стало: {d}');
Writeln;
end;
end;
end;
А уж с кортежами решение просто великолепно:
procedure Solve4();
begin
var dollars := Range(0, 99);
var cents := Range(0, 98, 2);
var solutions := Cartesian(dollars, cents)
.Where(\(d, c) -> Uslovie(c, d)) // Распаковываем кортеж
.Select(\(d, c) -> (d, c));
// Возвращаем кортеж
// печатаем ответ:
foreach var (d, c) in solutions do begin
Writeln($' Долларов было: {d} | Центов было: {c}');
Writeln($' Долларов стало: {c div 2} | Центов стало: {d}');
Writeln;
end;
end;
Решаем задачу чисто классически:
procedure Solve5();
begin
for var d := 0 to 99 do
for var c := 0 to 99 do
begin
var c1 := d;
var d1 := c div 2;
if (100 * d + c = 2 * (100 * d1 + c1)) then begin
Writeln($' Долларов было: {d} | Центов было: {c}');
Writeln($' Долларов стало: {d1} | Центов стало: {c1}');
Writeln;
end
246
end;
Writeln;
end;
begin
Writeln(' Чисто американская задача');
Writeln(' Математический фольклор. Задача Д45');
Writeln;
Solve();
Solve2();
Solve3();
Solve4();
Solve5();
end.
247
Проект Чисто французская задача
Исходный код программы находится в файле Чисто французская задача.pas.
В те далёкие времена это были франки и сантимы. Об этом нам говорит и
это подтверждает задача Д44 из книги Математический фольклор.
Как учит нас французская задача, присутствие жены пагубно сказывается
на содержимом кошелька: у французского мсье осталась только пятая
часть денег. Сравните с одиноким американцем, который сохранил для
себя половину денег, и сделайте надлежащие выводы…
Что касается арифметической подоплёки предложенной нам задачи, то достаточно выгодно обменять доллары и центы на франки и сантимы – и задача решена!
uses MathExtensions;
248
// условие задачи:
function Uslovie(c, f:
begin
// c - сантимы, f var c1 := f;
var f1 := c div 5;
Result := 100 * f +
end;
int): bool;
франки
// после обмена: сантимов стало f
// после обмена: франков стало c/5 (должно быть целым)
c = 5 * (100 * f1 + c1)
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// f - франки, c - сантимы (кратно 5)
foreach var f in range(0, 100) do
foreach var c in range(0, 100, 5) do
if Uslovie(c, f) then begin
// печатаем ответ:
Writeln($' Франков было: {f}
// После обмена
var f1 := c div 5;
var c1 := f;
Writeln($' Франков стало: {f1}
end;
Writeln;
end;
| Франков было:
{c}');
| Франков стало: {c1}');
begin
Writeln(' Чисто французская задача');
Writeln(' Задача Д44 из книги Математический фольклор');
Writeln;
Solve();
end.
Решение задачи слегка осложняется бухгалтерскими выкладками, но мы
тут же получаем чёткий расчёт счёта:
Чисто французская задача
Задача Д44 из книги Математический фольклор
Франков
Франков
Франков
Франков
было:
стало:
было:
стало:
0 | Франков было: 0
0 | Франков стало: 0
99 | Франков было: 95
19 | Франков стало: 99
249
Поскольку мсье Метивье вряд ли осмелился бы пойти с женой в ресторан
без франков и сантимов, то следует признать верным только второй ответ.
Функционально французская задача решается так же, как американская:
procedure Solve2();
begin
var francs := Range(0, 100);
var centims := Range(0, 100, 5);
var solutions := Cartesian(centims, francs)
.Where(\(c, f) -> Uslovie(c, f))
.Select(\(c, f) -> (c, f));
// печатаем ответ:
foreach var (c, f) in solutions do begin
Writeln($' Франков было: {f} | Франков было: {c}');
var f1 := c div 5;
var c1 := f;
Writeln($' Франков стало: {f1} | Франков стало: {c1}');
end;
Writeln;
end;
250
Считаем франки чисто классически:
procedure Solve3();
begin
for var f := 0 to 99 do
for var c := 0 to 99 do begin
var c1 := f;
var f1 := c div 5;
if (100 * f + c = 5 * (100 * f1 + c1)) then begin
Writeln($' Франков было: {f} | Франков было: {c}');
Writeln($' Франков стало: {f1} | Франков стало: {c1}');
var zaplatil := 4 * (100 * f1 + c1);
Writeln($' Мсье Метивье заплатил: {zaplatil div 100} фр. и
{(zaplatil - zaplatil div 100 * 100)} c.');
Writeln;
end
end;
Writeln;
end;
begin
Writeln(' Чисто французская задача');
251
Writeln(' Задача Д44 из книги Математический фольклор');
Writeln;
Solve();
Solve2();
Solve3();
end.
252
Проект Американские цыпочки
Исходный код программы находится в файле Американские цыпочки.pas.
В очередной американской задаче - Д41 из книги Математический фольклор - мы снова встречаемся с долларами. На этот раз с одинокими – без центов. Несколько скрашивают излишне меркантильную базарную картину
цыпочки, уточки и гусачки.
Соблюдая свойственную нам аккуратную осторожность при американских
делах и сделках, ещё раз многократно читаем условие задачи, чтобы не попасть под кабальное соглашение.
Замечаем: из последнего равенства в условии задачи следует, что цыплёнок
стоит не дороже 25 долларов, утка – не дороже 12 долларов и гусь – не дороже 8 долларов. Три недалёких цикла в разумных диапазонах– и мы
узнаем цену всем американским цыпочкам.
253
Условие задачи быстро переносим в программу:
uses MathExtensions;
// условие задачи:
function Uslovie(цыплёнок, гусь, утка: int) := (3 * цыплёнок + утка = 2 *
гусь) and (цыплёнок + 2 * утка + 3 * гусь = 25);
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var цыплёнок in range(0, 25) do
foreach var гусь in range(0, 8) do
foreach var утка in range(0, 12) do
if Uslovie(цыплёнок, гусь, утка) then begin
// печатаем ответ:
Writeln($' Цыплёнок стоит: {цыплёнок}');
Writeln($' Гусь стоит:
{гусь}');
Writeln($' Утка стоит:
{утка}');
end;
Writeln;
end;
begin
Writeln(' Американские цыпочки);
Writeln(' Задача Д41 из книги Математический фольклор');
Writeln;
Solve();
end.
А вот и цены на цыпочек, которые Киса Воробьянинов прокомментировал
бы кратно, но веско: Однако!
Американские цыпочки
Задача Д41 из книги Математический фольклор
Цыплёнок стоит: 2
Гусь стоит:
5
Утка стоит:
4
254
Поднаторев в функциональном программировании, вы оцените американских цыпочек и так и сяк:
procedure Solve2();
begin
var цыплёнок := Range(0, 25);
var гусь := Range(0, 8);
var утка := Range(0, 12);
var solutions := Cartesian(цыплёнок, гусь, утка)
.Where(\(цыплёнок, гусь, утка) -> Uslovie(цыплёнок,
гусь, утка))
.Select(\(цыплёнок, гусь, утка) -> (цыплёнок, гусь,
утка));
// печатаем ответ:
foreach var (ц, г, у) in solutions do begin
Writeln($' Цыплёнок стоит: {ц}');
Writeln($' Гусь стоит:
{г}');
Writeln($' Утка стоит:
{у}');
end;
Writeln;
end;
Решаем задачу классически:
procedure Solve3();
begin
for var cyplenok := 0 to 25 do
for var gus := 0 to 8 do
for var utka := 0 to 12 do begin
if ((3 * cyplenok + utka = 2 * gus) and
(cyplenok + 2 * utka + 3 * gus = 25)) then
begin
Writeln($' Цыплёнок стоит: {cyplenok}');
Writeln($' Гусь стоит:
{gus}');
Writeln($' Утка стоит:
{utka}');
Writeln;
end
end;
Writeln;
end;
255
begin
Writeln(' Американские цыпочки');
Writeln(' Задача Д41 из книги Математический фольклор');
Writeln;
Solve();
Solve2();
Solve3();
end.
Проект Американское наследство
Исходный код программы находится в файле Американское наследство.pas.
Время неумолимо отсчитывает за веком век, занимательнее задачи становятся всё интереснее, изощрённее и многообразнее. Некоторые из них благополучно пережили века и до сих пор будоражат воображение и привлекают внимание любителей поломать голову.
256
Мы уже решили пару старых задач, которые новее старинных, но старее современных. В этом проекте мы добавим к ним ещё одну, чтобы устроить им
тройню.
Эта задача продолжает крючкотворную задачу про римских адвокатов, но
несколько иначе и в другой окружающей среде. Это задача Д45 из книги
Математический фольклор и называется она Американское наследство.
Обычно делёж наследства – процесс сложный и неприятный, но сотню долларов мы легко поделим по-американски – не поровну, но по-честному.
В цикле for перебираем долю Чарлза. Роберт получит остатки от сотни долларов.
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// делим деньги в цикле:
for var Чарлз := 0 to 100 do begin
// доля Роберта:
var Роберт := 100 - Чарлз;
257
// условие задачи:
if (Роберт / 4 - Чарлз / 3 = 11) then begin
Когда условие задачи выполнится, мы напечатаем ответ на экране:
Writeln($' Чарлз получил: {Чарлз}');
Writeln($' Роберт получил: {Роберт}');
Writeln;
end;
end;
end;
begin
Writeln(' Американское наследство');
Writeln(' Математический фольклор. Задача Д39');
Writeln;
Solve();
end.
Запускаем программу, и она выдаёт нам юридический отчёт о проделанной
работе. Чарлз получил от папаши 24 доллара, а Роберт – 76:
Американское наследство
Математический фольклор. Задача Д39
Чарлз получил: 24
Роберт получил: 76
Легко заметить и отметить, что наш успешный цикл for легко заменить методом расширения To, с которым мы познакомились в предыдущих проектах.
Условие задачи слегка, но не чрезмерно удлинилось, но всё остальное в полном порядке:
procedure Solve2();
258
begin
// решаем задачу в функциональном стиле:
var a := 0.To(100)
.Where(Чарлз -> (100 - Чарлз) / 4 - Чарлз / 3 = 11)
.First;
// доля Чарлза:
var Чарлз := a;
// доля Роберта:
var Роберт := 100 - Чарлз;
Writeln($' Чарлз получил: {Чарлз}');
Writeln($' Роберт получил: {Роберт}');
Writeln;
end;
Запускаем программу – полный функционал налицо и на экране!
Американское наследство
Математический фольклор. Задача Д39
Чарлз получил: 24
Роберт получил: 76
Чарлз получил: 24
Роберт получил: 76
В некоторых проектах мы использовали бесконечные циклы while. Метод
расширения Step выдаёт бесконечную последовательность целых чисел,
начиная с текущего.
Метод Step без параметров выдаёт последовательность с шагом 1, а метод
Step с параметром – с заданным шагом.
procedure Solve3();
begin
// решаем задачу в функциональном стиле:
var a := 0.Step
.Where(Чарлз -> (100 - Чарлз) / 4 - Чарлз / 3 = 11)
.First;
// доля Чарлза:
var Чарлз := a;
259
// доля Роберта:
var Роберт := 100 - Чарлз;
Writeln($' Чарлз получил: {Чарлз}');
Writeln($' Роберт получил: {Роберт}');
Writeln;
end;
Методы расширения Step выдают бесконечную последовательность целых чисел, что может подвесить программу. Поэтому нужно предусмотреть
завершение этих методов, когда решение будет найдено.
В нашем случае мы знаем, что задача имеет единственное решение, поэтому используем метод расширение Take, чтобы вовремя и восвояси закончить генерацию чисел. Передаём ему единицу, то есть число решений.
procedure Solve4();
begin
// решаем задачу в функциональном стиле:
var a := 0.Step
.Where(Чарлз -> (100 - Чарлз) / 4 - Чарлз / 3 = 11)
.Take(1)
.ToArray;
// доля Чарлза:
var Чарлз := a.First;
// доля Роберта:
var Роберт := 100 - Чарлз;
Writeln($' Чарлз получил: {Чарлз}');
Writeln($' Роберт получил: {Роберт}');
Writeln;
end;
Запустите программу и убедитесь, что она работает исправно.
Мы можем использовать метод Take иначе. Если мы поставим его сразу после метода Step, то он пропустит дальше только заданное количество чисел.
Нас интересуют числа от нуля до ста, поэтому передаём методу Take сотню.
Теперь метод Where получит последовательность чисел от нуля до сотни,
что нам и нужно.
260
procedure Solve5();
begin
// решаем задачу в функциональном стиле:
var a := 0.Step()
.Take(100)
.Where(Чарлз -> (100 - Чарлз) / 4 - Чарлз / 3 = 11)
.ToArray();
// доля Чарлза:
var Чарлз := a.First;
// доля Роберта:
var Роберт := 100 - Чарлз;
Writeln($' Чарлз получил: {Чарлз}');
Writeln($' Роберт получил: {Роберт}');
Writeln;
end;
Запустите программу и убедитесь, что такая программа тоже годится.
Для прямой замены цикла for от нуля до заданного числа минус единица
используйте метод расширения Times. Перед методом напишите верхнюю
границу цикла.
procedure Solve6();
begin
// решаем задачу в функциональном стиле:
var a := 101.Times()
.Where(Чарлз -> (100 - Чарлз) / 4 - Чарлз / 3 = 11)
.ToArray();
// доля Чарлза:
var Чарлз := a.First;
// доля Роберта:
var Роберт := 100 - Чарлз;
Writeln($' Чарлз получил: {Чарлз}');
Writeln($' Роберт получил: {Роберт}');
Writeln;
end;
Запустите программу для проверки. Метод Times работает исправно!
261
И наконец, решаем задачу с функцией Range:
procedure Solve7();
begin
// доля Чарлза:
var Чарлз := Range(0, 100)
.Where(n -> (100 - n) / 4 - n / 3 = 11)
.ElementAt(0);
// доля Роберта:
var Роберт := 100 - Чарлз;
Writeln($' Чарлз получил: {Чарлз}');
Writeln($' Роберт получил: {Роберт}');
Writeln;
end;
Американское наследство
Математический фольклор. Задача Д39
Чарлз получил: 24
Роберт получил: 76
Чарлз получил: 24
Роберт получил: 76
Чарлз получил: 24
Роберт получил: 76
Чарлз получил: 24
Роберт получил: 76
Чарлз получил:
Роберт получил:
Чарлз получил:
Роберт получил:
Чарлз получил:
Роберт получил:
24
76
24
76
24
76
Теперь вы можете решать простые переборные задачи с циклами for, как
это делается обычно, или в функциональном стиле – для усиления прогресса в программировании.
262
Проект Английский юмор
Исходный код программы находится в файле Английский юмор.pas.
Известно, что задачи у англичан с хитринками и подвохами. Чтобы оценить
и распознать козни, внимательнейшим образом читаем условие задачи
назубок!
Тут следует заметить, что эта задача возникла неспроста. Подобные задачи
были известны в Англии ещё в XVII веке. Встречаются варианты со шляпами и корзинами.
Так как чашек было 3 – нечётное число, то чётное число кусочков сахара –
10 – нельзя между ними «распределить» без русской смекалки.
То есть после осахаривания чашек одну из них – с нечётным числом кусочков сахара – следует поставить в чашку с чётным числом кусочков сахара.
Тогда в ней также будет нечётное число кусочков рафинада.
263
Обозначим чашку с чётным числом кусочков сахара буквой а, тогда остальные две чашки – с нечётным числом кусочков сахара – логично обозначить
буквами b и c.
Из условия задачи следует, что число кусочков сахара в чашке а изменяется
от 0 до 10 через два, а в чашке b - от 1 до 10 минус a и тоже через два. На
чашку с приходятся оставшиеся кусочки сахара.
uses MathExtensions;
function Uslovie(a, b: int) := 10 - a - b >= 0;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var vars := 0;
foreach var a in range(0, 10, 2) do begin
foreach var b in range(1, 10 - a + 1, 2) do begin
// печатаем ответ:
if Uslovie(a, b) then begin
vars += 1;
var c := 10 - a - b;
Writeln($' Вариант #{vars}: a = {a} | b = {b} | c = {c}');
end;
end;
end;
Writeln;
end;
begin
Writeln(' Английский юмор');
Writeln(' Математический фольклор. Задача Д37');
Writeln;
Solve();
end.
Существует 15 способов решения сахарной задачи коварной, но очаровательной мисс Локайер:
Английский юмор
264
Математический фольклор. Задача Д37
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
Вариант
#1: a = 0 | b = 1 | c = 9
#2: a = 0 | b = 3 | c = 7
#3: a = 0 | b = 5 | c = 5
#4: a = 0 | b = 7 | c = 3
#5: a = 0 | b = 9 | c = 1
#6: a = 2 | b = 1 | c = 7
#7: a = 2 | b = 3 | c = 5
#8: a = 2 | b = 5 | c = 3
#9: a = 2 | b = 7 | c = 1
#10: a = 4 | b = 1 | c = 5
#11: a = 4 | b = 3 | c = 3
#12: a = 4 | b = 5 | c = 1
#13: a = 6 | b = 1 | c = 3
#14: a = 6 | b = 3 | c = 1
#15: a = 8 | b = 1 | c = 1
Решение задачи с циклами легко переделать в функциональное:
procedure Solve2();
begin
var solutions := Range(0, 10, 2)
.SelectMany(a -> Range(1, 10 - a + 1, 2)
.Where(b -> Uslovie(a, b))
.Select(b -> (a, b, 10 - a - b)));
// печатаем ответ:
var vars := 0;
foreach var (a, b, c) in solutions do begin
vars += 1;
Writeln($' Вариант #{vars}: a = {a} | b = {b} | c = {c}');
end;
Writeln;
end;
Решаем задачу классически:
procedure Solve3();
begin
var variant := 0;
265
var a := 0;
while(a <= 10) do begin
var b := 1;
while(b <= 10 - a) do begin
var c := 10 - a - b;
variant += 1;
Writeln($' Вариант #{variant}: a = {a} | b = {b} | c = {c}');
b += 2;
end;
a += 2;
end;
Writeln;
end;
266
Проект Русские яблоки
Исходный код программы находится в файле Русские яблоки.pas.
А вот и задачка про яблочки наши наливные! Эти яблочки прикатились к
нам родом из книги Математический фольклор, не петляя и не вихляя черешком прямо из задачи Д35.
Тут следует отметить, что у болгар есть аналогичная
задача, но про груши.
Не к месту будь сказано, но команда КВН под названием БАК -Соучастники
лихо и зверски здорово исполнила песню На яблоки на снегу, которая не
имеет никакого отношения к нашей задаче и не укладывается в рамки
нашего математического формата.
267
А также не грех здесь вспомнить детский анекдот про яблоки и груши:
Папа спрашивает у сына:
- Скажи, сколько будет, если к трём грушам прибавить ещё две груши?
Сын отвечает:
- Не знаю, папа, мы в школе решаем задачи только про яблоки!
Что же касается собственно задачи, то начните приступ к её решению с тщательной проработки условия, чтобы правильно выстроить стратегию и тактику поражения этого круглого фрукта в самое яблочко.
И тут мы должны заметить, что условие задачи настолько циклично, что
мы просто обязаны применить цикл for (или loop), в котором 6 покупателей поочерёдно приобретают яблоки.
Число яблок нам неизвестно, поэтому мы перебираем их во внешнем бесконечном цикле while, начиная с единицы. Как только крестьянка продаст
всю корзину яблок, мы напечатаем ответ и решительно закончим решение
задачи.
uses MathExtensions;
268
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var apple := 1;
while True do begin
var ost := apple + 0.0;
loop 6 do begin
ost := (ost - 1) / 2;
end;
// печатаем ответ:
if (ost = 0) then begin
Writeln($' Яблок было: {apple}');
Writeln;
break;
end;
apple += 1;
end;
end;
begin
Writeln(' Русские яблоки');
Writeln(' Математический фольклор. Задача Д35');
Writeln;
Solve();
end.
С задачей легко справиться даже вручную, если решать её ретроспективно,
то есть задом наперёд, начиная с нулевого конца.
С задачей легко справиться вручную, если решать её ретроспективно, то
есть начиная с конца.
Действительно, шестому покупателю досталась половина яблока и ещё
столько же, то есть ровно 1 яблоко. После этого корзина опустела. Пятый
купил в 2 раза больше + 1 яблоко, то есть 3. Четвёртый – 3*2 + 1 = 7. И так
далее – до победного начала.
Для тренировки ретроспективного мышления вспять давайте изложим
этот безвозвратно-поступательный процесс на языке паскаля!
269
procedure Solve2();
begin
// число покупателей:
var buyers := 6;
// остаток после последнего покупателя:
var apple := 0;
// двигаемся от конца к началу:
loop buyers do
apple := apple * 2 + 1;
// печатаем ответ:
Writeln($' Яблок было: {apple}');
// проверка:
var current := apple;
for var i := 1 to buyers do begin
// половина + половинка яблока:
var sold := (current + 1) div 2;
var after := current - sold;
Writeln($' Покупатель {i} --> было {current}, продано {sold},
осталось {after}');
current := after;
end;
end;
И если у вас найдётся небольшой временной досуг, то подумайте, сколько было бы яблок у крестьянки при шестидесяти четырёх покупателях.
Но, как ни решай, как ни крути фруктовые и глазные яблоки в орбитах, а
ответ получается один и тот же: в изрядной корзине крестьянки сначала
было 63 яблока, от которых она удачно, то есть за деньги избавилась вчистую:
Русские яблоки
Математический фольклор. Задача Д35
Яблок было: 63
270
Яблок было: 63
Покупатель 1 -->
Покупатель 2 -->
Покупатель 3 -->
Покупатель 4 -->
Покупатель 5 -->
Покупатель 6 -->
было
было
было
было
было
было
63, продано 32, осталось 31
31, продано 16, осталось 15
15, продано 8, осталось 7
7, продано 4, осталось 3
3, продано 2, осталось 1
1, продано 1, осталось 0
Считаем яблоки функционально:
// ПРОВЕРЯЕМ УСЛОВИЕ ЗАДАЧИ
function Uslovie(apple : integer) : boolean;
begin
var ost := double(apple);
for var i := 1 to 6 do
ost := (ost - 1)/2;
Result := ost = 0;
end;
procedure Solve3();
begin
Write(' Яблок было: ');
1.Step
.Where(apple -> Uslovie(apple))
.ElementAt(0)
.Println;
Writeln;
end;
begin
Writeln(' Русские яблоки');
Writeln(' Математический фольклор. Задача Д35');
Writeln;
Solve();
Solve2();
Solve3();
end.
271
Проект Американские яблоки
Исходный код программы находится в файлах Американские яблоки.pas и Американские яблоки 2.pas.
На этот раз американцы делят не наследство в долларах, а фрукты в яблоках:
Так как мы пока не знаем фамилии девочек, то диапазон числа яблок сестры
каждого брата – 1..4.
Тогда Гарри получил 1 * (1..4) яблока. А вместе с сестрой – 1 *(1..4 + 1).
Аналогично для остальных братьев и сестёр:
Том получил 2 * (1..4) яблока. А вместе с сестрой – 2 *(1..4 + 1).
Билл получил 3 * (1..4) яблока. А вместе с сестрой – 3 *(1..4 + 1).
Джек получил 4 * (1..4) яблока. А вместе с сестрой – 4 *(1..4 + 1).
272
Все вместе они получили:
1 *(1..4 + 1) + 2 *(1..4 + 1) + 3 *(1..4 + 1) + 4 *(1..4 + 1) = 32 яблока
(1)
Таким образом, нам нужно выписать 4 вложенных цикла for и вычислять
сумму полученных яблок до тех пор, пока она не станет равна 32.
Мы также должны учесть, что все девочки получили разное число яблок.
Записываем имена девочек и их фамилии в строковые массивы:
//РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// имена девочек:
var SisterName := ['', 'Ani', 'Betti', 'Cat', 'Mari'];
//фамилии:
Гарри
Том
Билл
Джек
var familyName := ['', 'Smit', 'Brown', 'Johnson', 'Robinson'];
Фамилии в массиве familyName должны соответствовать кратности полученных братьями яблок. Именно так мы использовали имена братьев при
выводе формулы (1).
Теперь мы легко решим задачу и напечатаем ответ в понятном виде:
for var sisterApple1 := 1 to 4 do
for var sisterApple2 := 1 to 4 do
begin
if (sisterApple2 = sisterApple1) then
continue;
for var sisterApple3 := 1 to 4 do begin
if ((sisterApple3 = sisterApple2) or
(sisterApple3 = sisterApple1)) then
continue;
for var sisterApple4 := 1 to 4 do begin
if ((sisterApple4 = sisterApple3) or
(sisterApple4 = sisterApple2) or
(sisterApple4 = sisterApple1)) then continue;
var apples := 1 * (sisterApple1 + 1) +
2 * (sisterApple2 + 1) +
273
3 * (sisterApple3 + 1) +
4 * (sisterApple4 + 1);
if (apples = 32) then begin
println($' {sisterName[sisterApple1]}
{fami-
println($' {sisterName[sisterApple2]}
{fami-
println($' {sisterName[sisterApple3]}
{fami-
println($' {sisterName[sisterApple4]}
{fami-
lyName[1]}');
lyName[2]}');
lyName[3]}');
lyName[4]}');
end
end
end
end;
println;
end;
begin
Writeln(' Американские яблоки');
Writeln(' Задача Д34 из книги Математический фольклор');
Writeln;
Solve();
//Solve2();
end.
Действительно, сестра Гарри получила sisterApple1 яблок. Значит, её имя sSisterName[sisterApple1]. У неё такая же фамилия, как и у брата, то есть
familyName[1]. Точно так же мы находим фамилии остальных девочек:
Американские яблоки
Задача Д34 из книги Математический фольклор
Cat Smit
Mari Brown
Ani Johnson
Betti Robinson
Задачу можно решить иначе:
274
// имена девочек:
type
Names = (Ani, Betti, Cat, Mari );
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// имена девочек:
var sisterName := new Dictionary<Names, string>();
sisterName.Add(Names.Ani, 'Ani');
sisterName.Add(Names.Betti, 'Betti');
sisterName.Add(Names.Cat, 'Cat');
sisterName.Add(Names.Mari, 'Mari');
// число яблок у девочек:
var apple := new Dictionary<string, integer>();
apple.Add('Ani', 1);
apple.Add('Betti', 2);
apple.Add('Cat', 3);
apple.Add('Mari', 4);
// кратность яблок у мальчиков:
var krat := new Dictionary<string, integer>();
krat.Add('Гарри', 1);
krat.Add('Том', 2);
krat.Add('Билл', 3);
krat.Add('Джек', 4);
for var Brown := names.Ani to names.Mari do
for var Johnson := names.Ani to names.Mari do begin
if (Johnson = Brown) then continue;
for var Robinson := names.Ani to names.Mari do begin
if ((Robinson = Johnson) or (Robinson = Brown)) then
continue;
for var Smit := names.Ani to names.Mari do begin
if ((Smit = Robinson) or (Smit = Johnson) or
(Smit = Brown)) then continue;
// Brown -->
// сестру звали:
var name := sisterName[Brown];
// у неё было яблок:
var appleS := apple[name];
// брата звали Том
275
// он получил яблок:
var appleB := appleS * krat['Том'];
// вместе они получили:
var appleSB1 := appleS + appleB;
// Johnson -->
// сестру звали:
name := sisterName[Johnson];
// у неё было яблок:
appleS := apple[name];
// брата звали Билл
// он получил яблок:
appleB := appleS * krat['Билл'];
// вместе они получили:
var appleSB2 := appleS + appleB;
// Robinson -->
// сестру звали:
name := sisterName[Robinson];
// у неё было яблок:
appleS := apple[name];
// брата звали Джек
// он получил яблок:
appleB := appleS * krat['Джек'];
// вместе они получили:
var appleSB3 := appleS + appleB;
// Smit -->
// сестру звали:
name := sisterName[Smit];
// у неё было яблок:
appleS := apple[name];
// брата звали Джек
// он получил яблок:
appleB := appleS * krat['Гарри'];
// вместе они получили:
var appleSB4 := appleS + appleB;
if (appleSB1 +
appleSB4 =
println($'
println($'
println($'
println($'
println;
appleSB2 + appleSB3 +
32) then begin
{sisterName[Brown]} Brown');
{sisterName[Johnson]} Johnson');
{sisterName[Robinson]} Robinson');
{sisterName[Smit]} Smit');
276
end
end
end
end
end;
begin
Writeln(' Американские яблоки 2');
Writeln(' Задача Д34 из книги Математический фольклор');
Writeln;
Solve();
end.
Результат, естественно, вы получите тот же самый, что и раньше, но на «латинском» языке:
Американские яблоки 2
Задача Д34 из книги Математический фольклор
Mari Brown
Ani Johnson
Betti Robinson
Cat Smit
Американцы известны своим пристрастием к пересъёмкам французских
фильмов на свой лад и со своими актёрами. Не избежала этой печальной
участи и французская задача (Увлекательная математика, 1):
Во время летнего пикника четыре супружеские пары выпили 32 бутылки
лимонада. Жёны выпили: Жанна – 1 бутылку, Жаклин – 2 бутылки, Колетта – 3 бутылки и Анетта – 4 бутылки. Мужья не уступили жёнам:
месье Пон выпил столько же, сколько его жена, Месье Дюбуа – вдвое
больше своей жены, месье Пейзан – втрое и месье Фонтен – вчетверо
больше своих жён.
Как зовут мадам Пон, Дюбуа, Пейзан и Фонтен.
277
Проект Свинская задача
Исходный код программы находится в файле Свинская задача.pas.
Вас не должны смущать древние
имена некоторых персонажей
этой задачи. Она появилась в Дании ещё в XVIII веке и остаётся популярной на протяжении нескольких веков. Например, эта задача напечатана в журнале
Popular Science.
Её можно найти и в сборнике задач Cyclopedia of Puzzles Сэма
Лойда →
278
Мы можем предположить, что ни одна жена не купила больше 100 свиней.
Если эта гипотеза не приведёт нас к решению задачи, то мы расширим диапазон поиска.
Итак, согласно нашему предположению, Катрин купила от 1 до 100 свиней
и заплатила за них costKatrin крон:
// РЕШАЕМ ЗАДАЧУ
279
procedure Solve();
begin
var name := new List<string>();
var names := new List<string>();
var Katrin := 0;
for Katrin := 1 to 100 do begin
// Katrin заплатила:
var costKatrin := Katrin * Katrin;
Нам известно, что Нильс купил на 23 свиньи больше, чем Катрин, и заплатил за них costNils крон:
// Нильс купил:
var Nils := Katrin + 23;
// Нильс заплатил:
var costNils := Nils * Nils;
Геертринг также купила от одной до ста свиней, заплатив за них
costGeertring крон:
var Geertring := 0;
for Geertring := 1 to 100 do begin
// Geertring заплатила:
var costGeertring := Geertring * Geertring;
Клаас купил на 11 свиней больше, чем Геертринг, и заплатил costClaas
крон:
// Клаас купил:
var Claas := Geertring + 11;
// Клаас заплатил:
var costClaas := Claas * Claas;
И наконец, Анна купила от 1 до 100 свиней и заплатила costAnna крон:
280
var Anna := 0;
for Anna := 1 to 100 do begin
// Anna заплатила:
var costAnna := Anna * Anna;
Нильс заплатил на 63 кроны больше своей жены, и это должна быть одна
из трёх женщин. Если при текущих значениях переменных costNils,
costKatrin, costGeertring и costAnna это условие не выполняется, значит,
нужно продолжать перебор:
if ((costNils <> costKatrin + 63) and
(costNils <> costGeertring + 63) and
(costNils <> costAnna + 63)) then continue;
Аналогичное условие должно выполняться и для Клааса:
if ((costClaas <> costKatrin + 63) and
(costClaas <> costGeertring + 63) and
(costClaas <> costAnna + 63)) then continue;
Если оба эти условия выполняются, то мы нашли для Нильса и Клааса их
жён. Для дальнейших проверок и печати ответа нам понадобятся 2 списка:
var name := new List<string>();
var names := new List<string>();
В первый мы поместим имена уже найденных жён, во второй – информацию для ответа.
Перед каждой проверкой мы очищаем оба списка:
name.Clear();
names.Clear();
281
Мы точно знаем, что при текущих значениях затрат на приобретение свиней Нильс заплатил на 63 кроны больше одной из женщин. Давайте установим её имя:
if (costNils = costKatrin + 63) then begin
name.Add('Катрин');
names.Add(' Катрин - жена Нильса');
end
else if (costNils = costGeertring + 63) then begin
Name.Add('Геертринг');
Names.Add(' Геертринг - жена Нильса');
end
else begin
name.Add('Анна');
names.Add(' Анна - жена Нильса');
end;
Теперь в список name мы занесли имя предполагаемой жены Нильса.
Аналогичные розыскные мероприятия мы проводим и в отношении Клааса:
if ((costClaas = costKatrin + 63) and
(not Name.Contains('Катрин'))) then begin
name.Add('Катрин');
names.Add(' Катрин - жена Клааса');
end
else if ((costClaas = costGeertring + 63) and
(not Name.Contains('Геертринг'))) then begin
name.Add('Геертринг');
names.Add(' Геертринг - жена Клааса');
end
else if (not Name.Contains('Анна')) then begin
name.Add('Анна');
names.Add(' Анна - жена Клааса');
end;
После чего Нильс и Клаас должны получить имена своих жён. Поскольку
жёны у них разные, то в списке имён name должно оказаться ровно 2 записи:
282
if (Name.Count <> 2) then continue;
Печатаем на экране уже достоверно известную нам информацию:
println($'
println($'
println($'
println($'
Катрин = {Katrin}');
Катрин заплатила: {costKatrin}');
Нильс = {Nils}');
Нильс заплатил: {costNils}');
println($'
println($'
println($'
println($'
Геертринг = {Geertring}');
Геертринг заплатила: {costGeertring}');
Клаас = {Claas}');
Клаас заплатил: {costClaas}');
println($' Анна = {Anna}');
println($' Анна заплатила: {costAnna}');
println;
И последнее, что мы должны сделать, - найти жену для Корнелиуса. Это
легко сделать, ведь её имя отсутствует в списке name:
if (not name.Contains('Анна')) then
names.Add(' Анна - жена Корнелиуса')
else if (not name.Contains('Геертринг')) then
names.Add(' Геертринг - жена Корнелиуса')
else
names.Add(' Катрин - жена Корнелиуса');
Печатаем ответ на задачу:
foreach var s in names do
println(s);
println;
end
end
end;
println;
283
end;
begin
Writeln(' Свинская задача');
Writeln(' Задача Д33 из книги Математический фольклор');
Writeln;
Solve();
end.
Свинская задача
Задача Д33 из книги Математический фольклор
Катрин = 9
Катрин заплатила: 81
Нильс = 32
Нильс заплатил: 1024
Геертринг = 1
Геертринг заплатила: 1
Клаас = 12
Клаас заплатил: 144
Анна = 31
Анна заплатила: 961
Анна - жена Нильса
Катрин - жена Клааса
Геертринг - жена Корнелиуса
Вот такие задачи решали в XVIII веке!
284
Проект Турецкие долгожители
Исходный код программы находится в файле Турецкие долгожители.pas.
Прилежно путешествуем по математическому миру приключений вместе с
книгой Математический фольклор. Сегодня мы найдём себе приют в задаче
Д32 про турецких долгожителей.
Читая условие задачи, мы должны смекнуть, что число детей из поколения
в поколение возрастало в геометрической прогрессии.
Обозначим буквой n число детей у Исхана.
• Тогда число внуков у Исхана равно n в квадрате
• Число правнуков у Исхана равно n в кубе
• А число праправнуков у Исхана равно n в четвёртой степени
• Итого получаем 2800 человек, к которым нужно добавить и самого
Исхана.
285
Совершенно очевидно, что нам нужно найти n, которое весьма невелико, но
нам заранее неизвестно.
Значение переменной n можно искать в любом цикле, ограничив диапазон
сверху разумным значением. Но мы поступим иначе. Метод расширения
Step сгенерирует бесконечную последовательность целых чисел, начиная с
единицы:
uses MathExtensions;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
// n - число детей у Исхана
// n * n - число внуков у Исхана
// n * n * n - число правнуков у Исхана
// n * n * n * n - число праправнуков у Исхана
// всего - 2801 человек вместе с Исханом
begin
var n := 1.Step()
Метод расширения Where подсчитает народонаселение Исхана и сравнит
текущее значение с заданным числом 2801:
.Where(n -> 1 + n + n.IPower(2) + n.IPower(3) + n.IPower(4) = 2801)
Если такое статистическое чудо произойдёт, случится или свершится, метод расширения Select запомнит искомое значение переменной n:
.Select(n -> n)
А метод расширения FirstOrDefault прекратит дальнейшую бессмысленную перепись деревенского населения:
.FirstOrDefault();
286
Изящно, но культурно публикуем статистический отчёт решения задачи
всем кому ни попадя для куража или интереса:
// печатаем ответ:
Writeln($' Число детей у Исхана: {n}');
Writeln($' Поколения: 1, {n}, {n.IPower(2)}, {n.IPower(3)},
{n.IPower(4)}');
Writeln;
end;
begin
Writeln(' Турецкие долгожители');
Writeln(' Математический фольклор. Задача Д32');
Writeln;
Solve();
end.
Запускаем программу. Все детишки Исхана - великолепная семёрка - аккуратно пересчитаны. Добиваем окружающую среду полной статистикой плодородия долгожителя Исхана:
Турецкие долгожители
Математический фольклор. Задача Д32
Число детей у Исхана: 7
Поколения: 1, 7, 49, 343, 2401
Проводим детоперепись классически:
procedure Solve2();
begin
for var n := 1 to integer.MaxValue do
begin
// n - число детей у Исхана
// n * n - число внуков у Исхана
// n * n * n - число правнуков у Исхана
// n * n * n * n - число праправнуков у Исхана
287
// всего - 2800 человек без Исхана:
if (n + n.IPower(2) + n.IPower(3) + n.IPower(4) = 2800) then
begin
Writeln(' Число детей у Исхана: ' + n);
Writeln;
break;
end
end;
Writeln;
end;
begin
Writeln(' Турецкие долгожители');
Writeln(' Математический фольклор. Задача Д32');
Writeln;
Solve();
Solve2();
end.
288
Проект Винные бочки
Исходный код программы находится в файле Винные бочки.pas.
Эта задача льётся и струится из книги Математический фольклор, где она
служит затычкой бочки номер Д31.
Справедливости ради следует отметить, что у болгар
также имеется задача про эти винные бочки.
С содержимым бочек, бутылок, фляжек и прочих сосудов и
склянок у нас связаны самые тёплые воспоминания. И тут
я вынужден обязательно порекомендовать вам к прослушиванию и к просмотру песню Уральских пельменей под
289
этикеткой «40 градусов тепла» из шоу «Вуз в рукаве».
Она весело бодрит всех, кто ещё под градусом или только
готовится к сугреву.
Из условия задачи ясно и явно следует, что каждый получил по 7 бочек, в
которых содержится 3,5 бочки вина.
Из этих семи бочек:
a - полных бочек
b - полных наполовину
c - пустых
Значения a и b заключены в диапазоне 0..7, причём их общее число не может превышать 7.
На долю пустых бочек с приходится 7 – a – b.
Начинаем решение задачи и начиняем её списком всех возможных распределений семи бочек разной наполненности между мужиками.
290
Тут же мы должны учесть условие задачи, что каждый из них получил по
3,5 бочки вина. Чтобы упростить проверку и избежать дробей, умножаем
число бочек и полученный объём вина на 2.
Пополняем список новой комбинацией распределённых бочек.
Всего существует 4 способа поделить бочки по-мужски, то есть по-честному.
Комбинируем эти способы дележа так, чтобы число разнозаполненных бочек равнялось семи. Всего имеется 6 способов дележа бочек по-братски
между всеми мужиками.
uses MathExtensions;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
// a - полных бочек
// b - полных наполовину
// c - пустых
begin
// список бочек у каждого персонажа:
var lst := new List<array of int>();
// решаем задачу -->
// полные бочки:
for var a := 0 to 7 do begin
// бочки, наполненные наполовину:
for var b := 0 to 7 do begin
// их должно быть не больше 7:
if (a + b > 7) then continue;
// должно быть не более 7/2 бочек вина:
if (2 * a + b <> 7) then continue;
// записываем бочковую комбинацию:
lst.Add([a, b, 7 - a - b]);
end;
end;
// все способы дележа бочек:
foreach var c in lst do begin
var s : string := ' ';
291
foreach var i in c do
s += i + ' ';
Writeln($' {s}')
end;
Writeln();
var res := 0;
foreach var i in lst do
foreach var p in lst do
foreach var v in lst do begin
for var n := 0 to 3-1 do begin
if (i[n] + p[n] + v[n] = 7) then begin
res += 1;
Writeln($' Иван получил:
{i[0]} полных {i[1]}
наполовину {i[2]} пустых');
Writeln($' Пётр получил:
{p[0]} полных {p[1]}
наполовину {p[2]} пустых');
Writeln($' Василий получил: {v[0]} полных {v[1]}
наполовину {v[2]} пустых');
Writeln;
break;
end;
end;
end;
Writeln($' Всего найдено решений: {res}');
end;
begin
Writeln(' Винные бочки');
Writeln(' Математический фольклор. Задача Д31');
Writeln;
Solve();
end.
Делить на троих всегда трудная задача, но мы с ней справились:
Винные бочки
Математический фольклор. Задача Д31
0 7 0
1 5 1
2 3 2
292
3 1 3
Иван получил:
1 полных 5 наполовину 1 пустых
Пётр получил:
3 полных 1 наполовину 3 пустых
Василий получил: 3 полных 1 наполовину 3 пустых
Иван получил:
2 полных 3 наполовину 2 пустых
Пётр получил:
2 полных 3 наполовину 2 пустых
Василий получил: 3 полных 1 наполовину 3 пустых
Иван получил:
2 полных 3 наполовину 2 пустых
Пётр получил:
3 полных 1 наполовину 3 пустых
Василий получил: 2 полных 3 наполовину 2 пустых
Иван получил:
3 полных 1 наполовину 3 пустых
Пётр получил:
1 полных 5 наполовину 1 пустых
Василий получил: 3 полных 1 наполовину 3 пустых
Иван получил:
3 полных 1 наполовину 3 пустых
Пётр получил:
2 полных 3 наполовину 2 пустых
Василий получил: 2 полных 3 наполовину 2 пустых
Иван получил:
3 полных 1 наполовину 3 пустых
Пётр получил:
3 полных 1 наполовину 3 пустых
Василий получил: 1 полных 5 наполовину 1 пустых
Всего найдено решений: 6
В задаче неявно предполагается, что вино нельзя переливать из одной
бочки в другую. В книге Математический фольклор, в Задаче 43 это ограничение отсутствует.
Совершенно очевидно, что 8 бочек невозможно поровну разделить на 3 части. Но мы можем из двух полных бочек перелить вино в другие бочки, тогда полных бочек останется 6, и мы сможем передать каждому сыну по 2
полные бочки.
Переливать вино в полупустые бочки смысла нет, поэтому из двух полных
бочек мы переливаем вино в 4 пустые бочки, превращая их в полупустые.
Теперь мы имеем 12 полупустых бочек. От восьми пустых бочек осталось 4,
но к ним добавились две бочки, которые раньше были полными. В
293
результате этих переливаний из полного в порожнее мы получили другой
расклад бочек: 6 12 6.
Число всех видов бочек кратно 3, так что мы легко поделим их между сыновьями поровну: 2 4 2. Каждому досталось по 2 полные и 2 пустые бочки, а
также по 4 полупустые бочки.
И последнее замечание: распределить бочки так, чтобы все три мужика
имели разные наборы бочек — нельзя.
294
Проект Чешские сливы
Исходный код программы находится в файле Чешские сливы.pas.
Внимательно читаем обычную для чехов сказочную историю про сливы. Но
к нам она пришла не из сказки-раскраски, а из книги Математический
фольклор, откуда она удачно слилась под номером Д27.
Вы непременно должны увидеть в этой задаче усложнённый вариант Русских яблок, поэтому мы в сжатые сроки непринуждённо переделаем яблочный исходный код в сливовый.
Если вы хотите больше узнать про сливы, их влияние на
здоровье и жизнь человека как таковую, то смотрите номер «Универмаг Пуля» из шоу «Уральских пельменей» «20
лет в тесте. Часть первая».
295
Если вам этого оказалось или показалось недостаточно,
чтобы
понять
и
внять,
то
продолжайте
смотреть
«Уральских пельменей». В шоу «Пинг-понг жив» вы снова
встретите на своём жизненном пути сливу в номере «Супермаркет Пуля».
296
В некотором роде Пуля – это не только название магазина
быстрого обслуживания, но и метафора мытарств людей,
которые не соблюдают повсеместно санитарную гигиену и
дисциплину. Об этом нам напоминает и нас предупреждает
песня про сливы из шоу «Уральских пельменей» «Корпорация морсов. 2 отжим».
Во внешнем цикле while мы бесконечно, но поштучно перебираем сливы:
uses MathExtensions;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var sliva := 1.0;
while True do begin
var ost := sliva;
Во вложенном цикле for находим остаток от текущего наличия слив:
for var i := 1 to 3 do
ost := ost / 2 - i;
Если сливы успешно обнулились, то мы печатаем наш ответ на сказочную
чешскую задачу:
// печатаем ответ:
if (ost = 0) then begin
Writeln($' Слив было: {sliva}');
Writeln;
break;
end;
sliva += 1;
297
end;
end;
begin
Writeln(' Чешские сливы');
Writeln(' Математический фольклор. Задача Д27');
Writeln;
Solve();
end.
Чешские сливы
Математический фольклор. Задача Д27
Слив было: 34
Считаем сливы функционально:
// ПРОВЕРЯЕМ УСЛОВИЕ ЗАДАЧИ
function Uslovie(sliva : integer) : boolean;
begin
var ost := double(sliva);
for var i := 1 to 3 do
ost := ost / 2 - i;
Result := ost = 0;
end;
procedure Solve2();
begin
Write(' Слив было: ');
1.Step
.Where(sliva -> Uslovie(sliva))
.ElementAt(0)
.Println;
Writeln;
end;
begin
Writeln(' Чешские сливы');
Writeln(' Математический фольклор. Задача Д27');
298
Writeln;
Solve();
Solve2();
end.
Чешские сливы
Математический фольклор. Задача Д27
Слив было: 34
Слив было: 34
Со сливами всё кончено! Конченных слив оказалось 34 штуки, как знаменитый советский танк!
Тут и сказочке конец. Кто сливы вымыл – молодец!
Все довольны – вы, мы, ты, если сливы вымыты!
Как это обычно бывает со сливами, задача легко и бодро решается задом
наперёд.
Проект Французский покупатель
Исходный код программы находится в файле Французский покупатель.pas.
Решаем задачу Д26 из книги Математический фольклор, которую я метко
назвал Французский покупатель.
Если внимательно прочитать длинное условие задачи, обременённое франками, быстрыми займами и французским Пьеродактилем, то можно понять:
если бы он был осмотрительнее и не занимал деньги безоглядно, то задача
решалась бы точно так же, как с яблоками и сливами.
299
Тут мы непременно должны вспомнить
книгу Дьёрдя Пойа Как решать задачу,
чтобы подумать, как решать задачу.
А решать её нужно и следует так.
Во внешнем цикле while мы бесконечно
считаем французские франки французского Пьера. Считать чужие деньги легко
и приятно:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// массив займов:
var traty := [ 0, 1, 2, 5, 6 ];
var money := 1;
// личные деньги:
while True do begin
var sum := money;
300
Во вложенном цикле for считаем траты и растраты этого ненасытного
Пьера, изымая их из предварительно созданного нами благотворительного
фонда:
// займы:
for var n := 1 to 4 do
sum := sum + sum - traty[n];
Как только, но не сразу, а когда беглый от долгов Пьер полностью исчерпает
возможности семейного бюджета и займы, мы выведем Пьера на чистую
воду с полной конфискацией неблагоприобретённых финансовых вливаний в собственные карманы:
// печатаем ответ:
if (sum = 0) then begin
Writeln($' У Пьера было франков:
{money}');
Writeln($' Пьер потратил франков: {traty.Sum()}');
Writeln;
Оператор break навсегда прервёт учёт и контроль денежных знаков расторопного на широкую ногу Пьера:
break;
end;
money += 1;
end;
end;
begin
Writeln(' Французский покупатель');
Writeln(' Математический фольклор. Задача Д26');
Writeln;
Solve();
end.
301
Ответ на задачу весьма забавный: у Пьера было 2 франка, а потратил он 14!
Вот с кого мы должны брать пример растраты денег мимоходом или походя
по магазинам!
Французский покупатель
Математический фольклор. Задача Д26
У Пьера было франков:
2
Пьер потратил франков: 14
Тратим деньги функционально:
var traty := | 0, 1, 2, 5, 6 |;
// ПРОВЕРЯЕМ УСЛОВИЕ ЗАДАЧИ
function Uslovie(money : integer) : boolean;
begin
var sum := money;
for var n := 1 to 4 do
money := money + money - traty[n];
Result := money = 0;
end;
302
procedure Solve2();
begin
var res := 1.Step
.Where(money -> Uslovie(money))
.First;
Writeln($' У Пьера было франков:
{res}');
Writeln($' Пьер потратил франков: {traty.Sum()}');
Writeln;
end;
begin
Writeln(' Французский покупатель');
Writeln(' Математический фольклор. Задача Д26');
Writeln;
Solve();
Solve2();
end.
Эта задача наглядно показывает нам, что наличие денег возбуждает хорошее настроение, а оно, в свою очередь, - тягу к приобретательству, которое
приводит к тратам денег, отсутствие которых возбуждает хандру и депрессию.
Лучшее средство от хандры и депрессии – это работа, которая позволяет получить деньги и повысить настроение.
Итак, жизнь – это движение денег по кругу, или круговорот денег в природе.
303
Проект Болгарский парикмахер
Исходный код программы находится в файле Болгарский
хер.pas.
парикма-
С болгарскими перцами всегда возникают проблемы, но только не в этот
раз! Я имею в виду задачу 110 из книги Математический фольклор.
Если внезапно абстрагироваться от парикмахерской истории с левами и
правами, то мы неизбежно возвернёмся и придём к задаче с французским
покупателем, чешскими сливами и русскими яблоками.
То есть, кулинарно выражаясь, тех же щей и борщей, да пожиже только
влей. Тут, правда, вышла небольшая заминка, потому что влеи это в Румынии, а в Болгарии – влевы. Но это всё равно не остановит нас на пути к заветной цели.
Взяв на мушку и на прицел эту слегка математически поперчённую задачу,
мы постановили: расчёты лучше вести не в левах, а в стотинках, чтобы избежать неточных вычислений с вещественными числами. А окончательный
результат мы переведём в левы, как в книжном ответе.
304
Во внешнем цикле while мы добавляем болгарские левы, а во вложенном
цикле loop – вычитаем их из парикмахерского ящика до тех пор, пока этот
ящик не сыграет в ящик. И тогда болгарский перецмахер получит от нас
жёсткий отпор и наш твёрдый ответ на задачу:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var money := 100;
while True do begin
var ost := money;
// // парикмахерский процесс:
loop 3 do
ost := ost + ost - 200;
// печатаем ответ:
if (ost = 0) then begin
Writeln($' Денег было: {money / 100.0} лева');
Writeln;
break;
end;
money += 1;
end;
end;
begin
Writeln(' Болгарский парикмахер');
Writeln(' Математический фольклор. Задача 110');
Writeln;
Solve();
end.
Лихо, бодро и весело расправившись с задачей слева направо, мы предъявляем счёт насчёт счёта в ящике: там было 1,75 лева.
Болгарский парикмахер
Математический фольклор. Задача 110
Денег было: 1.75 лева
305
Постригаем всех функционально:
// ПРОВЕРЯЕМ УСЛОВИЕ ЗАДАЧИ
function Uslovie(money : integer) : boolean;
begin
for var i := 1 to 3 do
money := money + money - 200;
Result := money = 0;
end;
procedure Solve2();
begin
var money := 100.Step
.Where(money -> Uslovie(money))
.First;
Writeln($' Денег было: {money / 100.0} лева');
Writeln;
end;
begin
Writeln(' Болгарский парикмахер');
Writeln(' Математический фольклор. Задача 110');
Writeln;
Solve();
Solve2();
end.
Проект Болгарские сливы
Исходный код программы находится в файле Болгарские сливы.pas.
На смену обанкротившемуся с левами болгарскому парикмахеру поспели и
притопали к нам болгарские же сливы, которым тоже пришёл черёд закончиться счётом в корзине.
306
Удачно и умело осилив условие задачи, мы с прискорбием сообщаем себе,
что задача 116 из книги Математический фольклор со сливами чудесным
образом роднится не только со левами, но и с чешскими сливами, с русскими яблоками, и иже с ними.
Решение задачи со сливами, с точки зрения паскаля, что-то особенно сильно
напоминает решение задачи про французского покупателя, который так же
хаотично делал займы во франках, как женщины обошлись со сливами. Поэтому в сливовый массив мы впихиваем и втискиваем сливы для последующей удобной выемки их оттуда.
Затем в бесконечном цикле while добавляем сливы в корзину, а во вложенном цикле for изымаем их оттуда с учётом сливового массива. Процесс изъятия слив неизбежно опустошает корзину, о чём мы поведаем себе и миру
наглядно, явно и на экране:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// массив слив:
var brali := [0, 1, 1, 3];
307
var sliva := 1.0;
while True do begin
var ost := sliva;
// задачный процесс:
for var i :=1 to 3 do
ost := ost / 2 - brali[i];
// печатаем ответ:
if (ost = 0) then begin
Writeln($' Слив было: {sliva}');
Writeln;
break;
end;
sliva += 1;
end;
end;
begin
Writeln(' Болгарские сливы');
Writeln(' Математический фольклор. Задача 116');
Writeln;
Solve();
end.
Запускаем программу и получаем счёт слив в нашу пользу, которых оказалось ровным счётом 30 штучек.
Болгарские сливы
Математический фольклор. Задача 116
Слив было: 30
Эта задача легко решается задом наперёд. И функционально:
// ПРОВЕРЯЕМ УСЛОВИЕ ЗАДАЧИ
function Uslovie(sliva : integer) : boolean;
begin
var brali := | 0, 1, 1, 3 |;
var ost := double(sliva);
for var i := 1 to 3 do
308
ost := ost / 2 - brali[i];
Result := ost = 0;
end;
procedure Solve2();
begin
var res := 1.Step
.Where(sliva -> Uslovie(sliva))
.First;
Writeln($' Слив было: {res}');
Writeln;
end;
begin
Writeln(' Болгарские сливы');
Writeln(' Математический фольклор. Задача 116');
Writeln;
Solve();
Solve2();
end.
309
Проект Индийские рупии
Исходный код программы находится в файле Индийские рупии.pas.
Если кто не знает, что вряд ли, то рупии – это как белорусские рубли, только
индийские. Мы уже походя встречались с русскими рублями, французскими
франками, американскими долларами, немецкими пфеннигами, римскими
динариями и с прочими мирскими и заморскими монетами.
Имея такой изрядно богатый жизненный опыт в финансовых делах, можем
ли мы спасовать перед задачей Д11 из книги Математический фольклор? –
Никогда! Единственное, что нам может помешать в этом деле, - неаккуратное прочтение условия задачи или картавость языка речи.
Напрочь не обладая этими отвратительными для математики изъянами
особенностей, мы дюже сдюжим эту задачу про если бы кто-то кому-то дал
рупии, чего никогда не бывает.
310
Очевидно, что у первого было не меньше 10 рупий, а у второго – не меньше
100. Верхний предел нам неизвестен, но нельзя организовать 2 бесконечных вложенных цикла, поэтому во втором (но не в первом!) цикле мы полагаем, что у второго было не больше 1000 рупий. В случае неудачи мы повысим его денежное содержание без последующего удержания недержания.
По условию задачи, должны выполниться 2 условия, которые легко изложить на языке паскаля.
Если нам повезёт с первого раза, что скорее всего, чем нет, то мы напечатаем
счёт в рупиях для первого и второго анонимщика.
procedure Solve();
begin
var one := 10;
while True do begin
for var two := 100 to 1000 do begin
// условия задачи:
var uslovie1 := one + 100 = 2 * (two - 100);
var uslovie2 := two + 10 = 6 * (one - 10);
// печатаем ответ:
if (uslovie1 and uslovie2) then begin
Writeln($' У первого было: {one} рупий');
Writeln($' У второго было: {two} рупий');
Writeln;
exit;
end;
end;
one += 1;
end;
end;
begin
Writeln(' Индийские рупии');
Writeln(' Математический фольклор. Задача Д11');
Writeln;
Solve();
end.
311
Аналогично, но не даром, мы можем решить задачу в функциональном
стиле. Это дело для нас привычное и не шибко обременительное.
procedure Solve2();
begin
var x := Range(0, 1000);
var y := Range(0, 1000);
var res := Cartesian(x,y)
.where(\(x, y) -> x + 100 = 2 * (y - 100))
.where(\(x, y) -> y + 10 = 6 * (x - 10))
.Select(\(x, y) -> (x, y));
// печатаем ответ:
foreach var (one, two) in res do begin
Writeln($' У первого было: {one} рупий');
Writeln($' У второго было: {two} рупий');
end;
Writeln;
end;
// условие 1
// условие 2
Люди с математическим складом ума, который пока ещё не ограбили, могут
по условию задачи составить систему линейных уравнений и лихо, с
наскока и подскока решить её по методу Крамера, которым математики
кормятся уже давно.
// Функция для решения системы линейных уравнений методом Крамера
function SolveLinearSystem(a1, b1, c1,
a2, b2, c2 : double) : (double, double);
begin
// вычисляем определитель системы:
var determinant := a1 * b2 - a2 * b1;
// если определитель равен нулю,
// система не имеет единственного решения:
if (determinant = 0) then begin
Writeln($' Система не имеет единственного решения!');
Writeln;
Result := (-1, -1);
end;
// вычисляем определители для x и y:
var detX := c1 * b2 - c2 * b1;
312
var detY := a1 * c2 - a2 * c1;
// находим решения:
var x := detX / determinant;
var y := detY / determinant;
Result := (x, y);
end;
procedure Solve3();
begin
// решаем систему уравнений:
// уравнение 1: A - 2B = -300
// уравнение 2:-6A + B = -70
var res := SolveLinearSystem(1, -2, -300,
-6, 1, -70);
// печатаем ответ:
Writeln($' У первого было: {res.Item1} рупий');
Writeln($' У второго было: {res.Item2} рупий');
Writeln;
end;
begin
Writeln(' Индийские рупии');
Writeln(' Математический фольклор. Задача Д11');
Writeln;
Solve();
Solve2();
Solve3();
end.
Но как задачу ни решай и как рупии ни считай, итог всегда одинаково такой
и не получится другой:
У первого было 40 рупий, а у второго – 170.
Индийские рупии
Математический фольклор. Задача Д11
У первого было: 40 рупий
У второго было: 170 рупий
313
У первого было: 40 рупий
У второго было: 170 рупий
У первого было: 40 рупий
У второго было: 170 рупий
Эта задача показывает нам, что древние индийские математики владели
методами решения систем уравнений задолго до их формального описания
в Европе. Подобные задачи встречаются в трактатах Брахмагупты (VII век)
и Бхаскары (XII век).
314
Проект Русские гуси
Исходный код программы находится в файле Русские гуси.pas.
Наконец-то мы добрались до живой пернатой и лапчатой живности природы, достойной упоминания в занимательных математических задачах.
Тут речь пойдёт и уже идёт о задаче Д22 из книги Математический фольклор про русских гусей.
Мы уже знаем присказки про рыбу, колбасу, деньги и болгарские сливы. В
России же есть большие белые лебеди, но сейчас задача не про них. Гуси
тоже ретиво летают стаями, но мельче и быстрее в полёте.
Для большей достоверности событий мы можем символически раскрасить
гусей, или - по-научному гусе-гусачков – в подобающие случаю цвета, тем
паче что гуси уже на две трети и сами сподобились так раскраситься в своей
природе бытия.
315
Что касается собственно гусиной задачи, то она неожиданно просто решается перебором в одном цикле и в одно действие. Нужно просто внимательно прочитать её и записать занимательное условие на математическом
языке.
Задача не про кроликов и даже не про фазанов, а про гусей. Считать ноголапы нам не нужно, а вот головы – непременно.
Обоюдопонятно, что гусей меньше сотни, поэтому мы можем найти их в
цикле. Как уже было обговорено нами ранее и допрежь, затеваем разумный
цикл for, начинающийся с единицы:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
for var гусей := 1 to 100 do
Также очевидно, что алгебраически задача решается с помощью линейного
уравнения, то есть решение единственное.
На каждой итерации проверяем условие задачи, высказанной гогочущим
гусиным вожаком стаи: если к числу гусей добавить столько же, ещё половину, четверть и одного гуся, то получится ровно сотня:
if (гусей + гусей + гусей div 2 + гусей div 4 + 1 = 100) then
Гусиные вожаки обычно не врут, потому что это птицы высокого полёта.
Скорее раньше, чем позже, высказывание предводителя стаи математически верно подтвердится, и мы от души с улыбкой от уши до уши выдадим
наш достойный ответ вопрошающему гусю:
begin
// печатаем ответ:
var otvet := $' Гусей было: {гусей}';
Writeln(otvet);
break;
316
end;
end;
begin
Writeln(' Русские гуси');
Writeln(' Задача Д22 из книги Математический фольклор');
Writeln;
Solve();
Writeln;
end.
Запускаем программу. Стайных гусей оказалось 36 клювоголов. Это составляет 6 в квадрате. Или это сумма кубов трёх первых натуральных чисел. Или
восьмое треугольное число. Или 36 картей четырёх мастей. Или полтора суток в часах. Как видите, русские гуси – очень хорошие математики и арифметики!
Русские гуси
Задача Д22 из книги Математический фольклор
Гусей было: 36
Настоящие гусе-гусачки предпочитают функциональное решение:
// решаем задачу в функциональном стиле:
function Uslovie(n: integer) := n + n + n / 2 + n / 4 + 1 = 100;
procedure Solve2();
begin
var res := 1.Step()
.Where(n -> Uslovie(n))
.First;
// печатаем ответ:
var otvet := $' Гусей было: {res}';
Writeln(otvet);
Writeln;
end;
317
У болгар имеется подобная задача про аистов (Книга Математический
фольклор, Задача 124):
Шёл человек с поля, а навстречу ему аисты.
- Здравствуйте, сто аистов, - поздоровался он.
А старший аист, их вожак, ответил:
- Нас не сто! А если к нам подлетит ещё столько, сколько нас, и ещё половина, и ещё четверть, и вместе с тобой нас станет 100!
Сколько было аистов?
В XVIII веке эта задача была популярна не только в России, но и в Германии,
только место гусей в ней заняли голуби (Книга Математический фольклор,
Задача Д21):
Крестьянин шёл с поля, встретил стаю голубей и сказал:
- Добрый день, сто голубей.
Но один из голубей ответил:
- Нет, нас не сто! Но если взять ещё столько, ещё половину, ещё четверть, и
вместе с тобой нас будет сто.
Сколько было голубей?
318
Копнув глубже историю математики, мы найдём в книге 6000 лет математики, на странице 344 задачу про подмастерьев, но они не должны сбить
нас с толку. Дальше по тексту мы видим, что арифметически задача решается с помощью простого линейного уравнения, в котором нет четверти и
ещё одного подмастерья. Вероятно, за сотню лет задача слегка усложнилась
и полностью ушла в фауну.
И неожиданная российская интерпретация этой же задачи (Книга Математический фольклор, Задача Д20).
Отец спросил учителя своего сына, сколько у него учеников. Учитель ответил:
- Если взять ещё столько учеников, сколько уже есть, да ещё половину и четверть их, да ещё другого твоего сына, то станет точно 100.
Сколько у него учеников?
319
Проект Задача Ризе
Исходный код программы находится в файле Задача Ризе.pas.
Решая задачу про русских гусей, мы копнули глубже и нашли там немецкую
задачу про подмастерьев, которую смастерил Адам Рис.
Легко заметить, что немецкий язык того времени весьма сильно отличался
от современного, поэтому фамилию автора задачи записывают также как
Ризе.
В Википедии вы можете углубить свои знания об этом Рисе-Ризе, если пожелаете усугубить расширение своего кругозора.
Как вы помните, эта задача – предшественница задач про русских гусей,
болгарских аистов и немецких голубей - решается в уме даже без напряжения мысли.
Но на следующей странице нас с нетерпением ждёт ожиданием вторая задача Адама Ризе, которая с наскока не решается. Но мы решим её с помощью перебора.
Как говаривал наш деревянный друг Буратино, некто за 100 гульденов купил 100 голов домашнего скота.
Быки стоили по четыре гульдена, свиньи – по полтора, телята – по половине гульдена и козы – по четверти гульдена.
По условиям задачи мы можем написать 2 уравнения, в которых 4 неизвестных. В книге прямо указано, что такие задачи умели решать ещё китайские математики, задолго до Адама Ризе и прочих европейских умников.
В книге приводятся 2 конкретных ответа, но также указано, что всего существует 222 решения этой задачи.
320
Поскольку задача решается неоднозначно, то без перебора здесь не обойтись, что, безусловно, оправдывает нашу тягу к знаниям и программированию на паскале.
Не мудрствуя лукаво пишем 4 цикла – по одному циклу для каждой хозяйственной живности.
Для краткости я обозначил животных буквами, как это принято в математике, но в той же последовательности, как и в книге. Если кто умудрится
запутаться в буквах и цифрах, то я не виноват.
В циклах мы учитываем, что быков куплено не более двадцати пяти, свиней – не более шестидесяти шести, телят и коз – не более сотни. Эта небольшая оптимизация необязательна, но иногда следует и умом раскинуть.
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// число вариантов:
var nSolutions := 0;
// минимальное число каждого вида животных:
321
var min := 1;
for var a := min to 25 do
for var b := min to 66 do
for var c := min to 100 do
for var d := min to 100 do begin
В первом условии мы учитываем, что всего куплено сто голов:
if (a + b + c + d <> 100) then
continue;
А, перемножив головы на стоимость оных и добросовестно просуммировав
произведения, мы получаем второе условие задачи:
if (a * 4 + b * 1.5 + c * 0.5 + d * 0.25 <> 100) then
continue;
Когда оба условия выполнятся, мы опубликуем решение в списке, а заодно
посчитаем общее число решений.
Writeln($' a = {a} b = {b}
nSolutions += 1;
end;
Writeln();
Writeln($' Всего: {nSolutions}');
c = {c}
d = {d}');
end;
begin
Writeln(' Задача Ризе');
Writeln(' 6000 лет математики');
Writeln;
Solve();
Writeln;
end.
322
Как учит нас книга, всего имеется 222 решения этой животрепещущей животноводческой задачи.
Задача Ризе
6000 лет математики
a = 1
a = 1
a = 1
b = 47
b = 48
b = 49
a = 18
a = 19
a = 19
b = 5
b = 1
b = 2
...
c = 50
c = 45
c = 40
d = 2
d = 6
d = 10
c = 5 d = 72
c = 10 d = 70
c = 5 d = 74
Всего: 222
Философский вопрос, может ли какая-либо сущность живности отсутствовать вообще, мы уже решили не в нашу пользу. То есть хотя бы одна голова
каждой животины должна быть и присутствовать в решении задачи.
Если же отрешиться от минимальных ограничений, то число решений ещё
более увеличится, но некоторые решения станут излишне простыми, что
нехорошо для ума.
323
Проект Немецкий вопрос
Исходный код программы находится в файле Немецкий вопрос.pas.
Разохотившись на немецкие задачи, давайте решим ещё одну – но из нынешних времён. Вы можете найти её в книге Математический фольклор Д14. Она
должна смутно напомнить вам задачу Ризе про подмастерьев, а также все последующие за ними.
Нетрудно заметить, что возраст отца кратен 4. Перебираем все числа, делящиеся на 4 без остатка, в бесконечном цикле while до тех пор, пока не выполнится условие задачи. Опыт решения линейных уравнений и аналогичные
задачные аналоги должны убедить нас, что задача имеет единственное решение.
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
324
begin
var лет := 0;
while True do begin
if (лет + лет div 2 + лет div 4 + 1 = 134) then begin
Writeln($' Возраст отца = {лет}');
break;
end;
лет += 4;
end;
end;
begin
Writeln(' Немецкий вопрос');
Writeln(' Математический фольклор, задача Д14');
Writeln;
Solve();
Writeln;
end.
Возраст отца 76 лет. Наверняка у этого отца взрослый сын. Странно, что он
не знает его возраста…
Немецкий вопрос
Математический фольклор, задача Д14
Возраст отца = 76
Чешем голову функционально:
procedure Solve2();
begin
var let := 0;
Write(' Возраст отца: ');
let.Step(4)
.Where(let -> let + let div 2 + let div 4 + 1 = 134)
.First
.Println;
Println;
end;
325
begin
Writeln(' Немецкий вопрос');
Writeln(' Математический фольклор, задача Д14');
Writeln;
Solve(); Writeln;
Solve2();
end.
Проект Задача Михаэля Штифеля
Исходный код программы находится в файле Задача Михаэля Штифеля.pas.
Первая задача из трёхтомника Михаэля Штифеля Arithmetica integra, напечатанного в 1544 году. Вы можете найти её в книге Увлекательная математика,
задача АПП4.
326
Я думаю, мы поступим правильно и верно, если не ограничимся первым числом, а заглянем после него дальше. Например, до тысячи:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// перебираем числа в заданном диапазоне:
for var num := 1 to 1000 do begin
var flg := true;
// перебираем остатки:
for var i := 3 to 6 do begin
if (num mod i <> i - 2) then begin
flg := false;
break;
end
end;
// нашли число:
if (flg) then
Writeln($' Число = {num}');
end;
end;
begin
Writeln(' Задача Михаэля Штифеля');
Writeln(' Увлекательная математика, задача АПП4');
Writeln;
Solve();
Writeln;
end.
Оказалось, что таких чисел бесконечно много, а первое из них – 58:
Задача Михаэля Штифеля
Увлекательная математика, задача АПП4
Число
Число
Число
Число
Число
Число
Число
=
=
=
=
=
=
=
58
118
178
238
298
358
418
327
Число
Число
Число
Число
Число
Число
Число
Число
Число
=
=
=
=
=
=
=
=
=
478
538
598
658
718
778
838
898
958
Каждое последующее больше предыдущего на 60 – это число, которое, кратно
всем штифелевым делителям.
328
Проект Китайская арифметика
Исходный код программы находится в файле Китайская арифметика.pas.
Почти через полвека после выхода книг Михаэля Штифеля, в 1593 году появился китайский трактат Начала искусства вычисления, в котором была
очень похожая задача. Это знаменитая Задача Сунь Цзы или Китайская теорема об остатках. Вы найдёте её в журнале Наука и жизнь, № 3, страница
38.
К счастью для нас и китайской математики, условие задачи оказалось коротким и неприхотливым для решения задачи любым разумным способом.
И тут мы должны отвесить низкий поклон древним китайским учёным, которые не шибко замучили нас из глубины веков. Китайскими словами это
будет так: «се се!»
Всем известно выражение китайская грамота, означающее сложные и непонятные вещи. Напротив, китайская арифметика и проста, и понятна.
329
Здесь остатки «незакономерны», поэтому выписываем все три условия целиком.
В условии задачи не требуется найти минимальное из возможных чисел, поэтому мы нашли сразу десяток:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var vars := 0;
var num := 0;
while vars < 10 do begin
if (num mod 3 = 2) and (num mod 5 = 3) and (num mod 7 = 2) then begin
Writeln($' Число = {num}');
vars += 1;
end;
num += 1;
end;
end;
begin
Writeln(' Китайская арифметика');
Writeln(' Наука и жизнь №3, 1963 год. Стр. 38, задача 3');
Writeln;
Solve();
Writeln;
end.
Запускаем программу в глубь математической истории.
Минимальное число равно 23. Каждое последующее больше предыдущего
на 105 – это число, которое, кратно всем китайским делителям.
Китайская арифметика
Наука и жизнь №3, 1963 год. Стр. 38, задача 3
Число
Число
Число
Число
=
=
=
=
23
128
233
338
330
Число
Число
Число
Число
Число
Число
=
=
=
=
=
=
443
548
653
758
863
968
Не успокаиваясь на,↓
// решаем задачу в функциональном стиле:
function Uslovie(num: integer) := (num mod 3 = 2) and
(num mod 5 = 3) and
(num mod 7 = 2);
procedure Solve2();
begin
var res := 0.Step()
.Where(n -> Uslovie(n))
.Take(10);
// печатаем ответ:
foreach var n in res do
Writeln($' Число = {n}');
Writeln;
end;
331
Проект Вторая задача Михаэля Штифеля
Исходный код программы находится в файле Вторая задача Михаэля
Штифеля.pas.
В этом проекте мы решим вторую задачу из трёхтомника Михаэля Штифеля
Arithmetica integra, напечатанного в 1544 году. Вы можете найти её в книге
Увлекательная математика, задача АПП5.
Оба числа – натуральные. Первое из них находится в диапазоне от одного
до восьмидесяти девяти, а второе дополняет его до девяноста.
Нам нужно перебрать в цикле for все значения для первого числа и найти для
него второе число. Также мы должны следить за выполнением условия задачи, а там нас ждут проценты. Это значит, что проценты от целых чисел - не
обязательно целые числа, поэтому делить нужно на вещественные числа.
// РЕШАЕМ ЗАДАЧУ
332
procedure Solve();
begin
// ищем первое число в цикле:
for var n1 := 1 to 89 do begin
// второе число:
var n2 := 90 - n1;
Условие задачи записывается очень просто, поэтому решение вместе с написанием кода заняло пару минут.
// условие задачи:
if (n1 * 25 / 100.0 + n2 * 75 / 100.0 = 30) then begin
// печатаем ответ:
Writeln($' Первое число = {n1}');
Writeln($' Второе число = {n2}');
;
end;
end;
end;
begin
Writeln(' Вторая задача Михаэля Штифеля');
Writeln(' Увлекательная математика, задача АПП5');
Writeln;
Solve();
Writeln;
end.
Запускаем программу, которая быстро и бодро сообщает нам, что первое
число - 75, а второе – 15.
Вторая задача Михаэля Штифеля
Увлекательная математика, задача АПП5
Первое число = 75
Второе число = 15
333
Проект Третья задача Михаэля Штифеля
Исходный код программы находится в файле Третья задача Михаэля
Штифеля.pas.
В этом проекте мы решим третью, и последнюю на сейчас задачу из трёхтомника Михаэля Штифеля Arithmetica integra, напечатанного в 1544 году.
Вы можете найти её в книге Увлекательная математика, задача АПП6.
Здесь мы встречаемся с числами, не образующими непрерывный диапазон,
который легко проверить в цикле for. Поэтому мы поместим заданные
числа в массив num:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// инициализируем массив:
var num := [ 13, 15, 20 ];
334
В трёх вложенных циклах foreach мы перебираем все возможные значения
для переменных циклов a, b и c из этого массива. При этом следим, чтобы
числа не повторялись:
// число а:
foreach var a in num do begin
// число b:
foreach var b in num do begin
// число b не равно числу а:
if (b = a) then continue;
// число с:
foreach var c in num do begin
// число с не равно числам а и b:
if (c = b) or (c = a) then
continue;
Мы нашли разные значения для переменных a, b и c. Находим значение требуемого выражения res. Оно должно быть положительным и целым:
// вычисляем выражение:
var res := 1.0 * a * (c - b) / (b - a);
Первое условие проверяется легко, а для проверки второго нужно отбросить дробную часть числа res с помощью функции Trunc. Если число целое,
то его значение при этом не изменится:
// печатаем ответ:
if (res > 0) and (Trunc(res) = res) then begin
Когда оба условия выполнятся, мы элегантно напечатаем ответ:
// печатаем ответ:
Writeln($' a = {a}');
Writeln($' b = {b}');
Writeln($' c = {c}');
Writeln($' res = {res}');
335
end;
end;
end;
end;
end;
begin
Writeln(' Третья задача Михаэля Штифеля');
Writeln(' Увлекательная математика, задача АПП6');
Writeln;
Solve();
Writeln;
end.
Запускаем программу, которая с радостью сообщает нам, какие значения
принимают переменные a, b и c, чтобы результат получился целым и положительным.
Третья задача Михаэля Штифеля
Увлекательная математика, задача АПП6
a =
b =
c =
res
20
15
13
= 8
Проект Задача Этьена Безу
Исходный код программы находится в файле Задача Этьена Безу.pas.
Этьен Безу – французский математик XVIII века - преподавал математику в
Училище гардемаринов и в Королевском артиллерийском корпусе.
Нам известна такая задача Этьена Безу:
336
Пусть они отработали х дней.
За эти дни им причитается по 48х франков.
Остальные (30 - х) дней были «выходными».
За них работники должны вернуть 12(30 - х) франков.
Всего за 30 дней каждому работнику причитается 0 франков:
48х - 12(30 - х) = 0
Так как число дней – целое, то уравнение решается простым перебором в
«бесконечном» цикле for. Когда условие задачи выполнится, цикл прекратится.
Вместо математической переменной х мы создадим программистскую переменную days. Вот и вся задача:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
337
begin
for var days := 0 to integer.MaxValue do begin
if (48 * days - 12 * (30 - days) = 0) then begin
// печатаем ответ:
Writeln($' Отработано дней: {days}');
break;
end
end;
Writeln;
end;
begin
Writeln(' Задача Этьена Безу');
Writeln;
Solve();
end.
А ответ такой:
Задача Этьена Безу
Отработано дней: 6
Работаем на дядю функционально:
procedure Solve2();
begin
var days := 0;
days.Step
.Where(days -> 48 * days - 12 * (30 - days) = 0)
.Take(1)
.Select(days -> $' Отработано дней: {days}')
.Println;
Writeln;
end;
begin
Writeln(' Задача Этьена Безу');
Writeln;
338
Solve();
Solve2();
end.
Задачу легко решить в уме.
Работники за 1 отработанный день получают в 4 раза больше, чем с них
взыскивают за каждый неотработанный день. В итоге они не заработали
ничего. Это значит, что они отработали в 4 раза меньше дней, чем «прогуляли». Это составляет одну пятую от 30 дней, то есть 6 дней.
Проект Кому сколько лет?
Исходный код программы находится в файле Кому сколько лет.pas.
Задача 70 из книги Русские головоломки:
339
Легко сообразить, что деду 121 – 44 = 77 лет, потому что остальные 44 года
из общей суммы приходятся на отца и сына.
Пусть отцу x лет, а сыну – y. Вместе им 44 года:
x + y = 44
Условие, что сын на 28 лет моложе отца, даёт нам второе уравнение:
y = x – 28
Заменив математические иксы-игреки более осмысленными переменными
otets и syn, мы быстро составим программу:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
for var syn := 1 to integer.MaxValue do begin
var otets := 28 + syn;
if (otets + syn = 44) then begin
println($' Возраст отца: {otets}');
println($' Возраст сына:
{syn}');
break;
end
end;
println;
end;
begin
Writeln(' Кому сколько лет?');
Writeln(' Русские головоломки. Задача 70');
Writeln;
Solve();
end.
Ответ не заставил себя долго ждать:
Кому сколько лет?
Русские головоломки. Задача 70
Возраст отца: 36
340
Возраст сына:
8
Решаем задачу функционально:
procedure Solve2();
begin
var syn := 1;
var res := syn.Step
.Where(syn -> 28 + syn {otets} + syn = 44)
.First;
println($' Возраст отца: {res + 28}');
println($' Возраст сына: {res}');
println;
end;
begin
Writeln(' Кому сколько лет?');
Writeln(' Русские головоломки. Задача 70');
Writeln;
Solve();
Solve2();
end.
Как-то даже неудобно перед компьютером, что мы задаём ему такие простые задачки…
Проект Ноги и головы
Исходный код программы находится в файле Ноги и головы.pas.
Задача 71 из книги Русские головоломки:
341
На скотном дворе гуляли гуси и поросята. Хозяин и его сын вышли на
двор, посмотрели на живность и пошли в поле. По дороге сын и спрашивает:
- Отец, сколько у нас на скотном дворе гусей и сколько поросят?
- А вот угадай-ка сам, - ответил отец. – Если считать по головам, то на
дворе 25 голов, а если по ногам, то 70 ног.
Сколько было гусей и сколько поросят?
Эта задача очень похожа на кроличье-фазанью, которых здесь заменили поросята и гуси:
const
// общее число голов:
HEADS = 25;
// общее число ног:
LEGS = 70;
342
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// макс. число поросят:
var MAX_PORS := LEGS div 4;
//решаем задачу:
for var i := 0 to MAX_PORS do begin
// число гусей:
var nGus := HEADS - i;
if (i * 4 + nGus * 2 = LEGS) then begin
println($' Поросят: {i}');
println($' Гусей:
{nGus}');
end
end;
println;
end;
begin
Writeln(' Ноги и головы');
Writeln(' Русские головоломки. Задача 71');
Writeln;
Solve();
end.
Задача простейшая даже для компьютера. Инвентаризация закончена
мгновенно: гусей было 15 голов, а поросят – 10:
Ноги и головы
Русские головоломки. Задача 71
Поросят: 10
Гусей:
15
Считаем ноги по головам, но функционально:
procedure Solve2();
343
begin
// макс. число поросят:
var maxPoros := LEGS div 4;
var res := (0..maxPoros)
.Where(poros -> poros * 4 + (HEADS - poros) * 2 = LEGS)
.First;
Writeln($' Поросят: {res}');
Writeln($' Гусей:
{HEADS - res}');
Writeln;
end;
begin
Writeln(' Ноги и головы');
Writeln(' Русские головоломки. Задача 71');
Writeln;
Solve();
Solve2();
end.
344
Проект Репетитор
Исходный код программы находится в файле Репетитор.pas.
Читаем задачу по арифметике, которую придумал Антон Павлович Чехов в
рассказе Репетитор.
Вполне вероятно и может быть, что вы знакомы с известными произведениями Чехова по школьной программе. Но не каждый знает, что Чехов начинал с коротких юмористических рассказов. Очень советую вам прочитать
их!
Кстати говоря, у ещё более серьёзного писателя Достоевского также есть
занимательные рассказы, которые весьма необременительно для разума
почитать в свободное от безделья время.
345
И уж совсем непременно – читайте русскую классику. Но читайте её несмеша, неспеша и со смаком. И возрадуйтесь настоящему русскому языку,
каковым он и должен быть.
Из условия задачи ясно следует, что общая длина чёрного и синего сукна
равна 138 аршин. А общая стоимость покупки составила 540 рублей.
Наш богатый жизненный опыт должен подсказать нам, что эти числа выгодно и полезно выразить в программе целочисленными константами.
Идентифицировать эти константы вы можете по своему вкусу – по-русски
или по-английски, но непременно ПРОПИСНЫМИ буквами.
// общая длина сукна в аршинах:
const LENGTH = 138;
// общая стоимость сукна в рублях:
COST = 540;
Ясно и очевидно, что за COST рублей можно купить не более COST поделить на 5 – это цена одного аршина синего сукна.
Определяем новую константу в программе:
// макс. длина синего сукна:
MAX_BLUE = COST div 5;
Записываем цикл for c учётом суконного условия задачи. Когда, рано или
поздно оно выполнится, мы напечатаем наш достойный ответ:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
for var синее := 0 to MAX_BLUE do
begin
// длина чёрного сукна:
var чёрное := LENGTH - синее;
if синее * 5 + чёрное * 3 = COST then begin
// печатаем ответ:
346
Writeln($' Синего сукна: {синее}');
Writeln($' Чёрного сукна: {чёрное}');
Writeln;
end;
end;
end;
begin
Writeln(' Репетитор');
Writeln(' Г.Н.Попов «Сборник исторических задач по элементарной математике». Задача 100');
Writeln;
Solve();
end.
Запускаем программу на ура самим себе и получаем ответ!
Репетитор
Синего сукна: 63
Чёрного сукна: 75
Вот так, без особых затей и напряжения мысли мы решили художественную
классическую задачу.
Давайте решим задачу функциональным способом.
Из последовательности чисел 0.. MAX_BLUE, которую выдаёт функция
Range, отбираем такие, которые удовлетворяют условиям задачи.
procedure Solve2();
begin
// решаем задачу:
var blue := Range(0, MAX_BLUE)
347
Условие задачи записываем в методе расширения Where. В итоге мы получим последовательность, состоящую из одного элемента. Нужно взять его
методом ElementAt, чтобы переменная blue имела целый тип integer:
.Where(n -> n * 5 + (LENGTH - n) * 3 = COST)
.ElementAt(0);
Аршинные метры синего сукна хранятся в переменной blue. Остальные аршины вычисляем с помощью вычитания синих аршин из общей длины
сукна в этих же аршинах.
Всю прелесть наших аршинных вычислений печатаем на экране:
// печатаем ответ:
Writeln($' Синего сукна (аршин): {blue}');
Writeln($' Чёрного сукна (аршин): {LENGTH - blue}');
Writeln;
end;
begin
Writeln(' Репетитор');
Writeln;
Solve();
Solve2();
end.
Запускаем программу, которая решает вдрызг репетиторскую задачу.
Купец купил 63 аршина синего сукна и 75 аршинов – чёрного.
Репетитор
Синего сукна: 63
Чёрного сукна: 75
Синего сукна (аршин): 63
Чёрного сукна (аршин): 75
348
Папа книжного недоросля Удодов на счётах в два счёта уже давно решил эту
задачу.
Как видите, решение таких задач на компьютере – дело техники, а вот на
счётах – это уже искусство…
Читайте классиков!
349
Проект Насос Эдисона
Исходный код программы находится в файле Насос Эдисона.pas.
Задача СНП9 из книги Увлекательная математика основана на историческом анекдоте. Достоверно известно, что калитки с насосом у Эдисона не
было, да и быть не могло, поскольку её никто не смог бы открыть, и Эдисон
быстро остался бы без друзей.
Условие насосно-эдисосной задачи пространное, но приятное для чтения
как вслух, так и про себя, поскольку предполагает усмешку или даже смех.
Это очень полезно! Кстати говоря, очень полезно качать мышцы и мускулатуру, накачивая воду в цистерну. Желательно, конечно, в свою, потому что
от качать воду друзьям и соседям себе пользы будет не шибко много или
нет.
Вот что нам известно из известной песни Водовоз:
350
Удивительный вопрос:
Почему я водовоз?
Потому что без воды —
И ни туды и ни сюды!
Откуда же берётся вода в бочковой цистерне? – На этот удивительный вопрос нам ответил анекдотический Эдисон: нужно качать рычаг и туды, и
сюды!
Однако хватит лить воду в чужую цистерну, умирая над собой со смеху. Пора
нам вспомнить русский романс Калитка, припев которой начинается с
нашего ответа их Эдисону: Отвори потихоньку калитку и войди в тихий сад,
словно тень.
В переводе этих замечательных слов на современный суетливый язык: задачу Эдисона решить гораздо легче, чем отворить его калитку. Достаточно
переписать условие задачи на язык паскаля. При этом мы должны учесть,
что в нём присутствует операция деления, поэтому выбираем вещественный тип для переменной в бесконечном цикле while.
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var vol := 0.0;
while True do begin
Затем потихоньку и по-тихому проверяем условие задачи – до тех пор, пока
эдисоновская цистерна не наполнится чужими руками доверху, чего
обычно и добиваются все эдисоны во все времена и нравы.
// печатаем ответ:
if (vol / 25 = vol / 20 - 12) then begin
Writeln($' Объём цистерны = {vol}');
Writeln;
break;
end;
vol += 1;
end;
351
end;
begin
Writeln(' Насос Эдисона');
Writeln(' Увлекательная математика. Задача СНП9');
Writeln;
Solve();
end.
Если у вас с устатку ещё сохранились силы, то вы можете - туды-сюды! – решить эту задачу и в функциональном стиле.
// решаем задачу в функциональном стиле:
function Uslovie(vol: integer) := vol / 25 = vol / 20 - 12;
procedure Solve2();
begin
var res := 0.Step()
.Where(n -> Uslovie(n))
.First;
// печатаем ответ:
Writeln($' Объём цистерны = {res}');
Writeln;
end;
Окончательно устав руками от удивления, но накачав воду Эдисону, а себе
- бицепсы и трицепсы, посылаем ответ Эдисону: у большого мастера и цистерна была соответствующего объёма.
Насос Эдисона
Увлекательная математика. Задача СНП9
Объём цистерны = 1200
Объём цистерны = 1200
Дружите с математикой и программированием, и не дружите с разбогатевшим чужим трудом Эдисоном!
Берегите друзей, не нагружайте их своими заботами!
352
Проект Задача Рачинского
На картине русского художника Николая Петровича Богданова-Бельского
Устный счёт. В народной школе С. А .Рачинского (1895) вы можете видеть
интересное арифметическое выражение.
В журнале Наука и жизнь, № 7 за 2006 год, на страницах 50-51 обсуждаются
способы устного решения этой задачи.
Задача легко решается, если догадаться, что сумма трёх первых квадратов
равна сумме двух последних и равна … вы уже догадались чему?
Если не догадались, то давайте решим задачу на паскале:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := 10 * 10 + 11 * 11 + 12 * 12 + 13 * 13 + 14 * 14;
res := res div 365;
// печатаем ответ:
353
var otvet := $' Результат = {res}';
WriteLn(otvet);
Легко заметить, что числа образуют последовательность, поэтому сумму
квадратов можно вычислить в цикле. Результат мы получим тот же самый.
res := 0;
for var n := 10 to 14 do
res += n * n;
res := res div 365;
otvet := $' Результат = {res}';
WriteLn(otvet);
Менее очевидный способ обязан функциональному программированию.
Сначала мы создаём последовательность чисел 10..14, затем каждое из них
возводим в квадрат, и наконец, вычисляем сумму квадратов:
res := Range(10, 15)
.Select(n -> n * n)
.Sum();
res := res div 365;
otvet := $' Результат = {res}';
WriteLn(otvet);
end;
begin
Writeln(' Задача Рачинского');
Writeln(' Наука и жизнь, № 7, 2006 год, стр. 50-51 ');
Writeln;
Solve();
Writeln;
end.
Любую задачу можно решить несколькими способами. Знатоки и мастера
математических дел утверждают, что лучше одну задачу решить дюжиной
способов, чем дюжину задач — одним.
354
Задачу легко решить и в уме – получается двойка (не опять двойка, а просто двойка!)
Задача Рачинского
Наука и жизнь, № 7, 2006 год, стр. 50-51
Результат = 2
Результат = 2
Результат = 2
В книге Перельмана Занимательная алгебра вы
найдёте доказательство, что имеется ещё одна последовательность из 5 чисел, обладающая таким же свойством: сумма квадратов первых трёх чисел в точности
равна сумме квадратов последних двух чисел. Но эта последовательность
совершенно неинтересная:
-2, -1, 0, 1, 2
Иногда эту задачу обобщают, то есть не ограничивают длину последовательности. Такие ряды чисел называются последовательностями Рачинского. Они состоят из квадратов идущих подряд чисел, которые можно разбить на 2 группы с равными суммами так, что во второй группе на 1 число
меньше, чем в первой.
В журнале Наука и жизнь №8 за 2007 год опубликована статья Полознева
Последовательности Рачинского. Он доказал, что существует бесконечное
множество последовательностей Рачинского.
Самая первая последовательность — это пифагорова тройка чисел, известная всем по школьным урокам геометрии.
Вторая последовательность — картинная.
В правой части тождества число слагаемых каждый раз увеличивается на 1.
Точно так же и в левой.
32 + 42 = 52 = 25
212 + 222 + 232 + 242 = 252 + 262 + 272 = 2030
362 + 372 + 382 + 392 + 402 = 412 + 422 + 432 + 442 = 7230
355
32 + 42 = 52 = 25
102 + 112 + 122 = 132 + 142 = 365
212 + 222 + 232 + 242 = 252 + 262 + 272 = 2030
362 + 372 + 382 + 392 + 402 = 412 + 422 + 432 + 442 = 7230
Для всех последовательностей Рачинского любой длины существует
только одно тождество. Это доказал Королёв в журнале Наука и жизнь, №10
за 2007 год.
Если мы обозначим буквой k число слагаемых в правой части, то первый
член последовательности равен 2 умножить на k в квадрате плюс k.
Пользуясь этой формулой, вы найдёте столько последовательностей Рачинского, сколько пожелаете.
Также Королёв доказал, что суммы членов всех последовательностей
кратны пяти или даже десяти.
356
А также, что разности первых членов соседних последовательностей образуют арифметическую последовательность с разностью 4.
Картинная задача познакомила нас с интереснои последовательностью
чисел Рачинского, которая развивает идею пифагоровых троек чисел. А
также мы решили задачу на паскале тремя разными способами. За вами
остался еще один способ — решить задачу в уме, то есть без компьютера и
даже без бумаги и ручки. Попытаитесь!
Проект Задача Рачинского для любознательных
Если справа k слагаемых, то слева – k + 1. Пусть n – это первое слагаемое,
тогда для каждого k существует единственное натуральное n, удовлетворяющее уравнению. n можно найти, решив квадратное уравнение, полученное
из условия равенства сумм.
Алгоритм поиска последовательностей Рачинского:
• Перебираем k
• Для каждого k перебираем n
• Вычисляем суммы квадратов:
Левая часть: n² + (n+1)² + ... + (n+k)² (k+1 слагаемое)
Правая часть: (n+k+1)² + ... + (n+2k)² (k слагаемых)
• Проверяем равенство сумм
Простая программа для любознательных:
// РЕШАЕМ ЗАДАЧУ
procedure Solve(limit_n : integer; limit_k: integer);
begin
for var k := 1 to limit_k do
for var n := 1 to limit_n do begin
var left := 0;
var right := 0;
357
// Левая часть
for var i := 0 to limit_k do
left += (n + i) * (n + i);
// Правая часть
for var i := 1 to limit_k do
right += (n + k + i) * (n + k + i);
if left = right then
WriteLn($' Найдено: k={k}, n={n}, сумма={left}');
end;
end;
begin
Writeln(' Задача Рачинского для любознательных');
Writeln;
WriteLn(' Поиск последовательностей Рачинского:');
WriteLn(' n? + (n+1)? + ... + (n+k)? = (n+k+1)? + ... + (n+2k)?');
WriteLn;
Solve(100,
Solve(100,
Solve(200,
Solve(200,
Solve(200,
Solve(250,
Solve(300,
Solve(300,
Solve(500,
Solve(500,
Solve(500,
Writeln;
end.
5);
6);
7);
8);
9);
10);
11);
12);
13);
14);
15);
Самые простые последовательности вы уже видели, но мы можем найти и
другие!
k=5
55² + 56² + 57² + 58² + 59² + 60² = 61² + 62² + 63² + 64² + 65²
= 19855
358
k=6
78 + 79² + 80² + 81² + 82² + 83² + 84² = 85² + 86² + 87² + 88²
+ 89² + 90² = 45955
k=7
105² + 106² + 107² + 108² + 109² + 110² + 111² + 112² = 113² +
114² + 115² + 116² + 117² + 118² + 119² = 94220
k=8
136² + 137² + 138² + 139² + 140² + 141² + 142² + 143² + 144² =
145² + 146² + 147² + 148² + 149² + 150² + 151² + 152² = 176460
k=9
171² + 172² + 173² + 174² + 175² + 176² + 177² + 178² + 179² +
180² = 181² + 182² + 183² + 184² + 185² + 186² + 187² + 188² +
189² = 308085
k = 10
210² + 211² + 212² + 213² + 214² + 215² + 216² + 217² + 218² +
219² + 220² = 221² + 222² + 223² + 224² + 225² + 226² + 227² +
228² + 229² + 230² = 508585
Задания для самостоятельного решения
Задача Фольклор Д43
Бразильский кофе
Каждое утро Рейнальдо выпивает 1 чашку крепкого кофе, которая содержит определённую дозу кофеина. Однажды ему предстояло приготовить себе традиционный кофе из партии зёрен, из которых уже было извлечено 97% кофеина.
359
Сколько чашек такого кофе должен выпить Рейнальдо, чтобы в них
содержалось столько кофеина, сколько содержалось его в одной чашке
крепкого кофе?
Простая задача на пропорции.
Ответ: 33 ⅓ чашки.
Задача Фольклор Д29
Возраст сыновей
Когда у отца спросили, сколько лет его сыновьям, он ответил:
- Старшему в три раза больше, чем младшему, а им вместе столько лет,
сколько было мне 29 лет назад. Сейчас мне 45 лет.
Сколько лет сыновьям?
29 лет назад отцу было 16 лет. Отсюда следует…
Ответ: 12 лет и 4 года.
Задача Фольклор Д25
Который час?
На вопрос: «Сколько времени?» - был дан такой ответ:
- Две пятых времени, прошедшего от полночи до этого момента, равно
двум третьим времени, которое осталось до полудня.
Сколько сейчас времени?
360
От полночи до полудня 12 часов. Если от полночи прошло х часов, то до
полудня осталось (12-х) часов. Осталось составить простейшее уравнение
с одним неизвестным.
Ответ: 7 часов 30 минут.
Задача Фольклор Д24
Австралийский скотовод
Скотовод завещал трём своим сыновьям – Альфреду, Джону и Чарльзу –
разделить стадо овец следующим образом: Альфред получит на 20%
больше Джона и на 25% больше Чарльза. Часть Джона – 3600 овец.
Сколько овец получит Чарльз?
Простая задача, решение которой не требует дополнительных пояснений.
Ответ: 3456.
Задача Фольклор Д23
Французские братья
Три брата хотели купить дом, который стоил 26000 франков. Условились, что первый даст половину, второй – треть, а третий – четверть стоимости.
Сколько денег даст каждый?
Легко посчитать, что ½ + ⅓ + ¼ = 13/12, что соответствует 26000 франков. Теперь найти долю каждого брата не составит труда.
Ответ: 12000, 8000 и 6000.
361
Глава #3. Диофантовы уравнения
362
Под диофантовыми понимаются неопределённые уравнения, которые решаются в целых числах (часто – в натуральных). Они названы в честь древнегреческого математика Диофанта, жившего в третьем веке нашей эры.
Диофант Александриийский
Διόφαντος ὁ Ἀλεξανδρεύς, Diophantus
В честь Диофанта назван и кратер на Луне (на рисунке – в правом нижнем
углу).
363
В тринадцатитомном труде Арифметика (до нас дошли только 6 первых
книг) он показывает, как решать подобные уравнения.
В 1974 году была издана книга Арифметика и книга о многоугольных
числах, содержащая перевод на русский язык всех трудов Диофанта. В 2007
году книга была переиздана.
Диофантовы книги
Более подробно о Диофанте вы можете прочитать в книге Якова Перельмана Занимательная математика и Изабеллы Башмаковой.
И книги о Диофанте
364
Впрочем, диофантовы уравнения появились задолго до самого Диофанта.
Первое из таких уравнений было известно ещё в Древнем Вавилоне:
x2 + y2 = z2
Оно было решено пифагорейцами.
Второе уравнение:
x2 – ay2 = 1
решил в целых числах Евклид.
О способах решения диофантовых уравнений можно узнать в книге Башмаковой, а также в Справочном пособии к решению задач: Диофантовы уравнения Дмитрия Базылева.
Самая известная занимательная задача, связанная с диофантовыми уравнениями, это, безусловно, про кроликов и фазанов.
365
Проект На ферме
Исходный код программы находится в файле На ферме.pas.
Задача 12 (13) из книги Удивительный
мир чисел [КА86], страница 53:
На ферме выращивают кроликов и фазанов.
В настоящее время их столько, что у всех
вместе 740 голов и 1980 ног.
Сколько же в настоящее время находится на ферме кроликов и фазанов?
Поскольку и число голов, и число ног у кроликов и фазанов выражается целыми числами, то достаточно перебрать все варианты распределения 740
голов по кроликам и фазанам:
const
// общее число голов:
GOLOVY = 740;
// общее число ног:
NOGI = 1980;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var kroliki in Range(0, GOLOVY) do
foreach var fazany in Range(0, GOLOVY) do begin
if (kroliki + fazany <> GOLOVY) then
continue;
При этом мы должны учитывать, что у кроликов по 4 ноги, а у фазанов –
только по 2:
if (kroliki*4 + fazany*2 <> NOGI) then
continue;
366
WriteLn($' Кроликов: {kroliki}');
WriteLn($' Фазанов: {fazany}');
end;
WriteLn;
end;
begin
Writeln(' На ферме');
Writeln(' Кордемский, с.53, Задача 12');
Writeln;
Solve();
end.
И вмиг кролики и фазаны пересчитаны и занесены в Окно вывода нашей
программы:
На ферме
Кордемский, с.53, Задача 12
Кроликов: 250
Фазанов: 490
Проект Кролики и фазаны
Исходный код программы находится в файле Кролики и фазаны.pas.
Подобные задачи легко решаются простым перебором значении в циклах.
Дело это нехитрое, и вы наверняка его уже хорошо освоили. В этом проекте
мы решим эту задачу с помощью математическои библиотеки
PascalMathNet. Она находит решения систем линеиных уравнении.
Даваите решим такую задачу про кроликов и фазанов:
367
У фазанов и кроликов 62 ноги и 19 голов.
Сколько фазанов и кроликов?
Вызываем функцию Solve для непосредственного решения задачи:
begin
Writeln(' Кролики и фазаны');
Writeln;
Solve();
end.
Обозначаем константами число ног и голов:
uses PascalMathNet;
const
// общее число голов:
HEADS = 19;
// общее число ног:
LEGS = 62;
Это необязательно, но с ними программа понятнее и приятнее.
С константами все понятно, а вот уравнения еще нужно составить,
внимательно прочитав условие задачи, и вспомнить урок биологии, на
котором вы узнали, что у кроликов 4 ноги (или в общем случае конечности),
а у фазанов 2.
Согласно математическим правилам, составляем систему уравнений:
rabbits + pheasants = HEADS
4 * rabbits + 2 * pheasants = LEGS
Передаём функции Mat2 коэффициенты из левых частей уравнений:
368
procedure Solve();
begin
var left := MathNetPascal.Mat2(1,1, 4,2);
А в функцию Vec2 – свободные члены из правых частей:
var right := MathNetPascal.Vec2(HEADS, LEGS);
Метод Solve возвращает в переменную res корни системы уравнений:
var res := left.Solve(right);
Ответ на экране ясен и понятен каждому любителю живои природы и
диофантовых уравнении:
// печатаем ответ:
WriteLn(' Кроликов: ', res[0]);
WriteLn(' Фазанов:: ', res[1]);
Writeln;
end;
Кролики и фазаны
Кроликов: 12
Фазанов: 7
Чтобы решить задачу из предыдущего проекта, достаточно изменить
значения констант:
const
// общее число голов:
HEADS = 740;//19;
369
// общее число ног:
LEGS = 1980;//62;
Кролики и фазаны
Кроликов: 250
Фазанов: 490
На сайте http://festival.1september.ru/articles/569698/ вы найдёте такую разновидность этой китайской задачи с подробным разбором её решения:
Сегодня на уроке мы вновь встретимся с вами с хорошо известной вам задачей
про фазанов и кроликов (задача выводится на доску “В клетке находятся фазаны и кролики. Известно, что у них 35 голов и 94 ноги. Узнайте число фазанов
и число кроликов”), но если раньше мы её решали арифметическим способом,
то сегодня будем её решать с помощью уравнений и даже системы уравнений.
Решите её самостоятельно!
Проект Решите систему уравнений
Исходный код программы находится в файле Решите систему
уравнений.pas.
Задача 7 из книги Удивительный мир чисел
[КА86], страница 100:
Решите систему уравнений:
370
Далеко неочевидно, но значения переменных x и y должны быть целыми,
иначе верхнее равенство вряд ли выполнится. Его лучше записать в другом
виде:
x + y = 2x
Теперь можно сделать замену во втором уравнении:
2х * 6x = 248832 → 12х = 248832
После преобразований задача решается в одном коротком цикле:
uses MathExtensions;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var x in Range(0, 10) do
if 12.Power(x) = 248832 then begin
var y := 2.IPower(x) - x;
WriteLn($' x = {x} y = {y}');
Writeln;
break
end;
end;
begin
Writeln(' Решите систему уравнений');
Writeln(' Кордемский, с.100, Задача 7');
Writeln;
Solve();
end.
371
А вот и ответ на задачу. Значения переменных оказались совсем небольшими!
Решите систему уравнений
Кордемский, с.100, Задача 7
x = 5
y = 27
Проект Сооружение для лаборатории
Исходный код программы находится в файле Сооружение для лаборатории.pas.
Задача 7 из книги Удивительный мир чисел [КА86], страница 64:
Для
одной
лаборатории
изготовили
конструкцию
из двух спаянных пустотелых параллелепипедов
(см.
рисунок). Длины всех рёбер (в дециметрах) - целые
числа. Основания параллелепипедов — квадраты.
Высота первого параллелепипеда равна стороне
основания второго, а высота второго равна стороне основания первого параллелепипеда.
Поместится ли эта конструкция, объём которой
3900 дм3, в лаборатории, если расстояние от пола
лаборатории до потолка равно 2 м 75 см?
372
Поскольку x и y – целые числа, то мы можем просто перебрать все их возможные комбинации. Например, можно сразу заключить, что оба эти числа
больше нуля.
Из уравнения
x2*y + x*y2 = 3900
(1)
следует, что ни одно из этих чисел не больше корня квадратного из 3900.
Точное значение корня нам не нужно, пусть это будет 70.
Осталось в двух вложенных циклах foreach составить все пары чисел x и y и
для каждой пары проверить выполнение условия (1):
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var vars := 0;
foreach var x in Range(1, 70) do
foreach var y in Range(1, 70) do begin
if (x * x * y + x * y * y <> 3900) then
continue;
Если оно выполняется, значит, размеры конструкции найдены:
vars += 1;
WriteLn($' Вариант #{vars}');
WriteLn($' x = {x}');
WriteLn($' y = {y}');
Общая высота сооружения равна x+y. Значение этой суммы мы печатаем на
экране:
WriteLn($' Высота сооружения равна = {x + y}');
Writeln;
end;
373
end;
begin
Writeln(' Сооружение для лаборатории');
Writeln(' Кордемский, с.64, Задача 7');
Writeln;
Solve();
end.
Как показывает наша программа, либо x = 12, y = 13, либо y = 12, x = 13, но
в обоих случаях высота конструкции составляет 12 + 13 = 25 дециметров =
2,5 метра, что меньше высоты лаборатории. Значит, конструкция в ней поместится.
Сооружение для лаборатории
Кордемский, с.64, Задача 7
Вариант #1
x = 12
y = 13
Высота сооружения равна = 25
Вариант #2
x = 13
y = 12
Высота сооружения равна = 25
374
Проект И такие есть числа 3
Исходный код программы находится в файле И такие есть числа 3.pas.
Задача 4-3 из книги Удивительный мир чисел [КА86], страница 63:
Два простых числа обладают свойством:
если от каждого из них вычесть половину
другого, то одна разность будет в 5 раз
больше другой.
Найдите эти числа.
begin
Writeln(' И такие есть числа 3');
Writeln(' Кордемский, с.63, Задача 4-3');
Writeln;
Solve();
end.
Пусть первое число n1 меньше второго числа n2. Тогда n2 как минимум на
1 больше n1, но не более, чем в 2 раза.
Первое число мы ищем в бесконечном цикле while, начиная с двойки - первого простого числа. Второе число ищем в цикле foreach, начиная со следующего числа и заканчивая числом, вдвое большим, чем первое:
uses MathExtensions;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var n1 := 2 - 1;
while True do begin
375
n1 += 1;
if (not n1.IsPrime) then
continue;
foreach var n2 in Range(n1 + 2, 2 * n1) do begin
Для каждой пары чисел проверяем условие задачи:
if (2 * n2 - n1 <> 5 * (2 * n1 - n2)) then
continue;
Чтобы избежать дробных чисел, мы оба числа умножили на
двойку.
Как только оно выполнится, мы напечатаем решение на экране и закончим
поиски:
WriteLn($' Первое число = {n1}');
WriteLn($' Второе число = {n2}');
end;
end;
Writeln;
end;
Обратите внимание, что для решения этой задачи нам даже не потребовалось проверять оба числа n1 и n2 на простоту.
WriteLn($' Первое число = {n1}');
WriteLn($' Второе число = {n2}');
end;
end;
Writeln;
end;
Но если вы совсем «забудете» о проверке чисел на простоту, то найдёте ещё
множество пар чисел, которые удовлетворяют условиям задачи:
376
если от каждого из них вычесть половину другого, то одна разность будет в 5 раз больше другой.
Но при этом не являются простыми.
Пишем процедуру Solve2:
procedure Solve2();
begin
var vars := 0;
var n1 := 2 - 1;
while (vars < 30) do begin
n1 += 1;
foreach var n2 in Range(n1 +
if (2 * n2 - n1 <> 5 * (2
continue;
vars += 1;
WriteLn($' Первое число =
WriteLn($' Второе число =
Writeln;
end;
end;
Writeln;
end;
2, 2 * n1) do begin
* n1 - n2)) then
{n1}');
{n2}');
И получаем 30 пар чисел с такими свойствами:
Первое число = 7
Второе число = 11
Первое число = 77
Второе число = 121
Первое число = 147
Второе число = 231
Первое число = 14
Второе число = 22
Первое число = 84
Второе число = 132
Первое число = 154
Второе число = 242
Первое число = 21
Второе число = 33
Первое число = 91
Второе число = 143
Первое число = 161
Второе число = 253
Первое число = 28
Второе число = 44
Первое число = 98
Второе число = 154
Первое число = 168
Второе число = 264
377
Первое число = 35
Второе число = 55
Первое число = 105
Второе число = 165
Первое число = 175
Второе число = 275
Первое число = 42
Второе число = 66
Первое число = 112
Второе число = 176
Первое число = 182
Второе число = 286
Первое число = 49
Второе число = 77
Первое число = 119
Второе число = 187
Первое число = 189
Второе число = 297
Первое число = 56
Второе число = 88
Первое число = 126
Второе число = 198
Первое число = 196
Второе число = 308
Первое число = 63
Второе число = 99
Первое число = 133
Второе число = 209
Первое число = 203
Второе число = 319
Первое число = 70
Второе число = 110
Первое число = 140
Второе число = 220
Первое число = 210
Второе число = 330
Проект И такие есть числа 4
Исходный код программы находится в файле И такие есть числа 4.pas.
Задача 4-4 из книги Удивительный мир чисел [КА86],
страница 63:
Сумма первого числа и квадрата второго равна 57, а
сумма второго числа и квадрата первого равна 71.
Найдите эти числа.
Пусть первое число – это n1, а второе - n2. Тогда мы быстро получаем систему нелинейных уравнений:
n1 + n2 * n2 = 57
n1 * n1 + n2 = 71
378
Она легко решается в двух циклах foreach:
uses MathExtensions;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var n1 in Range(0, 100) do
foreach var n2 in Range(0, 100) do
if (n1 + n2 * n2 = 57) and (n1 * n1 + n2 = 71) then begin
WriteLn($' Первое число = {n1}');
WriteLn($' Второе число = {n2}');
end;
Writeln;
end;
begin
Writeln(' И такие есть числа 4');
Writeln(' Кордемский, с.63, Задача 4-3');
Writeln;
Solve();
end.
Запускаем программу и получаем ответ:
И такие есть числа 4
Кордемский, с.63, Задача 4-3
Первое число = 8
Второе число = 7
379
Проект Ящики
Исходный код программы находится в файле Ящики.pas.
Задача 424 из книги Математическая шкатулка [Нагибин88], страница
81:
Завод должен переслать заказчику 1100 деталей, которые
для пересылки упаковываются в ящики трёх типов. Один
ящик первого типа вмещает 70 деталей, второго типа – 40
деталей, третьего типа – 25 деталей. Стоимость пересылки одного ящика первого, второго и третьего типов
равна соответственно 20, 10 и 7 р.
Какие ящики должен использовать завод, чтобы стоимость пересылки бала наименьшей? Недогрузка ящиков не допускается.
Эта типичная задача линейного (точнее - целочисленного) программирования может быть успешно решена простым перебором вариантов.
Примеры задач целочисленного программирования:
•
•
•
•
задача о назначениях
задача коммивояжера
задача почтальона
задача о максимальном паросочетании
Обозначим через k1, k2 и k3 число ящиков каждого типа. Минимальное
число этих ящиков равно 0. Верхнюю границу легко определить, поделив
общее число деталей на вместимость каждого ящика:
const
DETALI = 1100;
380
K1 = DETALI div 70;
K2 = DETALI div 40;
K3 = DETALI div 25;
Для хранения текущего значения минимальной стоимости пересылки мы
заведём переменную minCost, которой сначала необходимо присвоить какое-либо большое значение:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// мин. стоимость пересылки:
var minCost := 1000000;
Во вложенных циклах foreach перебираем все варианты «расфасовки» деталей по ящикам, учитывая, что в сумме в них должно оказаться 1100 деталей:
foreach var k1 in Range(0, K1) do
foreach var k2 in Range(0, K2) do
foreach var k3 in Range(0, K3) do begin
if (k1 * 70 + k2 * 40 + k3 * 25 <> DETALI) then
continue;
Для каждой удачной комбинации ящиков мы находим стоимость пересылки и, если она окажется меньше текущего значения переменной
minCost, то печатаем текущую рекордную упаковку:
// стоимость пересылки:
var cost := k1 * 20 + k2 * 10 + k3 * 7;
if (minCost >= cost) then begin
minCost := cost;
WriteLn($' k1 = {k1} k2 = {k2} k3 = {k3}');
WriteLn($' Минимальная стоимость = {minCost}');
Writeln;
end;
381
end;
end;
begin
Writeln(' Ящики');
Writeln(' Нагибин, с.81, Задача 424');
Writeln;
Solve();
end.
Если разные расфасовки дадут одинаковую стоимость, мы напечатаем все,
чтобы узнать общее число решений задачи.
Поскольку результаты нашей упаковочной деятельности не ухудшаются по
ходу работы программы, то минимальная стоимость окажется в самом низу
списка:
Ящики
Нагибин, с.81, Задача 424
k1 = 0 k2 = 0 k3 = 44
Минимальная стоимость = 308
k1 = 0 k2 = 15 k3 = 20
Минимальная стоимость = 290
k1 = 0 k2 = 5 k3 = 36
Минимальная стоимость = 302
k1 = 0 k2 = 20 k3 = 12
Минимальная стоимость = 284
k1 = 0 k2 = 10 k3 = 28
Минимальная стоимость = 296
k1 = 0 k2 = 25 k3 = 4
Минимальная стоимость = 278
Итак, решение задачи единственное:
Детали нужно упаковать в 25 ящиков второго типа и в 4 ящика третьего типа. При этом минимальная стоимость пересылки составит 278
рублей.
382
Проект Путёвки
Исходный код программы находится в файле Путёвки.pas.
Задача 425 из книги Математическая шкатулка [Нагибин88], страница
81:
Предполагается использовать 2000 р. на путёвки в дома отдыха.
Путёвки имеются на 15, 27 и 45 дней, стоимость их соответственно 21, 40 и 60 р.
Сколько и каких путёвок нужно купить,
чтобы общее число дней отдыха было
наибольшим?
Эту задачу можно решить за пару минут, просто слегка изменив предыдущий проект:
const
SUMMA = 2000;
K1 = SUMMA div 21;
K2 = SUMMA div 40;
K3 = SUMMA div 60;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// макс. число дней:
var maxDays := 0;
foreach var k1 in Range(0, K1) do
foreach var k2 in Range(0, K2) do
foreach var k3 in Range(0, K3) do begin
if (k1 * 21 + k2 * 40 + k3 * 60 <> SUMMA) then
continue;
383
// число дней отдыха:
var days := k1 * 15 + k2 * 27 + k3 * 45;
if (maxDays <= days) then begin
//if (maxDays <= days) and (k1 * k2 * k3 <> 0) then begin
maxDays := days;
WriteLn($' k1 = {k1} k2 = {k2} k3 = {k3}');
WriteLn($' Максимальное число дней = {maxDays}');
Writeln;
end;
end;
end;
begin
Writeln(' Путёвки');
Writeln(' Нагибин, с.81, Задача 425');
Writeln;
Solve();
end.
Замечательно, что, решив одну задачу, потом можно использовать её код
как заготовку при решении множества других подобных задач!
Наша программа советует не покупать путёвки на 15 дней, а сэкономленные деньги потратить на приобретение двух 27- и тридцати двух 45-дневных путёвок. Они позволят трудящимся заслуженно отдыхать 1494 дня:
Путёвки
Нагибин, с.81, Задача 425
k1 = 0 k2 = 2 k3 = 32
Максимальное число дней = 1494
В книге приведён другой ответ. Вероятно, автор не учёл, что можно вообще
не покупать путёвки какого-либо вида. Чтобы проверить эту догадку, закомментированную строку замените другой:
//if (maxDays <= days) then begin
if (maxDays <= days) and (k1 * k2 * k3 <> 0) then begin
384
Теперь нулевые значения числа путёвок будут проигнорированы, и программа выдаст «книжные» результаты:
Путёвки
Нагибин, с.81, Задача 425
k1 = 20 k2 = 2 k3 = 25
Максимальное число дней = 1479
У автора задачи получилось на 15 дней меньше, чем у нас. Да, без компьютера толком не отдохнёшь!
Проект На базаре
Исходный код программы находится в файле На базаре.pas.
Задача 7 (2.3) из книги Удивительный мир чисел [КА86], страница 51:
На базаре за 9 кг орехов и 2 кг фейхоа заплатили столько же денег, сколько за 6 кг гранатов. А за 6 кг орехов, 5 кг фейхоа и 4 кг гранатов заплатили 43 р.
Сколько стоит 1 кг орехов, фейхоа и гранатов отдельно, если известно, что стоимость
каждого продукта выражается целым числом
рублей?
Эту базарную задачу легко решить по аналогии с упаковочной - достаточно
учесть её фруктовый контекст:
const
385
COST = 43;
OREHI = COST div 6;
FEIHOA = COST div 5;
GRANAT = COST div 4;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// макс. число дней:
var maxDays := 0;
foreach var orehi in Range(0, OREHI) do
foreach var feihoa in Range(0, FEIHOA) do
foreach var granat in Range(0, GRANAT) do begin
if (orehi * 6 + feihoa * 5 + granat * 4 <> COST) then
continue;
if (orehi * 9 + feihoa * 2 <> granat * 6) then
continue;
WriteLn($' Орехи = {orehi} Фейхоа = {feihoa} Гранаты =
{granat}');
Writeln;
end;
end;
begin
Writeln(' На базаре');
Writeln(' Кордемский, с.51, Задача 75');
Writeln;
Solve();
end.
Теперь вы можете смело отправляться на рынок 1986 года – вас никто не
обманет!
На базаре
Кордемский, с.51, Задача 75
Орехи = 2
Фейхоа = 3 Гранаты = 4
386
Проект Дедушка и внучка
Исходный код программы находится в файле Дедушка и внучка.pas.
Задача 10 (10.1) из книги Удивительный мир чисел
[КА86], страница 52:
Сколько дедушке лет, столько месяцев внучке. Дедушке с внучкой вместе 91 год.
Сколько лет дедушке и сколько внучке?
Самое сложное в этой задаче – составить простое уравнение:
x + 12 * x = 91 * 12
Это линейное уравнение, которое однозначно решается в единственном
цикле:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var x in Range(0, 91) do
if (x + 12 * x = 91 * 12) then begin
WriteLn($' Возраст дедушки в годах = {x}');
WriteLn($' Возраст внучки в годах = {91 - x}');
Writeln;
end;
end;
begin
Writeln(' Дедушка и внучка');
Writeln(' Кордемский, с.52, Задача 105');
Writeln;
Solve();
end.
387
Возраст дедушки получаем в чистом виде. Возраст внучки вычисляем по
условию задачи:
Дедушка и внучка
Кордемский, с.52, Задача 105
Возраст дедушки в годах = 84
Возраст внучки в годах = 7
Проект Сколько у мамы дочерей и сыновей?
Исходный код программы находится в файле Сколько у мамы дочерей
и сыновей.pas.
Задача 15 (4) из книги Удивительный мир чисел
[КА86], страница 55:
В семье 12 детей. Мама принесла для них 70
штук фейхоа. Половину всех фейхоа она раздала дочерям поровну, остальные отдала сыновьям, которые разделили их между собой также
поровну. Каждый мальчик получил на 2 фейхоа
больше, чем каждая девочка.
Сколько было у этой мамы дочерей и сыновей?
Оne day англичанин и шотландец нашли клад и решили его поделить.
- Давай поделим его по-честному, - предложил англичанин.
- Нет, давай лучше поровну! – возразил шотландец.
388
С точки зрения математики, мальчики и девочки ничем не отличаются от
кроликов и фазанов, поэтому эту задачу мы решаем так же, как и про животных на ферме:
const
// число
FEIJOA =
// число
CHILDREN
фейхоа:
70;
детей:
= 12;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var m in Range(1, CHILDREN) do
foreach var d in Range(1, CHILDREN) do begin
if (m + d <> CHILDREN) then
continue;
Единственная закавыка этой задачи - в написании вот этой проверки:
if (FEIJOA div 2 div m - FEIJOA div 2 div d <> 2) then
continue;
WriteLn($' Мальчиков: {m}');
WriteLn($' Девочек:
{d}');
Writeln;
end;
end;
begin
Writeln(' Сколько у мамы дочерей и сыновей');
Writeln(' Кордемский, с.55, Задача 15');
Writeln;
Solve();
end.
Впрочем, при внимательном чтении условия задачи легко сообразить, что
девочки и мальчики получили странных фруктов фейхоа поровну, то есть
по FEIJOA div 2 штук. Всё остальное – дело техники, то есть компьютера.
389
А вот и плоды раздела плодов и полов в семье плодовитой мамы:
Сколько у мамы дочерей и сыновей
Кордемский, с.55, Задача 15
Мальчиков: 5
Девочек:
7
Проект Счётные палочки
Исходный код программы находится в файле Счётные палочки.pas.
Задача 594 из книги Математическая шкатулка
[Нагибин88], страница 97:
Из 36 счётных палочек построили треугольники, квадраты и домики – всего 10 фигур.
Найдите число фигур каждого вида.
Ясно, что из 36 палочек можно построить от 0 до 36 div3 треугольников,
от 0 до 36 div 4 квадратов и от 0 до 36 div 6 домиков. Все варианты строительства мы, как обычно, перебираем во вложенных циклах foreach:
390
const
PALOCHKI = 36;
FIGUR = 10;
T = PALOCHKI div 3;
K = PALOCHKI div 4;
D = PALOCHKI div 6;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var t in Range(0, T) do
foreach var k in Range(0, K) do
foreach var d in Range(0, D) do begin
if (t + k + d <> FIGUR) then
continue;
if (t * 3 + k * 4 + d * 6 <> PALOCHKI) then
continue;
WriteLn($' Треугольники = {t} Квадраты = {k} Домики = {d}");
Writeln;
end;
end;
begin
Writeln(' Счётные палочки');
Writeln(' Нагибин, с.97, Задача 594');
Writeln;
Solve();
end.
Программа уверенно демонстрирует нам 3 решения задачи, хотя в книге
указано только второе.
Счётные палочки
Нагибин, с.97, Задача 594
Треугольники = 4
Треугольники = 6
Треугольники = 8
Квадраты = 6 Домики = 0
Квадраты = 3 Домики = 1
Квадраты = 0 Домики = 2
Мы опять можем предположить, что следовало построить все фигуры хотя
бы по одному разу, хотя в книге это требование отсутствует.
391
Проект Три сестры на рынке/Продажа кур
Исходный код программы находится в файле Три сестры на рынке
pas.
Это старинная задача из книги Перельмана Занимательная алгебра. Задача
известна также под названием Продажа кур:
Три сестры пришли на рынок с курами. Одна принесла для продажи 10 кур,
другая 16, третья 26. До полудня они продали часть своих кур по одной и
той же цене. После полудня, опасаясь, что не все куры будут проданы, они
понизили цену и распродали оставшихся кур снова по одинаковой цене. Домой все трое вернулись с одинаковой выручкой: каждая сестра получила от
продажи 35 рублей.
По какой цене продавали они кур до и после полудня?
Далеко не факт, что цена в рублях целая, поэтому переметнёмся к копейкам
– в надежде, что копейки не пилили и не ломали как-то иначе.
Цена до обеда была выше, но не больше 5 рублей за курицу. Цена после
обеда хотя бы на 1 копейку меньше:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var cenado in Range(100, 500) do
foreach var cenaposle in Range(99, cenado-1) do
Зная текущие цены на дообеденных и послеобеденных кур, мы можем –
опять же в циклах – подсчитать выручку каждой сестры. В копейках выручка должна составить в точности 35 рублей, то есть 3500 копеек.
В таких случаях мультяшный Удав говаривал так: А в попугаях я гораздо
длиннее!
392
foreach var prodanodo1 in Range(0, 10) do begin
if cenado * prodanodo1 + cenaposle * (10 - prodanodo1) <>
3500 then continue;
foreach var prodanodo2 in Range(0, 16) do begin
if cenado * prodanodo2 + cenaposle * (16 - prodanodo2)
<> 3500 then continue;
foreach var prodanodo3 in Range(0, 26) do
if (cenado * prodanodo3 + cenaposle * (26 prodanodo3) = 3500) then begin
Когда все продажные цены сойдутся с условиями задачи, мы смело напечатаем ответ на экране:
WriteLn($' Цена до обеда = {cenado}
Цена по-
сле обеда = {cenaposle}');
WriteLn($' {prodanodo1}+{10-prodanodo1}
{prodanodo2}+{16-prodanodo2} {prodanodo3}+{26-prodanodo3}');
Writeln;
end;
end;
end;
end;
begin
Writeln(' Три сестры на рынке');
Writeln;
Solve();
393
end.
Запускаем программу. И что же мы видим? – Цена на дообеденных кур была
3 рубля 75 копеек, а на послеобеденных – всего 1 рубль 25 копеек:
Три сестры на рынке
Цена до обеда = 375
9+1
6+10 1+25
Цена после обеда = 125
То есть:
• цена 1 курицы до полудня составляла 3 рубля 75 копеек
• после полудня – 1 рубль 25 копеек
• первая сестра продала до полудня 9 кур, а после полудня – 1 курицу
• вторая сестра продала до полудня 6 кур, а после полудня – 10
• третья сестра продала до полудня 1 курицу, а после полудня – 25
Мораль задачи такова: если хочешь быть богатым, не спи до обеда.
Давайте слегка измучаем себя школьной алгеброй!
Сложное решение вы найдёте в книге Перельмана, а мы попробуем решить
задачу по-другому.
Обозначим через x, y и z число кур, которые продали сёстры до полудня. Тогда после полудня они продали столько кур, соответственно:
10 - x
16 - y
26 - z
Обозначим буквами m и n цену 1 курицы до полудня и после.
Общая выручка каждой сестры составила 35 рублей:
mx + n(10 - x) = 35
my + n(16 - y) = 35
mz + n(26 - z) = 35
394
Запишем эти уравнения иначе:
(m - n)x + 10n = 35
(m - n)y + 16n = 35
(m - n)z + 26n = 35
Выразим x, y и z через m и n из предыдущих уравнений:
x =
y =
z =
𝟑𝟓 − 𝟏𝟎𝐧
𝒎−𝒏
𝟑𝟓 − 𝟏𝟔𝐧
𝒎−𝒏
𝟑𝟓 − 𝟐𝟔𝐧
𝒎−𝒏
Из условия задачи мы знаем, что x – это целое число из диапазона 0..10, y–
это целое число из диапазона 0..16, z– это целое число из диапазона 0..26.
Про m и n – цены на кур – мы не знаем, выражаются ли они целыми числами,
но здравый смысл нам подсказывает, что цены в копейках должны быть целыми. Денег, мельче 1 копейки нет, поэтому купить 1 курицу будет невозможно.
Заменим рубли копейками:
x =
y =
z =
𝟑𝟓𝟎𝟎 − 𝟏𝟎𝐧
𝒎−𝒏
𝟑𝟓𝟎𝟎 − 𝟏𝟔𝐧
𝒎−𝒏
𝟑𝟓𝟎𝟎 − 𝟐𝟔𝐧
𝒎−𝒏
В этих уравнениях все переменные – целые числа, поэтому их можно решить простым перебором всех возможных значений переменных m и n.
Дополуденная цена за курицу не могла превышать 3500 копеек, а после
обеда – и того меньше.
395
В двух вложенных циклах foreach перебираем значения переменных m и n
и проверяем, получают ли при этом переменные x, y и z целые значения.
Также мы должны учесть, что значения этих переменных должны попадать
в диапазоны возможных значений, о которых мы говорили выше:
uses MathExtensions;
procedure Solve2();
// курица до полудня стоила не дороже 35 рублей
// курица после полудня стоила дешеевле, чем до полудня
begin
foreach var m in Range(1, 3500) do
foreach var n in Range(1, m-1) do begin
var x := (3500 - 10*n) / (m - n);
if not x.IsInteger then continue;
if Integer(x) not in Range(0, 10) then continue;
var y := (3500 - 16*n) / (m - n);
if not y.IsInteger then continue;
if Integer(y) not in Range(0, 16) then continue;
var z := (3500 - 26*n) / (m - n);
if not z.IsInteger then continue;
if Integer(z) not in Range(0, 26) then continue;
// печатаем ответ:
WriteLn($' m = {m}
WriteLn($' x = {x}
Writeln;
n = {n}');
y = {y} z = {z}');
end;
end;
begin
Writeln(' Три сестры на рынке');
Writeln(' Перельман. Занимательная алгебра, стр. 112-114');
Writeln;
Solve();
Solve2();
end.
396
Наше решение получилось гораздо более коротким и простым, чем у Перельмана, а ответ мы получили быстрый и точный:
Три сестры на рынке
Перельман. Занимательная алгебра, стр. 112-114
Цена до обеда = 375
9+1
6+10 1+25
Цена после обеда = 125
m = 375 n = 125
x = 9 y = 6 z = 1
Проект Парикмахер
Исходный код программы находится в файле Парикмахер.pas.
Очередная старинная задача на диофантовы уравнения:
Парикмахер подстриг 12 человек за 12 руб.: женщин — за 2 руб., мужчин — за
50 коп., детей — за 25 коп. Сколько кого?
Решение задачи очень простое:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var женщин in Range(0, 12) do
foreach var мужчин in Range(0, 12 - женщин) do
foreach var детей in Range(0, 12 - женщин - мужчин) do
if женщин * 2 + мужчин / 2 + детей / 4 = 12 then
WriteLn($' Женщин = {женщин} Мужчин = {мужчин}
{детей}');
Writeln;
end;
Детей =
397
begin
Writeln(' Парикмахер');
Writeln;
Solve();
end.
Оказывается, у парикмахера был большой выбор:
Парикмахер
Женщин
Женщин
Женщин
Женщин
Женщин
Женщин
=
=
=
=
=
=
4
5
5
5
5
6
Мужчин
Мужчин
Мужчин
Мужчин
Мужчин
Мужчин
=
=
=
=
=
=
8
1
2
3
4
0
Детей
Детей
Детей
Детей
Детей
Детей
=
=
=
=
=
=
0
6
4
2
0
0
Проект Фломастеры и карандаши
Исходный код программы находится в файле Фломастеры и карандаши.pas.
Очень простая задача на диофантовы уравнения:
Куплены фломастеры по 7 руб. и карандаши по 4 руб. на сумму 53 руб.
Сколько чего?
Такие диофантовы уравнения имеют вид:
ax + by = c
Для решения подобных уравнений у нас есть модуль
DiophantineEquations.
398
В метод FindAllSolutions передаём a, b, c и диапазон значений для x и y. Значения для a, b, c мы получаем из уравнения, а диапазоны нужно выбрать
самостоятельно, исходя из условия задачи:
7x + 4y = 53
Легко видеть, что диапазоны 0..20 заведомо перекрывают все возможные
значения для переменных.
uses
System,
DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(7, 4, 53, 0,20, 0,20);
Метод FindAllSolutions возвращает последовательность кортежей (x, y),
поэтому распечатываем значения переменных в цикле foreach:
foreach var(x, y) in res do
WriteLn($' фломастеры = {x}, карандаши = {y}');
WriteLn;
end;
begin
WriteLn(' Фломастеры и карандаши');
WriteLn;
Solve();
end
А задача имеет 2 решения:
Фломастеры и карандаши
399
фломастеры = 3, карандаши = 8
фломастеры = 7, карандаши = 1
Проект Плитка на полу
Исходный код программы находится в файле Плитка на полу.pas.
Ещё одна очень простая задача на диофантовы уравнения:
Пол выложен квадратными плитками 2×2 и 3×3. Общая площадь — 47 кв. ед.
Сколько плиток каждого типа?
Из условия задачи вытекает уравнение:
4x + 9y= 47
Непринуждённо решаем его:
uses
System,
DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(4, 9, 47, 0,12, 0,12);
foreach var(x, y) in res do begin
WriteLn($' Плиток 2 х 2 = {x}');
WriteLn($' Плиток 3 х 3 = {y}');
end;
WriteLn;
end;
400
begin
WriteLn(' Плитка на полу');
WriteLn;
Solve();
end.
Плитки подсчитаны и аккуратно уложены:
Плитка на полу
Плиток 2 х 2 = 5
Плиток 3 х 3 = 3
Проект Мартышка и кокосовые орехи
Исходный код программы находится в файле Мартышка и кокосовые орехи.pas.
Эту кокосовую задачу я отыскал в книге Мартина Гарднера Математические головоломки и развлечения, в главе 24 под названием Мартышка и кокосовые орехи.
Читать Мартина Гарднера очень интересно, поэтому я привожу начало этой
истории целиком и полностью:
9 октября 1926 года в газете "Сатердей ивнинг пост" был напечатан небольшой рассказ Б. Э. Уильямса под названием "Кокосовые орехи". Сюжет
этого рассказа сводился к тому, что некий строительный подрядчик хотел
во что бы то ни стало помешать своему конкуренту получить важный заказ. Находчивый клерк подрядчика, зная страсть конкурента к занимательной математике, подсунул тому задачу настолько захватывающего содержания, что бедный конкурент, всецело поглощенный ее решением, забыл подать заявку в установленный срок и упустил контракт.
401
Вот эта задача в том виде, как ее сформулировал клерк из рассказа Уильямса.
Пять матросов и мартышка потерпели кораблекрушение и высадились на
необитаемом острове. Весь первый день они занимались сбором кокосовых
орехов. Вечером они сложили все орехи в кучу и легли спать.
Ночью, когда все заснули, один из матросов встал. Он подумал, что утром
при разделе орехов может вспыхнуть ссора, и решил взять свою долю орехов
немедля. Поэтому он разделил все кокосовые орехи на пять равных кучек, а
один оставшийся орех отдал мартышке. Затем он спрятал свою долю, а все
остальные орехи снова сложил в одну кучу.
Через некоторое время проснулся другой "робинзон" и сделал то же самое. У
него тоже остался один лишний орех, и он отдал его мартышке. И так один
за другим поступили все пятеро потерпевших кораблекрушение. Каждый из
них взял себе одну пятую орехов из той кучи, которую он нашел при пробуждении, и каждый отдал один орех мартышке. Утром они поделили оставшиеся орехи, и каждому досталось поровну - по одной пятой. Разумеется, каждый из матросов не мог не знать, что части орехов не хватает, но так как
у каждого из них совесть была одинаково нечиста, то никто ничего не сказал. Сколько кокосовых орехов было первоначально?
В рассказе Уильямса ответа не давалось. Говорят, что уже в течение первой
недели после опубликования рассказа редакция "Сатердей ивнинг пост" получила около 2000 писем. Джордж X. Лоример, занимавший в то время пост
главного редактора газеты, направил Уильямсу следующую историческую
телеграмму:
Ради бога, сообщите, сколько было орехов. В редакции творится черт знает
что.
В течение 20 лет Уильяме продолжал получать письма либо с просьбой сообщить ответ, либо с новыми решениями. В настоящее время задача о кокосовых орехах принадлежит к числу наиболее часто решаемых, но наименее
поддающихся решению диофантовых головоломок (термин "диофантово
уравнение" происходит от имени Диофанта Александрийского, греческого
математика, который впервые подробно исследовал уравнения, допускающие решения в рациональных числах).
402
Задачу о кокосовых орехах придумал не Уильяме. Он лишь видоизменил уже
известную до него задачу, чтобы сильнее запутать ее. Более старая версия
задачи почти полностью совпадает с приведенной в рассказе Уильямса.
Единственное различие заключается в том, что утром при окончательном
разделе орехов в старом варианте задачи один орех снова оказывается лишним и достается мартышке, в то время как в рассказе окончательный раздел производится точно, без остатка. Некоторые диофаитовы уравнения
имеют лишь одно решение (например, уравнение x2 + 2 = y3); другие допускают конечное число решений, третьи (например, уравнение x 3 + y3 = z3) не
имеют ни одного решения. Задача о кокосовых орехах и в изложении Уильямса, и в формулировке его предшественников допускает бесконечно много
решений в целых числах. Наша задача состоит в том, чтобы найти среди них
наименьшее положительное число.
Далее Мартин Гарднер приводит математические выкладки:
С помощью хорошо известных из алгебры приемов эти уравнения нетрудно
свести к одному диофантову уравнению с двумя неизвестными:
1024N = 15 625F + 11529.
Чтобы найти ответ, то есть наименьшее целое положительное число, удовлетворяющее данным задачи, нам остаётся только прибавить 15 625 к -4 и
получить искомое решение: 15 621.
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(1024, -15625, 11529, 0,20000, 0,20000)
.Sorted.First;
WriteLn($' Орехов было: {res.Item1}');
WriteLn;
end;
begin
Writeln(' Мартышка и кокосовые орехи');
403
Writeln(' Мартин Гарднер. Математические головоломки и развлечения,
глава 24');
Writeln;
Solve();
end.
Орехов было – о-го-го!
Мартышка и кокосовые орехи
Мартин Гарднер. Математические головоломки и развлечения, глава 24
Орехов было: 15621
404
Проект Мартышка и кокосовые орехи 2
Исходный код программы находится в файле Мартышка и кокосовые орехи 2.pas.
В конце главы Мартин Гарднер, сжалившись над убогими математиками,
предлагает решить более простую задачу:
Три моряка, бродя по острову, нашли кучу кокосовых орехов. Первый из них
взял себе половину всех орехов и еще пол-ореха, второй - половину того, что
осталось, и еще пол-ореха, и, наконец, третий также взял половину
остатка и еще пол-ореха. Остался ровно один орех, который они и отдали
мартышке. Сколько орехов было в куче, когда моряки набрели на нее?
Вооружившись памятью набекрень, мы должны вспомнить задачу Персидские яблоки. Слегка переделав яблочную программу, мы получим решение
для кокосовой:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
// считаем орехи:
var cocos := 1;
while True do begin
// остаток орехов:
var ost : real := cocos;
loop 3 do
ost := ost / 2 - 0.5;
// проверяем условие задачи:
if (ost = 1) then begin
Writeln($' Орехов было: {cocos}');
Writeln;
exit;
end;
cocos += 1;
end;
end;
begin
405
Writeln(' Мартышка и кокосовые орехи 2');
Writeln(' Мартин Гарднер. Математические головоломки и развлечения,
глава 24');
Writeln;
Solve();
end.
Запускаем программу и получаем ответ в полном согласии с маэстро:
Более простая задача о трех моряках, помещенная в конце главы, имеет ответ: 15 кокосовых орехов.
Мартышка и кокосовые орехи 2
Мартин Гарднер. Математические головоломки и развлечения, глава 24
Орехов было: 15
406
Проект Сказки Шехерезады
Исходный код программы находится в файле Сказки Шехерезады.pas.
Задача Сказки Шехерезады тоже очень проста для решения:
Шехерезада рассказывает 1001 сказку. В некоторые ночи — по 3 сказки, в
другие — по 5. Сколько ночей каждого типа?
Такие задачи мы умеем решать не просто, а запросто:
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(3, 5, 1001, 0,333, 0,200).First;
WriteLn($' По 3 сказки: {res.Item1}, По 5 сказок: {res.Item2}');
WriteLn;
end;
begin
WriteLn(' Сказки Шехерезады');
WriteLn;
Solve();
end.
Как известно, Шехерезада очень хитрая рассказчица, поэтому могла рассказывать свои сказки множеством разных способов. Но нам хватит и первого
сказочного расклада:
Сказки Шехерезады
По 3 сказки: 2, По 5 сказок: 199
407
Проект Пирожки
Исходный код программы находится в файле Пирожки.pas.
А теперь не сказка, а правдивая история про пирожки:
Три друга купили 13 пирожков. Алексей купил вдвое меньше Ивана, а Володя
— больше Алексея, но меньше Ивана. Сколько у каждого?
Внимательное (про)чтение условия задачи неизбежно приводит к такому
уравнению – с условием:
x + 2x + y = 13, где x < y < 2x
Делим пирожки по-честному, то есть по Диофанту:
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(1 + 2, 1, 13, 0,13, 0,13)
.Where(n -> (n.Item1 < n.Item2) and(n.Item2 < 2 * n.Item1))
.First;
WriteLn($' Алексей: {res.Item1} Иван: {res.Item1 * 2} Володя:
{res.Item2}');
WriteLn;
end;
begin
WriteLn(' Пирожки');
WriteLn;
Solve();
end.
408
Пирожки достались всем:
Пирожки
Алексей: 3
Иван: 6
Володя: 4
409
Проект Картины
Исходный код программы находится в файле Картины.pas.
Искусство счёта принадлежит математикам! Посему решаем такую задачу:
В каталоге 96 картин. На некоторых страницах по 4 картины, на других - по
6. Сколько страниц каждого типа?
Приходит на ум такое уравнение:
4x + 6y = 96
А для его решения и ума не нужно:
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(4, 6, 96, 0,24, 0,24)
.ToList;
foreach var (x, y) in res do
WriteLn($' По 4 картины: {x} По 6 картин: {y}');
WriteLn;
end;
begin
WriteLn(' Картины');
WriteLn;
Solve();
end.
Вариантов размещения картин по каталогу оказалось изрядно много:
410
Картины
По
По
По
По
По
По
По
По
По
4
4
4
4
4
4
4
4
4
картины:
картины:
картины:
картины:
картины:
картины:
картины:
картины:
картины:
0
3
6
9
12
15
18
21
24
По 6 картин: 16
По 6 картин: 14
По 6 картин: 12
По 6 картин: 10
По 6 картин: 8
По 6 картин: 6
По 6 картин: 4
По 6 картин: 2
По 6 картин: 0
Проект Осьминоги и морские звёзды
Исходный код программы находится в файле Осьминоги и морские
звёзды.pas.
Очередные задачи про ноги и головы:
У осьминогов 8 ног, у звёзд 5 лучей. Всего 39 «конечностей». Сколько осьминогов и звёзд?
Без труда не вынешь осьминога из пруда, поэтому спешно составляем уравнение по условию задачи:
8x + 5y = 39
И уже неспешно решаем задачу:
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(8, 5, 39, 0,5, 0,10)
411
.ToList;
foreach var (x, y) in res do
WriteLn($' Осьминогов: {x}
WriteLn;
end;
Морских звёзд: {y}');
begin
WriteLn(' Осьминоги и морские звёзды');
WriteLn;
Solve();
end.
Но решаем успешно:
Осьминоги и морские звёзды
Осьминогов: 3
Морских звёзд: 3
412
Проект Куры и кролики
Исходный код программы находится в файле Куры и кролики.pas.
«Наземная» задача про то же самое:
В клетке 20 лап. Сколько там кур и кроликов?
Уравняли условие задачи математически:
2x + 4y = 20
И сравняли по-программистски:
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(2, 4, 20, 0,10, 0,5)
.ToList;
foreach var (x, y) in res do
WriteLn($' Кур: {x} Кроликов: {y}');
WriteLn;
end;
begin
WriteLn(' Куры и кролики');
WriteLn;
Solve();
end.
Подсчёт поголовья выявил квантовую неопределённость:
413
Куры и кролики
Кур:
Кур:
Кур:
Кур:
Кур:
Кур:
0
2
4
6
8
10
Кроликов: 5
Кроликов: 4
Кроликов: 3
Кроликов: 2
Кроликов: 1
Кроликов: 0
414
Проект Пяти- и двухрублёвки
Исходный код программы находится в файле Пяти- и двухрублёвки.pas.
Считаем делёж денег:
Сколькими способами набрать 57 руб., используя только 2- и 5-рублёвые
монеты?
Сводим счёты и дебит с кредитом:
2x + 5y = 57
И вот всё, что нажито посильным трудом:
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(2, 5, 57, 0,30, 0,12)
.ToList;
foreach var (x, y) in res do
WriteLn($' Двухрублёвки: {x} Пятирублёвки: {y}');
WriteLn;
end;
Всего существует 6 способов:
Пяти- и двухрублёвки
Двухрублёвки:
Двухрублёвки:
Двухрублёвки:
Двухрублёвки:
1 Пятирублёвки: 11
6 Пятирублёвки: 9
11 Пятирублёвки: 7
16 Пятирублёвки: 5
415
Двухрублёвки: 21
Двухрублёвки: 26
Пятирублёвки: 3
Пятирублёвки: 1
Проект Покупка свитера
Исходный код программы находится в файле Покупка свитера.pas.
Подобная задача из книги Перельмана:
Вы должны заплатить за купленный в магазине свитер 19 руб. У вас одни
лишь трёхрублёвки, у кассира – только пятирублёвки. Можете ли вы при
наличии таких денег расплатиться с кассиром и как именно?
Пусть у меня сколько угодно трёхрублёвок, а у кассира сколько угодно пятирублёвок. Я могу заплатить ему 3х рублей, дав х трёхрублёвок. Так как 19
рублей нельзя заплатить трёхрублёвками, то кассир должен мне выдать
сдачу пятирублёвками. Пусть их будет y штук, тогда он вернёт мне 5y рублей.
Я должен дать кассиру на 19 рублей больше, чем он вернёт мне. Отсюда получается простое уравнение:
3x – 5y = 19
Понятно, что одно уравнение с двумя неизвестными имеет бесконечное
число решений.
Чтобы не истощать ресурсы компьютера и собственную нервную систему,
будем полагать, что у нас меньше 40 купюр (у каждого!).
Всё решение сводится к двум вложенным циклам и одной проверке условия задачи:
416
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var x in Range(0, 40) do
foreach var y in Range(0, 40) do
if 3*x - 5*y = 19 then
WriteLn($' Трёхрублёвки: {x}
WriteLn;
end;
Пятирублёвки: {y}');
procedure Solve2();
begin
var res := DiophantineSolver
.FindAllSolutions(2, 5, 57, 0,30, 0,12)
.ToList;
foreach var (x, y) in res do
WriteLn($' Трёхрублёвки: {x} Пятирублёвки: {y}');
WriteLn;
end;
begin
WriteLn(' Покупка свитера');
WriteLn(' Перельман. Занимательная алгебра, стр. 100-104');
WriteLn;
Solve();
end.
Запускаем программу и получаем список возможных вариантов:
Покупка свитера
Перельман. Занимательная алгебра, стр. 100-104
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
8 Пятирублёвки: 1
13 Пятирублёвки: 4
18 Пятирублёвки: 7
23 Пятирублёвки: 10
28 Пятирублёвки: 13
33 Пятирублёвки: 16
38 Пятирублёвки: 19
417
Здравый смысл нам подсказывает, что нужно остановиться на первом,
чтобы не шуршать деньгами впустую.
Значит, практичный ответ на задачу такой: мы выдаём кассиру 8 трёхрублёвок, что составляет 8 х 3 = 24 рубля, а он нам сдаёт 1 пятирублёвку:
24 – 5 = 19
Свитер куплен – да здравствует математика!
Если приглядеться к списку решений, то нетрудно установить общий вид
решений:
x = 8 + 5t
y = 1 + 3t
t≥0
Задавая различные значения переменной t, вы получите пары значений переменных x и y, которые служат решениями задачи.
Далее Перельман предлагает поменяться деньгами с кассиром и купить
тот же самый свитер, расплачиваясь пятирублёвками.
Чтобы решить новую задачу, нужно быстренько исправить условие в операторе if:
//if 3*x – 5*y = 19 then
if 5*x – 3*y = 19 then
Покупка свитера
Перельман. Занимательная алгебра, стр. 100-104
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
Трёхрублёвки:
5 Пятирублёвки: 2
8 Пятирублёвки: 7
11 Пятирублёвки: 12
14 Пятирублёвки: 17
17 Пятирублёвки: 22
20 Пятирублёвки: 27
23 Пятирублёвки: 32
418
Трёхрублёвки: 26
Пятирублёвки: 37
Общий вид решений в этом случае:
x = 5 + 3t
y = 2 + 5t
t≥0
И совсем просто задача решается с модулем DiophantineEquations:
uses DiophantineEquations;
procedure Solve2();
begin
var res := DiophantineSolver
.FindAllSolutions(3, -5, 19, 0,40, 0,40)
.ToList;
res.Sort;
foreach var (x, y) in res do
WriteLn($' Трёхрублёвки: {x} Пятирублёвки: {y}');
WriteLn;
end;
419
Проект Монеты на планете С
Исходный код программы находится в файле Монеты на планете
С.pas.
Отличная необычная задача про инопланетные деньги – тугрики:
На планете С в ходу монеты по 16 и 27 тугриков. Можно ли купить товар
за 1 тугрик?
Инопланетное уравнение само прёт в голову:
16x + 27y = 1
Если на этой планете тугрики не ломкие, а цельные, то иксы или игреки
должны быть отрицательными. Это обстоятельство мы учитываем в методе FindAllSolutions:
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(16, 27, 1, -100,100, -100,100)
.ToList;
foreach var (x, y) in res do
WriteLn($' Монет по 16 тугриков: {x} Монет по 27 тугриков: {y}');
WriteLn;
end;
begin
WriteLn(' Монеты на планете С');
WriteLn;
Solve();
end.
420
При большой наличности тугриков с обеих сторон (продавцы - покупатели),
можно по-разному распорядиться деньгами, чтобы предъявить налицо
свою наличность:
Монеты на планете С
Монет
Монет
Монет
Монет
Монет
Монет
Монет
по
по
по
по
по
по
по
16
16
16
16
16
16
16
тугриков:
тугриков:
тугриков:
тугриков:
тугриков:
тугриков:
тугриков:
-86
-59
-32
-5
22
49
76
Монет по 27 тугриков: 51
Монет по 27 тугриков: 35
Монет по 27 тугриков: 19
Монет по 27 тугриков: 3
Монет по 27 тугриков: -13
Монет по 27 тугриков: -29
Монет по 27 тугриков: -45
Самый скромный способ оплаты я выделил цветом.
421
Проект Дяди и тёти
Исходный код программы находится в файле Дяди и тёти.pas.
Криминальная задача от гимназии 1543:
На площади стоят дяди и тёти. У каждого дяди в кармане было 13 рублей, у
каждой тёти – 23 рубля. По площади прошёл вор и незаметно украл все
деньги.
Какое наибольшее число людей могло стоять на площади, если вор
украл всего 2011 рублей?
Эта история легко поддаётся расследованию с помощью модуля
DiophantineEquations, если правильно выразиться математически:
13x + 23y = 2011
Народ грамотно пересчитан:
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(13, 23, 2011, 0,1000, 0,1000)
.ToList;
var maxLudey := 0;
foreach var (x, y) in res do begin
WriteLn($' Дяди: {x} Тёти: {y}');
if x + y > maxLudey then
maxLudey := x + y;
end;
WriteLn($' Наибольшее число людей =: {maxLudey}');
WriteLn;
end;
422
begin
WriteLn(' Дяди и тёти');
WriteLn(' Гимназия 1543');
WriteLn;
Solve();
end.
Было обворовано 147 человек:
Дяди и тёти
Гимназия 1543
Дяди:
Дяди:
Дяди:
Дяди:
Дяди:
Дяди:
22
45
68
91
114
137
Тёти: 75
Тёти: 62
Тёти: 49
Тёти: 36
Тёти: 23
Тёти: 10
Наибольшее число людей =: 147
Странная сумма украденных денег заставляет нас предположить, что 2011
– это год выдумывания этой задачи. Сейчас 2026 год, поэтому людей чуток
прибавилось: воруют…
Дяди и тёти
Гимназия 1543
Дяди:
Дяди:
Дяди:
Дяди:
Дяди:
Дяди:
Дяди:
9 Тёти: 83
32 Тёти: 70
55 Тёти: 57
78 Тёти: 44
101 Тёти: 31
124 Тёти: 18
147 Тёти: 5
Наибольшее число людей =: 152
423
Проект Шлюбзики и шпегльморгеры
Исходный код программы находится в файле Шлюбзики и шпегльморгеры.pas.
Крайне необычная и весёлая задача от той же гимназии:
На складе 3000 шкафов. Розовый шлюбзик умеет носить 7 шкафов, а лысый
шпегльморгер -20 шкафов. Скольки способами можно послать отряд
шлюбзиков и шпегльморгеров, чтобы они за один раз вынесли весь
склад и каждый из них был загружен полностью?
Составляем уравнение для этих тёмных личностей со шкафами:
7x + 20y = 3000
Я не знаю, кто такие шлюбзики и шпегльморгеры, но науку не обманешь!
Всего существует 22 способа собрать отряд из этих шкафоносов.
uses DiophantineEquations;
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
var res := DiophantineSolver
.FindAllSolutions(7, 20, 3000, 0,10000, 0,10000)
.ToList;
foreach var (x, y) in res do
WriteLn($' Шлюбзики: {x} шпегльморгеры: {y}');
WriteLn($' Всего способов: {res.Count}');
WriteLn;
end;
begin
WriteLn(' Шлюбзики и шпегльморгеры');
WriteLn(' Гимназия 1543');
WriteLn;
424
Solve();
end.
Шлюбзики и шпегльморгеры
Гимназия 1543
Шлюбзики:
Шлюбзики:
Шлюбзики:
Шлюбзики:
Шлюбзики:
Шлюбзики:
Шлюбзики:
Шлюбзики:
Шлюбзики:
Шлюбзики:
0 шпегльморгеры: 150
20 шпегльморгеры: 143
40 шпегльморгеры: 136
60 шпегльморгеры: 129
80 шпегльморгеры: 122
100 шпегльморгеры: 115
120 шпегльморгеры: 108
140 шпегльморгеры: 101
160 шпегльморгеры: 94
180 шпегльморгеры: 87
Шлюбзики: 200 шпегльморгеры:
Шлюбзики: 220 шпегльморгеры:
Шлюбзики: 240 шпегльморгеры:
Шлюбзики: 260 шпегльморгеры:
Шлюбзики: 280 шпегльморгеры:
Шлюбзики: 300 шпегльморгеры:
Шлюбзики: 320 шпегльморгеры:
Шлюбзики: 340 шпегльморгеры:
Шлюбзики: 360 шпегльморгеры:
Шлюбзики: 380 шпегльморгеры:
Шлюбзики: 400 шпегльморгеры:
Шлюбзики: 420 шпегльморгеры:
Всего способов: 22
80
73
66
59
52
45
38
31
24
17
10
3
Искусственный интеллект тоже ничего не знает про шлюбзиков и шпегльморгеров, поэтому нарисовал пару шкафоголовых мужичков.
425
Проект Мыши
Исходный код программы находится в файле Мыши.pas.
Эту задачу придумал Акулич вместе с журналом Квант номер 4 за 1995 год,
страница 37.
Первые две строчки этой задачи – скороговорка и задача для детей. Продолжение скороговорки принадлежит Акуличу, который усугубил условие задачи:
Шли сорок мышей, несли сорок грошей,
Две мыши поплоше несли по два гроша,
Немало мышей – вообще без грошей.
Большие совсем тащили по семь.
А остальные несли по четыре.
Сколько мышей шли без грошей?
Обозначим больших мышей буквой x, остальных - буквой y, а без грошей буквой z.
Тогда мышеловка систематически должна быть такой:
x + y + z + 2 = 40
7x + 4y + 0z + 2 х 2 = 40
426
Перебираем мышей и гроши в двух циклах:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var x in Range(0, 40) do
foreach var y in Range(0, 40) do begin
var z := 40 - x - y - 2;
if (z >= 0) and (7*x + 4*y + 0*z + 2 * 2 = 40) then
WriteLn($' x = {x} y = {y}
z = {z}');
end;
WriteLn;
end;
begin
WriteLn(' Мыши');
WriteLn(' Квант номер 4 за 1995 год, страница 37');
WriteLn;
Solve();
end.
Оказалось, что пустопорожних мышек либо 29, либо 32:
Мыши
Квант номер 4 за 1995 год, страница 37
x = 0
x = 4
y = 9
y = 2
z = 29
z = 32
427
Проект Ревизия магазина
Исходный код программы находится в файле Ревизия магазина.pas.
Условие задачи:
При ревизии торговых книг магазина одна из записей оказалась залитой чернилами и имела такой вид:
Невозможно было разобрать число проданных метров, но было несомненно,
что число это не дробное; в вырученной сумме можно было различить
только последние три цифры, да установить ещё, что перед ними были три
какие-то другие цифры.
Может ли ревизионная комиссия по этим следам установить запись?
Так как первые 3 цифры в сумме залиты чернилами, то в копейках она составляет:
…728
1 метр ткани стоит 4936 копеек.
Обозначим через х число тысяч копеек, тогда вся сумма равна:
428
x*1000 + 728
Так как под кляксой скрываются 3 цифры, то х изменяется в диапазоне
100..999.
Мы знаем, что продано целое число метров ткани. Значит, сумма в копейках
x*1000 + 728 делится без остатка на цену 1 метра ткани в копейках 4936.
В цикле foreach мы перебираем все возможные значения переменной х и
делим сумму x*1000 + 728 на 4936. Когда сумма разделится на цену без
остатка, мы напечатаем ответ на задачу:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
// x - число копеек в тысячах
// x * 1000 + 728 - вырученная сумма
// (x * 1000 + 728) div 4936 - число метров проданной ткани
begin
foreach var x in Range(100, 1000) do
if (x * 1000 + 728) mod 4936 = 0 then
WriteLn($' Продано ткани: {(x * 1000 + 728) div 4936} метров.
Сумма = {(x * 1000 + 728)/100} рублей');
WriteLn;
end;
begin
WriteLn(' Ревизия магазина');
WriteLn(' Перельман. Занимательная алгебра, стр. 104-106');
WriteLn;
Solve();
end.
А ответ такой:
Ревизия магазина
Перельман. Занимательная алгебра, стр. 104-106
Продано ткани: 98 метров. Сумма = 4837.28 рублей
429
Наше решение оказалось гораздо короче, чем у Перельмана. И мы обошлись
только одной переменной х, а у Перельмана была ещё и вторая переменная
y.
Проект Покупка марок/Покупка почтовых марок
Исходный код программы находится в файле Покупка марок.pas.
Ещё одна денежная задача от Перельмана:
Требуется на один рубль купить 40 штук почтовых марок – копеечных, 4копеечных и 12-копеечных. Сколько окажется марок каждого достоинства?
Пусть копеечных марок куплено х штук, 4-копеечных y штук, а 12-копеечных z штук.
Всего куплено 40 марок:
x + y + z = 40
(1)
Из этого уравнения следует, что диапазон изменения всех переменных
0..40, а переменную z можно вычислить так:
z = 40 - x – y
(2)
Теперь вспомним, что у нас имеется всего 1 рубль и посчитаем расходы:
x + 4y + 12z = 100
(3)
Таким образом, нам нужно в двух вложенных циклах foreach перебрать все
возможные значения для переменных x и y. Значение переменной z легко
вычислить по формуле (2). При этом мы должны учесть, что z не может
быть отрицательным.
430
Если текущие значения переменных обращают уравнения (1) и (3) в верные
числовые равенства, то мы печатаем результаты:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var x in Range(0, 40) do
foreach var y in Range(0, 40) do begin
var z := 40 - x - y;
if (z >= 0) and (x + 4*y + 12*z = 100) and (x + y + z = 40) then
WriteLn($' x = {x} y = {y}
z = {z}');
end;
WriteLn;
end;
begin
WriteLn(' Покупка марок');
WriteLn(' Перельман. Занимательная алгебра, стр. 107-108');
WriteLn;
Solve();
end.
Марки можно купить двумя способами:
Покупка марок
Перельман. Занимательная алгебра, стр. 107-108
x = 20
x = 28
y = 20
y = 9
z = 0
z = 3
431
Проект Покупка фруктов
Исходный код программы находится в файле Покупка фруктов.pas.
А теперь – задача от Перельмана про фрукты!
На 5 руб. куплено 100 штук разных фруктов. Цены на фрукты таковы:
• Арбуз – 50 коп.
• Яблоко – 10 коп.
• Слива – 1 коп.
Сколько фруктов каждого вида было куплено?
Задача практически совпадает с предыдущей, поэтому решается так же.
Пусть куплено x арбузов, y яблок и z слив.
Всего – 100 штук:
x + y + z = 100
Слив куплено:
z = 100 - x – y
Потрачено 5 рублей:
50x + 10y + z = 500
Диапазон изменения переменных 0..100:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var x in Range(0, 100) do
foreach var y in Range(0, 100) do begin
var z := 100 - x - y;
432
if (z >= 0) and (x * 50 + y * 10 + z = 500) and (x + y + z = 100) then
WriteLn($' x = {x} y = {y}
z = {z}');
end;
WriteLn;
end;
begin
WriteLn(' Покупка фруктов');
WriteLn(' Перельман. Занимательная алгебра, стр. 108-109');
WriteLn;
Solve();
end.
Покупка фруктов
Перельман. Занимательная алгебра, стр. 108-109
x = 1
y = 39
z = 60
Как и Перельман, мы купили:
• 1 арбуз
• 39 яблок
• 60 слив
Проект Два числа и четыре действия
Исходный код программы находится в файле Два числа и четыре
действия.pas.
Задача Перельмана про числа:
Над двумя целыми положительными числами были выполнены следующие
четыре действия:
1) их сложили;
2) вычли из большего меньшее;
433
3) перемножили;
4) разделили большее на меньшее.
Полученные результаты сложили – составилось 243. Найти эти числа.
Пусть большее из двух чисел – х, а меньшее – y. Тогда условие задачи
можно записать так:
(x + y) + (x - y) + x*y + x/y = 243
Переменная х изменяется в диапазоне 2..243, а переменная y – в диапазоне
1..х-1.
В двух вложенных циклах foreach проверяем все возможные сочетания значений переменных. Когда условие выполнится мы напечатаем ответ:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var x in Range(2, 243) do
foreach var y in Range(1, x - 1) do
if (x + y) + (x - y) + x*y + x/y = 243 then
WriteLn($' x = {x} y = {y}');
WriteLn;
end;
begin
WriteLn(' Два числа и четыре действия');
WriteLn(' Перельман. Занимательная алгебра, стр. 114-115');
WriteLn;
Solve();
end.
Как и Перельман, мы нашли 2 решения задачи:
Два числа и четыре действия
Перельман. Занимательная алгебра, стр. 114-115
x = 24
y = 8
434
x = 54
y = 2
Проект Какой прямоугольник?
Исходный код программы находится в файле Какой прямоугольник.pas и Какой прямоугольник граф.pas.
Геометрическая задача от Перельмана:
Стороны прямоугольника выражаются целыми числами. Какой длины
должны они быть, чтобы периметр прямоугольника численно равнялся
его площади?
Если x и y – это длины сторон прямоугольника, то его периметр равен:
2x + 2y
А площадь:
хy
По условию задачи, мы должны найти прямоугольники, для которых выполняется условие:
2x + 2y = хy
Так как в задаче идёт речь обо всех целых числах, то мы не можем утверждать, что нашли все решения задачи. Предположим, что длины сторон не
превышают 1000 единиц, тогда задачу легко решить:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var x in Range(1, 1000) do
foreach var y in Range(x, 1000) do
if 2 * x + 2 * y = x * y then
WriteLn($' x = {x} y = {y}');
435
WriteLn;
end;
begin
WriteLn(' Какой прямоугольник?');
WriteLn(' Перельман. Занимательная алгебра, стр. 115-116');
WriteLn;
Solve();
end.
Мы быстро получим те же самые значения для переменных x и y, что и в
книге Перельмана:
Какой прямоугольник?
Перельман. Занимательная алгебра, стр. 115-116
x = 3
x = 4
y = 6
y = 4
Но наша программа не доказывает, что другие прямоугольники с такими
свойствами не существуют.
Давайте построим график функции f(x) = 2х / (x -2). Это две ветви гиперболы, которые асимптотически приближаются к прямым x = 2 и y = 2. Так
как нас интересуют только положительные значения переменных x и y, то
мы можем рассматривать только верхнюю правую ветвь гиперболы.
Все значения переменных x и y лежат на этой кривой. Но нас интересуют
только целые значения. Если вы внимательно посмотрите, какие узлы
сетки пересекает гипербола, то найдёте всего 2 точки.
Их координаты соответствуют найденным решениям. Больше нигде гипербола не проходит через узлы сетки. Это значит, что других решений не существует.
436
437
Проект Два двузначных числа
Исходный код программы находится в файле Два двузначных
числа.pas.
Задача Перельмана про числа:
Числа 46 и 96 обладают любопытной особенностью: их произведение не меняет своей величины, если переставить их цифры.
Действительно,
46 х 96 = 4416 = 64 х 69
Требуется установить, существуют ли ещё другие пары чисел с тем же
свойством. Как разыскать их все?
Так как двухзначных чисел очень мало, то все возможные пары можно просто перебрать во вложенных циклах foreach. Чтобы напечатать числа в алфавитном порядке, будем считать, что первое число меньше второго.
При переборе мы также должны отбросить числа, которые состоят из двух
одинаковых цифр, поскольку они не изменяются при их перестановке. Такие числа делятся на 11 без остатка, поэтому мы легко избавимся от них:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
// x - меньшее из двух чисел
// y - большее
begin
// всего решений:
var vsego := 0;
// список произведений:
var res := new List<integer>();
foreach var x in Range(12, 98) do begin
// числа с разными цифрами:
if x mod 11 = 0 then continue;
foreach var y in Range(x+1, 99) do begin
if y mod 11 = 0 then continue;
438
Чтобы не повторялись произведения, мы проверяем, не встречалось ли произведение текущих чисел раньше:
// произведение двух прямых чисел:
var mul := x * y;
// произведения не должны повторяться:
if mul in res then continue;
Для их хранения мы завели список res, который пока пуст.
Переставить цифры двузначного числа можно так.
Выделяем вторую цифру:
x mod 10
Она должна стать первой:
x mod 10 * 10
Находим первую цифру:
x div 10
Её нужно добавить к двузначному числу, образованному второй цифрой:
// обращённые числа:
var x1 := x mod 10 * 10 + x div 10;
var y1 := y mod 10 * 10 + y div 10;
// обращённые числа не должны совпадать с прямыми:
if (x1 = y) or (y1 = x) then continue;
Здесь мы опять проверяем числа, чтобы не было повторов.
Произведения прямых и обратных чисел должны совпадать:
// произведение обращённых чисел:
var mul1 := x1 * y1;
// если произведния равны,
439
// печатаем решение:
if mul = mul1 then begin
vsego += 1;
res.Add(mul);
WriteLn($' {x} * {y} = {x * y}
end;
end;
end;
WriteLn($' Всего: {vsego}');
WriteLn;
end;
{x1} * {y1} = {x * y}');
begin
WriteLn(' Два двузначных числа');
WriteLn(' Перельман. Занимательная алгебра, стр. 115-116');
WriteLn;
Solve();
end.
Обратите внимание, что мы добавляем произведение в список res, чтобы
наборы чисел не повторялись.
Печатаем найденные произведения:
Два двузначных числа
Перельман. Занимательная алгебра, стр. 115-116
12
12
12
13
13
14
23
23
24
24
26
34
36
46
*
*
*
*
*
*
*
*
*
*
*
*
*
*
42
63
84
62
93
82
64
96
63
84
93
86
84
96
=
=
=
=
=
=
=
=
=
=
=
=
=
=
504
756
1008
806
1209
1148
1472
2208
1512
2016
2418
2924
3024
4416
21 * 24 = 504
21 * 36 = 756
21 * 48 = 1008
31 * 26 = 806
31 * 39 = 1209
41 * 28 = 1148
32 * 46 = 1472
32 * 69 = 2208
42 * 36 = 1512
42 * 48 = 2016
62 * 39 = 2418
43 * 68 = 2924
63 * 48 = 3024
64 * 69 = 4416
440
Всего: 14
Всего таких произведений 14, как и в книге Перельмана.
Проект Пифагоровы тройки чисел/Пифагоровы числа
Исходный код программы находится в файле Пифагоровы тройки
чисел.pas.
Это по-настоящему интересная задача из книги Перельмана Занимательная алгебра.
Соотношение между сторонами прямоугольного треугольника описывается теоремой Пифагора.
Если a и b – длина катетов, а с – длина гипотенузы, то можно записать такое
равенство:
a2 + b2 = c2
Если все 3 числа a, b и с – натуральные числа, то они называются пифагоровыми, или пифагоровыми тройками.
Самая первая тройка пифагоровых чисел известна всем:
32 + 42 = 52
Эти числа использовались с древних времён при строительстве. Если на верёвке завязать 12 узлов, а концы верёвки связать вместе, то получится
удобный инструмент для отмеривания прямых углов на местности.
Если натянуть верёвку на колышки так, чтобы длины сторон были равны 3,
4 и 5 «междоузлий», то получится прямоугольный треугольник. Этот способ описан в книге Перельмана Занимательная алгебра и проиллюстрирован на обложке:
441
Пифагоровых троек существует бесконечное множество. О некоторых способах их отыскания вы можете прочитать в книге Перельмана, но их легко
найти и программным способом, которым мы сейчас и займёмся.
Так как нам нужны квадраты чисел, то вполне разумно создать массив квадратов чисел squares в заданном диапазоне:
// РЕШАЕМ ЗАДАЧУ
procedure Solve(maxHypo : integer);
// maxHypo - наибольшая длина гипотенузы
begin
// создаём список квадратов чисел:
var squares := ArrGen(maxHypo, n -> (n+1)*(n+1)).Println;
WriteLn;
end;
begin
WriteLn(' Пифагоровы тройки чисел');
442
WriteLn(' Перельман. Занимательная алгебра, стр. 117-120');
WriteLn;
Solve(31);
end.
Если вы распечатаете квадраты чисел до 1000, то получите вот такой список:
Пифагоровы тройки чисел
Перельман. Занимательная алгебра, стр. 117-120
1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400 441
484 529 576 625 676 729 784 841 900 961
Понятно, что квадраты катетов и гипотенузы находятся в этом списке. Мы
можем составлять все возможные суммы двух квадратов катетов, извлекая
их из списка. Если их сумма также имеется в списке, значит, квадрат
гипотенузы также выражается целым числом, и мы нашли пифагорову
тройку.
Чтобы не повторять наборы чисел, будем считать, что второй катет
длиннее первого.
В двух вложенных циклах for мы перебираем квадраты в списке и проверяем их сумму:
for var i:= 0 to squares.Count-2 do begin
var a2 := squares.ElementAt(i);
for var j:= i+1 to squares.Count-1 do begin
var b2 := squares.ElementAt(j);
// сумма квадратов:
var c2 := a2 + b2;
if c2 in squares then
println($' a2 = {a2} b2 = {b2} c2 = {c2}')
end;
end;
WriteLn;
end;
443
Самое сложное в этой программе – красиво напечатать решения.
Если длина гипотенузы не превышает 30, то вы получите такие пифагоровы тройки:
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
=
=
=
=
=
=
=
=
=
=
=
9 b2 = 16 c2 = 25
25 b2 = 144 c2 = 169
36 b2 = 64 c2 = 100
49 b2 = 576 c2 = 625
64 b2 = 225 c2 = 289
81 b2 = 144 c2 = 225
100 b2 = 576 c2 = 676
144 b2 = 256 c2 = 400
225 b2 = 400 c2 = 625
324 b2 = 576 c2 = 900
400 b2 = 441 c2 = 841
Но вы можете найти и гораздо большие тройки чисел, если вам интересно:
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
a2
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
1024
1089
1089
1225
1296
1296
1521
1521
1600
1600
1764
2025
2304
2304
2601
2916
3249
b2
b2
b2
b2
b2
b2
b2
b2
b2
b2
b2
b2
b2
b2
b2
b2
b2
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
3600
1936
3136
7056
2304
5929
2704
6400
1764
5625
3136
3600
3025
4096
4624
5184
5776
c2
c2
c2
c2
c2
c2
c2
c2
c2
c2
c2
c2
c2
c2
c2
c2
c2
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
4624
3025
4225
8281
3600
7225
4225
7921
3364
7225
4900
5625
5329
6400
7225
8100
9025
Цитата из Перельмана:
444
Пифагоровы числа обладают вообще рядом любопытных особенностей, кот
орые мы перечисляем далее без доказательств:
Один из "катетов" должен быть кратным трем.
Один из "катетов" должен быть кратным четырем.
Одно из пифагоровых чисел должно быть кратно пяти.
Великая теорема Ферма
Троек пифагоров чисел, то есть решений уравнения
a2 + b2 = c2
в натуральных числах существует бесконечно много. Тем более удивительно, что уравнения
an + bn = cn
для более высоких степеней вообще не имеют ни одного решения!
Впервые эту догадку высказал французский математик Пьер Ферма в 1637
году. По преданию, он записал на полях книги Диофанта, что нашёл «поистине удивительное доказательство этого предложения, но здесь мало места, чтобы его привести». Но доказательство так и не было обнаружено, поэтому учёные считают, что Ферма просто ошибся. Его предположение получило название Великая, или Последняя теорема Ферма.
Её более 300 лет пытались доказать многие выдающиеся учёные. Им удалось подтвердить предположение Ферма для многих натуральных степеней. Вы можете прочитать об этом в книге Перельмана. Правда, на момент
написания книги теорема ещё не была доказана, поэтому мы должны внести поправку в текст книги: теорема Ферма была полностью доказана в
1994 году английским математиком Эндрю Уайлсом.
Интересная цитата из книги Перельмана:
Последователям Ферма пришлось идти самостоятельным путем.
445
Вот результаты этих усилий: Эйлер (1797) доказал теорему Ферма для третьей и четвертой степеней; для пятой степени ее доказал Лежандр (1823),
для седьмой* - Ламе и Лебег (1840). В 1849 г. Куммер доказал теорему для
обширной группы степеней и, между прочим, - для всех показателей, меньших
ста. Эти последние работы далеко выходят за пределы той области математики, какая знакома была Ферма, и становится загадочным, как мог последний разыскать общее доказательство своего "великого предложения".
Впрочем, возможно, он ошибался.
* (Для составных показателей (кроме 4) особого доказательства не требуется: эти случаи сводятся к случаям с простыми показателями.)
Интересующимся историей и современным состоянием задачи Ферма
можно рекомендовать брошюру А. Я. Хинчина "Великая теорема Ферма".
Написанная специалистом, брошюра эта предполагает у читателя лишь
элементарные знания из математики.
Проект Сумма кубов
Исходный код программы находится в файле Сумма кубов.pas.
Как следует из теоремы Ферма, уравнение
a3 + b3 = c3
не имеет решений для натуральных чисел. Но вот если добавить третий куб,
то получится уравнение:
a3 + b3 + c3 = d3
Оно очень похоже на «пифагоровское», поэтому его можно решать тем же
самым способом, в трёх вложенных циклах:
uses MathExtensions;
446
// РЕШАЕМ ЗАДАЧУ
procedure Solve(maxCube : integer);
// maxHypo - наибольшая длина гипотенузы
begin
// создаём список кубов чисел:
var cubes := ArrGen(maxCube, n -> (n+1).IPower(3)).Println;
Println;
for var i:= 0 to cubes.Count-3 do begin
var a3 := cubes.ElementAt(i);
for var j:= i+1 to cubes.Count-2 do begin
var b3 := cubes.ElementAt(j);
for var k:= j+1 to cubes.Count-1 do begin
var c3 := cubes.ElementAt(k);
// сумма кубов:
var d3 := a3 + b3 + c3;
if d3 in cubes then
println($' a3 = {a3} b3 = {b3} c3 = {c3}
end;
end;
end;
WriteLn;
end;
d3 = {d3}')
begin
WriteLn(' Сумма кубов');
WriteLn(' Перельман. Занимательная алгебра, стр. 120-124');
WriteLn;
Solve(31);
end.
Для небольших чисел наша программа нашла такие интересные решения:
Сумма кубов
Перельман. Занимательная алгебра, стр. 120-124
1 8 27 64 125 216 343 512 729 1000 1331 1728 2197 2744 3375 4096 4913
5832 6859 8000 9261 10648 12167 13824 15625 17576 19683 21952 24389 27000
29791
447
a3
a3
a3
a3
a3
a3
a3
a3
a3
a3
a3
a3
a3
=
=
=
=
=
=
=
=
=
=
=
=
=
1 b3 = 216 c3 = 512 d3 = 729
8 b3 = 1728 c3 = 4096 d3 = 5832
27 b3 = 64 c3 = 125 d3 = 216
27 b3 = 1000 c3 = 5832 d3 = 6859
27 b3 = 5832 c3 = 13824 d3 = 19683
64 b3 = 4913 c3 = 10648 d3 = 15625
216 b3 = 512 c3 = 1000 d3 = 1728
343 b3 = 2744 c3 = 4913 d3 = 8000
729 b3 = 1728 c3 = 3375 d3 = 5832
1331 b3 = 3375 c3 = 19683 d3 = 24389
1728 b3 = 4096 c3 = 8000 d3 = 13824
3375 b3 = 8000 c3 = 15625 d3 = 27000
5832 b3 = 6859 c3 = 9261 d3 = 21952
Многие натуральные числа можно представит в виде суммы трёх кубов:
3 as the sum of the 3 cubes – Numberphile
https://yandex.ru/video/preview/15749174229937532927
Как вы видите, можно использовать кубы отрицательных чисел.
Некоторые числа не удалось представить в виде трёх кубов. Например,
числа вида 9k + 4 и 9k + 5 не поддаются кубиковому разложению.
448
Вот числа из первой сотни, которые рьяно упёрлись раскубикованию.
Однако для трёх «возможных» чисел из первой сотни долго не могли найти
решение:
449
Первым «раскололось» число 74:
Затем дало слабину число 33.
450
Последним сдалось число 42.
451
Неуёмные математики полагают, что «возможные» числа можно разложить
на кубы бесконечным числом способов, но это утверждение не доказано.
За подробностями обращайтесь к ролику на Ютубе.
Проект Неопределенное уравнение третьей степени
Исходный код программы находится в файле Неопределенное уравнение третьей степени.pas.
Продолжение кубической истории из книги Перельмана Занимательная
алгебра:
Сумма кубов трех целых чисел может быть кубом четвертого числа. Например, 33 + 43 + 53 = 63.
Это означает, между прочим, что куб, ребро которого равно 6 см, равновелик сумме трех кубов, ребра которых равны 3 см, 4 см и 5 см (рис. 14), - соотношение, по преданию, весьма занимавшее Платона.
Рис. 14. Куб, ребро которого равно 6 см, равновелик сумме трех кубов, ребра
которых равны 3 см, 4 см и 5 см
Ничтоже сумняшеся в математических выкладках Перельмана, мы доверимся следующим уравнениям:
x =
28r2 + 11rs - 3s2
452
у = 21r2 - 11rs - 4s2
z = 35r2 + 7rs + 6s2
t = -42r2 - 7rs - 5s2
Они приведут нас к такому решению этой задачи:
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
foreach var r in (0..2) do
foreach var s in (-2..5) do begin
var x := 28*r*r + 11*r*s - 3*s*s;
var y := 21*r*r - 11*r*s - 4*s*s;
var z := 35*r*r + 7*r*s + 6*s*s;
var t := -42*r*r - 7*r*s - 5*s*s;
println($' r = {r} s = {s} -> x = {x}
end;
WriteLn;
end;
y = {y}
z = {z}
t = {t}');
begin
WriteLn(' Неопределенное уравнение третьей степени');
WriteLn(' Перельман. Занимательная алгебра');
WriteLn;
Solve();
end.
Совершенно очевидно, что даже при небольших значениях переменных r и
s мы получим немало решений:
Неопределенное уравнение третьей степени
Перельман. Занимательная алгебра
r
r
r
r
r
r
r
r
r
=
=
=
=
=
=
=
=
=
0
0
0
0
0
0
0
0
1
s
s
s
s
s
s
s
s
s
=
=
=
=
=
=
=
=
=
-2 ->
-1 ->
0 ->
1 ->
2 ->
3 ->
4 ->
5 ->
-2 ->
x = -12 y = -16 z = 24 t = -20
x = -3 y = -4 z = 6 t = -5
x = 0 y = 0 z = 0 t = 0
x = -3 y = -4 z = 6 t = -5
x = -12 y = -16 z = 24 t = -20
x = -27 y = -36 z = 54 t = -45
x = -48 y = -64 z = 96 t = -80
x = -75 y = -100 z = 150 t = -125
x = -6 y = 27 z = 45 t = -48
453
r
r
r
r
r
r
r
r
r
r
r
r
r
r
r
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
1
1
1
1
1
1
1
2
2
2
2
2
2
2
2
s
s
s
s
s
s
s
s
s
s
s
s
s
s
s
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
-1 ->
0 ->
1 ->
2 ->
3 ->
4 ->
5 ->
-2 ->
-1 ->
0 ->
1 ->
2 ->
3 ->
4 ->
5 ->
x = 14 y = 28 z = 34 t = -40
x = 28 y = 21 z = 35 t = -42
x = 36 y = 6 z = 48 t = -54
x = 38 y = -17 z = 73 t = -76
x = 34 y = -48 z = 110 t = -108
x = 24 y = -87 z = 159 t = -150
x = 8 y = -134 z = 220 t = -202
x = 56 y = 112 z = 136 t = -160
x = 87 y = 102 z = 132 t = -159
x = 112 y = 84 z = 140 t = -168
x = 131 y = 58 z = 160 t = -187
x = 144 y = 24 z = 192 t = -216
x = 151 y = -18 z = 236 t = -255
x = 152 y = -68 z = 292 t = -304
x = 147 y = -126 z = 360 t = -363
Как справедливо отмечает Перельман, при r = 1, s = 1 мы получим уравнение:
363 + 63 + 483 = 543
После дележа чисел сокращением на 6 это уравнение станет краше:
63 + 13 + 83 = 93
Таким образом, исходное уравнение
33 + 43 + 53 = 63
порождает бесконечное число новых решений неопределённых уравнений.
Следуем дальше по пятам за Перельманом:
Заметим, что если в исходной четверке, 3, 4, 5, -6 или в одной из вновь полученных четверок поменять числа местами и применить тот же прием, то
получим новую серию решений. Например, взяв четверку 3, 5, 4, -6 (т. е. положив a = 3, b = 5, с = 4, d = -6), мы получим для х, y, z, t значения:
х
y
z
t
= 20r2
= 12r2
= 16r2
= -24r2
+ 10rs - 3s2,
- 10rs - 5s2,
+ 8rs + 6s2,
- 8rs - 4s2.
454
Теперь вы можете плодить плодородно плоды просвещения и сколько
угодно кубических уравнений:
procedure Solve2();
begin
foreach var r in (0..2) do
foreach var s in (-2..5) do begin
var x := 20*r*r + 10*r*s - 3*s*s;
var y := 12*r*r - 10*r*s - 5*s*s;
var z := 16*r*r + 8*r*s + 6*s*s;
var t := -24*r*r - 8*r*s - 4*s*s;
println($' r = {r} s = {s} -> x = {x}
end;
WriteLn;
end;
y = {y}
z = {z}
t = {t}');
begin
WriteLn(' Неопределенное уравнение третьей степени');
WriteLn(' Перельман. Занимательная алгебра');
WriteLn;
//Solve();
Solve2();
end.
Неопределенное уравнение третьей степени
Перельман. Занимательная алгебра
r
r
r
r
r
r
r
r
r
r
r
r
=
=
=
=
=
=
=
=
=
=
=
=
0
0
0
0
0
0
0
0
1
1
1
1
s
s
s
s
s
s
s
s
s
s
s
s
=
=
=
=
=
=
=
=
=
=
=
=
-2 ->
-1 ->
0 ->
1 ->
2 ->
3 ->
4 ->
5 ->
-2 ->
-1 ->
0 ->
1 ->
x = -12 y = -20 z = 24 t = -16
x = -3 y = -5 z = 6 t = -4
x = 0 y = 0 z = 0 t = 0
x = -3 y = -5 z = 6 t = -4
x = -12 y = -20 z = 24 t = -16
x = -27 y = -45 z = 54 t = -36
x = -48 y = -80 z = 96 t = -64
x = -75 y = -125 z = 150 t = -100
x = -12 y = 12 z = 24 t = -24
x = 7 y = 17 z = 14 t = -20
x = 20 y = 12 z = 16 t = -24
x = 27 y = -3 z = 30 t = -36
455
r
r
r
r
r
r
r
r
r
r
r
r
=
=
=
=
=
=
=
=
=
=
=
=
1
1
1
1
2
2
2
2
2
2
2
2
s
s
s
s
s
s
s
s
s
s
s
s
=
=
=
=
=
=
=
=
=
=
=
=
2 ->
3 ->
4 ->
5 ->
-2 ->
-1 ->
0 ->
1 ->
2 ->
3 ->
4 ->
5 ->
x
x
x
x
=
=
=
=
28
23
12
-5
x = 28
x = 57
x = 80
x = 97
x = 108
x = 113
x = 112
x = 105
y = -28 z = 56 t = -56
y = -63 z = 94 t = -84
y = -108 z = 144 t = -120
y = -163 z = 206 t = -164
y = 68 z = 56 t = -80
y = 63 z = 54 t = -84
y = 48 z = 64 t = -96
y = 23 z = 86 t = -116
y = -12 z = 120 t = -144
y = -57 z = 166 t = -180
y = -112 z = 224 t = -224
y = -177 z = 294 t = -276
Проект Bogenschießen
Исходный код программы находится в файле Bogenschießen.pas.
В
задаче
из
немецкой
книги
Gehirnjogging речь идёт о стрельбе из
лука по мишени, но мы легко найдём в
ней диофантово уравнение.
Задача 26. Bogenschießen. Вот её условие на немецком языке:
Lilly hat im Garten eine Zielscheibe aufgebaut.
Welche Felder muss sie treffen, um genau auf 100 Punkte zu kommen?
(Sie darf beliebig viele Pfeile abschießen.)
Стрельба из лука
У Лили в саду висит мишень:
456
Как Лиля может выбить ровно 100 очков?
(Количество стрел может быть любым.)
PascalABC.NET не переносит немецкую букву ß (эсцет), поэтому в проекте её пришлось заменить двумя буквами ss.
Точно так же заменяют эту букву в кроссвордах и при печати немецкого текста только латинскими буквами.
На оригинальной мишени число 24 встречается дважды,
что уже совсем нехорошо. Пришлось заменить его числом
23.
Задача решается простым перебором во вложенных циклах for:
const
SCORE
C40 =
C39 =
C24 =
C23 =
C17 =
C16 =
= 100;
SCORE div
SCORE div
SCORE div
SCORE div
SCORE div
SCORE div
40;
39;
24;
23;
17;
16;
457
// РЕШАЕМ ЗАДАЧУ
procedure Solve();
begin
for var k40 := 0 to C40 do
for var k39 := 0 to C39 do
for var k24 := 0 to C24 do
for var k23 := 0 to C23 do
for var k17 := 0 to C17 do
for var k16 := 0 to C16 do begin
if (k40 * 40 + k39 * 39 +
k24 * 24 + k23 * 23 +
k17 * 17 + k16 * 16 = SCORE) then begin
if (k40 > 0) then
Writeln(' k40 = ' + k40);
if (k39 > 0) then
Writeln(' k39 = ' + k39);
if (k24 > 0) then
Writeln(' k24 = ' + k24);
if (k23 > 0) then
Writeln(' k23 = ' + k23);
if (k17 > 0) then
Writeln(' k17 = ' + k17);
if (k16 > 0) then
Writeln(' k16 = ' + k16);
Writeln ();
end
end;
Writeln;
end;
begin
Writeln(' Bogenschie?en');
Writeln(' Gehirnjogging, Задача 26');
Writeln;
Solve();
end.
Bogenschießen
Gehirnjogging, Задача 26
458
k17 = 4
k16 = 2
Если наверняка знать, что ответ единственный, то задачу можно решить и
в уме.
Задания для самостоятельного решения
Задача 11, страница 52
Удивительный мир чисел
Овчарка погналась за лисой, когда между ними было расстояние 99 м.
Скачок лисы 1,1 м, скачок овчарки 2,2 м. Когда овчарка делает 19 скачков, лиса делает 29 скачков.
Сколько метров проскачут они, пока овчарка догонит лису?
Кузнечик
Гимназия 1543
Кузнечик прыгает по числовой прямой. Сначала он делает один или несколько прыжков длины 3 в одну сторону, а затем один или несколько
прыжков длины 5 в другую.
Как ему попасть из точки 0 в точку 7? Найдите все варианты.
Икс и игрек
Гимназия 1543
Найдите все целые x, y, удовлетворяющие равенству 81x + 23y = 3.
459
Углы
Гимназия 1543
Даны углы в 32° и 25°. Как построить угол в 1°?
Точки
Гимназия 1543
Сколько точек с целыми координатами расположено на прямой y =
8*x/13 + 6/13 при -100 ≤ x ≤50?
Более сложные уравнения
Гимназия 1543
Решите в целых числах уравнения:
2x + 3y + 5z = 13
xy + 3x – 5y + 3 = 0
2x2 + 5xy + 3y2 = 21
x + y = x2 – xy + y2
Решите в натуральных числах уравнение:
2x + 7 = y 2
460
Литература
[Нагибин88]
Нагибин Ф.Ф., Канин Е.С.
Математическая шкатулка
М.: Просвещение, 1988. – 160 с.
[ОО80]
Оре О.
Приглашение в теорию чисел
М.: Наука, 1980 г. - 128 с.
Библиотечка Квант, Выпуск 3
461
[КА86] [КА96]
Б.А. Кордемский, А.А.Ахадов
Удивительный мир чисел
(МАТЕМАТИЧЕСКИЕ ГОЛОВОЛОМКИ
И ЗАДАЧИ ДЛЯ ЛЮБОЗНАТЕЛЬНЫХ)
М.: Просвещение, 1986. – 144 с.
М.: Просвещение, 1996. – 159 с
462
[ЗП88]
Абрамов С.А. и др.
Задачи по программированию
Наука, 1988. – 224 с.
ISBN: 5-02-013774-Х
Серия: Библиотечка программиста
[100]
В. А. Дагене, Г. К. Григас, К. Ф. Аугутис
100 задач по программированию
М.:Просвещение, 1993. – 251 с.
ISBN: 5-09-003864-3
463
[ВНН88]
Воробьёв Н.Н.
Признаки делимости
Наука. - 1988, 96 с.
ISBN 5-02-013731-6
[БК85]
Брудно А. Л. Каплан Л. И.
Олимпиады по программированию для
школьников
Наука. - 1985, 96 с.
464
[BE13]
Ehrhard Behrends
Fünf Minuten Mathematik: 100 Beiträge
der Mathematik-Kolumne der Zeitung DIE
WELT
Springer Spektrum. - 2013, 272 с. 3-е издание
ISBN: 978-3-658-00998-4
[ГМ72]
Гарднер Мартин
Математические досуги
М.: Мир, 1972. – 495 с.
465