TURBO PASCAL |
Новости
|
Биоритмы
Давно известно, что творческая и физическая активность человека не остается постоянной, циклически меняется, причем периодичность ее изменения приблизительно согласуется с периодом вращения Луны вокруг Земли. Существует теория, согласно которой физическая, эмоциональная и интеллектуальная активность человека подчиняется соответствующим биоритмам. Каждый биоритм представляет собой синусоиду со строго постоянным периодом, причем для каждого биоритма существует свой период. В отдельные дни все три биоритма человека могут достигнуть своего максимума и тогда человек испытывает подъем творческих и физических сил, в такие дни у него все спорится, он легко решает проблемы, которые в другое время ему решить гораздо сложнее. Точно также существуют и «черные» дни, соответствующие спаду всех трех биоритмов. Используя уже опробованную методику нисходящего программирования, создадим программу, в которой запрашивается дата рождения человека и дата, для которой требуется оценить его состояние. Программа должна рассчитать и выдать на экран ближайшие к этой дате дни пика и спада биоритмов. Алгоритм программы можно укрупнено записать следующим образом:
Будем считать, что каждое из перечисленных действий реализуется в отдельной процедуре, тогда начальный вариант программы будет таким: Procedure InputDates(var dO,mO,yO,d,m,y: Integer); {Вводит дату рождения и текущую дату. Контролирует правильность дат и их непротиворечивость (текущая дата должна быть позже даты рождения) } begin {InputDates} end; {InputDates} {..........................}
Procedure Get_count_pf_days (dO,mO,yO,d,m,y: Integer; var days: Integer); {Определяет полное количество дней, прошедших от одной даты до другой} begin {Get_count_of_days} end; {Get_count_of_days} {--------------------------} Procedure FindMaxMin (var dmin,dmax: Integer; days: Integer); {Ищет критические дни} begin {FindMaxMin} end; {FindMaxMin} {--------------------------} Procedure WriteDates ( dmin , dmax , days : Integer); {Определяет критические даты по количеству дней, прошедших от момента рождения, и выдает эти даты на экран} begin {WriteDates} end; {WriteDates} {--------------------------} var d0,d , {Дни рождения и текущий} m0,m, {Месяцы рождения и текущий} у0,у, {Годы рождения и текущий} dmin, {Наименее благоприятный день} dmax, {Наиболее благоприятный день} days: Integer; {Количество дней от рождения} begin {Главная программа} Input-Dates (d0,m0,y0,d,m,y) ; Get_numbers_of_days (d0,m0,y0,d,m,y,days) ; FindMaxMin (dmin, dmax, days) ; WriteDates (dmin, dmax, days) end . Начинаем детализацию программы. Прежде всего подумаем, как по двум датам вычислить разделяющее их количество дней? Если вспомнить, что следует учитывать неодинаковое количество дней по месяцам года, а также 29 февраля для високосных лет, то ответ на этот вопрос окажется не таким уж простым. Предлагаемый алгоритм подсчета количества дней заключается в вычислении количества дней от даты рождения до конца месяца, а затем и года рождения, количества дней, от начала текущего года до текущего месяца и текущей даты, а также - в подсчете количества полных лет, разделяющих обе даты. Количество лет затем легко пересчитывается в количество дней с учетом длины года (365 дней для обычных и 366 дней для високосных лет). Это очень прямолинейный алгоритм, но, откровенно говоря, мне не пришло в голову ничего другого. Возможно, существует более изящный способ подсчета и Вы его знаете, тогда программная реализация будет другой. Упростить алгоритм можно за счет создания и использования массива из 12 целых чисел, содержащего количества дней по месяцам невисокосного года, т.е. 31, 28, 31, 30 и т.д. Этот массив (назовем его SIZE_OF_MONTH - длина _месяца) можно использовать и для обратной задачи, т.е. для определения даты критических дней, а также для проверки правильности вводимых дат. Таким образом, массив SIZE__OF_MONTH будет использоваться сразу в трех процедурах. Сделаем его глобальным, для чего его описание поместим перед описанием процедур: const Size_of_Month: array - [1. .12] of Byte = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31); {--------------------------} Procedure InputDates (var d0,m0,y0,d,m,y: Integer); ......... Поскольку описание массива размещается до описания процедур, он становится доступным внутри каждой из процедур и служит для них глобальным. В отличие от этого все константы и переменные, объявляемые внутри некоторой процедуры, являются локальными и могут использоваться только в этой процедуре. С учетом сказанного напишем следующий начальный вариант программной реализации процедуры INPUTDATES: Procedure InputDates(var d0,m0,y0,d,m,y: Integer); {Вводит дату рождения и текущую дату. Контролирует правиль- ность дат и их непротиворечивость (текущая дата должна быть позже даты рождения)} var correctly: Boolean; {Признак правильного ввода} begin {InputDates} repeat {Вводим и контролируем дату рождения d0,m0,y0.} {Вводим и контролируем текущую дату d,m,y.} {Проверяем непротиворечивость дат:} correctly := у > у0; if not correctly and (у = y0) then begin correctly := m > m0; if not correctly and (m = m0) then correctly := d>=d0 end until correctly end; {InputDates} В этой процедуре дважды выполняется одно и то же алгоритмическое действие (ввод и контроль даты). Это действие можно вынести в отдельную внутреннюю процедуру с именем INPDATE, тогда получим следующий окончательный вариант: Procedure InputDates(var d0,m0,y0,d,m,y : Integer); {Вводит дату рождения и текущую дату. Контролирует правильность дат и их непротиворечивость (текущая дата должна быть позже даты рождения)} var correctly: Boolean; {Признак правильного ввода} {--------------------------} Procedure InpDate (text: String; var d,m,y: Integer); {Выводит приглашение TEXT, вводит дату в формате ДД ММ ГГГГ и проверяет ее правильность} const YMIN = 1800; {Минимальный правильный год} YМАХ = 2000; {Максимальный правильный год} begin {InpDate} repeat Write (text); ReadLn (d,m,y) ; correctly := (y >= YMIN) and (Y <= YMAX) and (m >= 1) and (m <= 12) and (d > 0) ; if correctly then if (m = 2) and (d = 29) and (y mod 4=0) then {Ничего не делать: это 29 февраля високосного года!} else correctly := d <= Size_of_Month[m] ; if not correctly then WriteLn (' Ошибка в дате!') until correctly end; {InpDate} {--------------------------} begin {InputDates} repeat InpDate (' .Введите дату рождения в формате ДД ММ ГГГГ:',d0,m0,y0) ; InpDate (' Введите текущую дату: ',d,m,y); {Проверяем непротиворечивость дат:} correctly := у > у0; if not correctly and (y = y0) then begin correctly := m > m0; if not correctly and (m = m0) then correctly := d >= d0 end until correctly end; {InputDates} В самом общем виде алгоритм подсчета количества дней, разделяющих две даты, описан выше. При его реализации следует учесть три возможных варианта:
С учетом этого составим начальный вариант программной реализации процедуры GET_NUMBERS_OF_DAYS : Procedure Get_numbers_of_days (d,m,y,d,m,y: Integer; var days: Integer); {Определение полного количества дней, прошедших от одной даты до другой } {--------------------------} Procedure Variant2; {Подсчет количества дней в месяцах,разделяющих обе даты} begin {Variant2} end; {Variant2} {--------------------------}
Procedure Variant3 ; {Подсчет количества дней в месяцах и годах, разделяющих обе даты} begin {Variant3} end; {Variant3} {--------------------------} begin {Get_numbers_of_days} if (y = y0) and (m = m0) then {Даты отличаются только днями: } days := d - d0 else {Даты отличаются не только днями:} begin days := d + Size_of_Month [m0] - d0; {Учитываем количество дней в текущем месяце и количество дней до конца месяца рождения} if (y0 mod 4=0) and (m0 = 2) then inc(days); {Учитываем високосный год} if у = y0 then Variant2 {Разница в месяцах одного и того же года} else Variant3 {Даты отличаются годами} end end; {Get_numbers_of_days} В этом фрагменте используется способ связи вспомогательных процедур VARIANT2 и VARIANT3 с основной процедурой через глобальные переменные, которыми являются параметры обращения к основной процедуре. Вспомогательные процедуры удобнее всего реализовать на основе циклов WHILE: Procedure Variant2 ; {Подсчет количества дней в месяцах, разделяющих обе даты } var mm : Integer; begin {Variant2} mm : = m0 ; while mm < m do begin days := days + Size_of_Month [mm] ; if (mm = 2) and (y0 mod 4=0) then inc (days) ; inc (mm) end end; {Variant2} {--------------------------} Procedure Variant3; {Подсчет количества дней в месяцах и годах, разделяющих обе даты } var mm/ УУ : Integer; begin {Variant3} mm : = m0 + 1 ; while mm <= 12 do {Учитываем остаток года рождения:} begin days := days+Size_of_Month [mm] ; if (mm = 2) and (y0 mod 4=0) then inc (days) ; inc (mm) end ; yy := y0 + 1; while yy < у do {Прибавляем разницу лет:} begin days : = days + 365; if yy mod 4=0 then inc (days) ; inc (yy) end; mm : = 1 ; while mm < m do {Прибавляем начало текущего года:} begin days := days + Size_of_Month [mm] ; if (y mod 4=0) and (mm = 2) then inc (days) ; inc (mm) end end; {Variant3} В процедуре FINDMAXMIN осуществляется поиск критических дней, т.е. ближайших к текущей дате дней, для которых все три биоритма достигают своего максимума и минимума. Предполагается, что биоритмы изменяются по законам синуса от количества прожитых дней с периодами ТF, ТE и TI соответственно для физической, эмоциональной и интеллектуальной активности человека. В программе приняты следующие периоды (в днях): Знакомство с языком Турбо Паскаля TF= 23.6884 ТЕ= 28.4261 TI= 33.1638 Самый простой алгоритм поиска заключается в том, чтобы вычислить значения сумм всех трех синусоид для текущего дня и для каждого из последующих дней на некотором заранее обусловленном интервале, например, в пределах месяца. Сопоставив результаты расчетов для каждого дня, нетрудно определить критические дни: Procedure FindMaxMin(var dmin,dmax: Integer; days: Integer); {Поиск критических дней} const TF = 2*3.1416/23.6884;{Период физической активности} ТЕ = 2*3.1416/28.4261;{Период эмоциональной активности} TI = 2*3.1416/33.1638;{Период интеллектуальной активности} INTERVAL =30; {Интервал прогноза} var min, {Накапливает минимум биоритмов} max, {Накапливает максимум биоритмов} x : Real; {Текущее значение биоритмов} i : Integer; begin {FindMaxMin} max := sin(days*TF)+sin(days*TE)+sin(days*TI); min := max; {Начальное значение минимума и максимума равно значению биоритмов для текущего дня} dmin := days; dmax := days; for i := 0 to INTERVAL do begin x := sin((days+i)*TF) + sin((days+i)*TE) + sin((days+i)*TI); if x > max then begin max : = x; dmax : = days + i end else if x < min then begin min := x; dmin := days + i end end; end; {FindMaxMin}
При разработке алгоритма процедуры WRITEDATES, с помощью которой на экран выводится результат работы программы, учтем, что основные сложности будут связаны с определением новой даты по начальной дате и количеству прошедших дней. Этот насчет будет повторяться дважды - для даты пика и даты спада биоритмов, поэтому его следует вынести в отдельную процедуру WRITEDATES. Кроме того, вряд ли Вы откажетесь от возможности вывода на экран дополнительной информации о том, сколько полных дней, часов, минут и секунд разделяют дату рождения человека и текущую дату. Однако реализация этого вывода не столь проста, как это может показаться на первый взгляд. Дело в том, что диапазон возможных значений данных типа INTEGER составляет от -32768 до +32767. Средняя продолжительность жизни человека - около 70 лет, т.е. 25550 дней. Это значение еще можно представить в Переменной типа INTEGER, однако часы, минуты и тем более секунды средней продолжительности жизни далеко превышают этот диапазон. Чтобы получить вывод достоверных данных, необходимо расширить диапазон значений целых чисел. Для этого в Турбо Паскале предусмотрен специальный тип данных LONGINT («длинный» целый), имеющий диапазон значений от -2147483648 до +2147483647 (см. гл. 4). Поэтому в процедуре WRITEDATES следует предусмотреть вспомогательную переменную этого типа, присвоить ей значение переменной DAYS и уже затем использовать «длинную» переменную для вычисления (и вывода) часов, минут, секунд. В результате начальный вариант процедуры WRITEDATES может быть таким: Procedure WriteDates (dmin,dmax,days : Integer); {Определение и вывод дат критических дней. Вывод дополнительной информации о количестве прожитых дней, часов, минут и секунд } {---------------------} Procedure WriteDate (text : String; dd : Integer); {Определение даты для дня DD от момента рождения. В глобальных переменных d, m и у имеется текущая дата, в переменной DAYS -количество дней, прошедших от момента рождения до текущей даты.Выводится сообщение TEXT и найденная дата в формате ДД-МЕС-ГГГГ} begin {WriteDate} end; {WriteDate} {---------------------} var LongDays: Longlnt; {"Длинная" целая переменная для часов,минут и секунд } begin {Wri teDates} LongDays : = days ; WriteLn( 'Прошло: ', LongDays,' дней, ' , longDays*24, ' часов, ', LongDays*24*60, ' минут, ', LongDays*24*60*60, ' секунд'); WriteDate ( 'Наименее благоприятный день: ', drain); WriteDate ( 'Наиболее благоприятный день: ',dmax) end; {WriteDates} Реализация процедуры WRITEDATE не вызывает особых сложностей: Procedure WriteDate (text: String; dd: Integer); const Names_of_Monthes : array [1..12] of String [3] =('янв','фев','мар','апр','мая', 'июн','июл','авг','сен','окт', 'ноя','дек'); var d0,m0,y0,ddd : Integer; begin {WriteDate} d0 := d; m0 := m; y0 : = y; ddd := days; while ddd<>dd do begin inc(d0); {Наращиваем число} if (y0 mod 4 <> 0) and (d0 > Size_of_Month[m0]) or (y0 mod ,4=0) and (d0=30) then begin {Корректируем месяц} d0 := 1; inc(m0); if m0 = 13 then {Корректируем год} begin m0 := 1; inc(y0) end end; inc(ddd) end; WriteLn(text,d0,'-',Names_of_Monthes[m0] ,'-',y0) end; {WriteDate} Собрав воедино отдельные части, получим полный текст программы (прил.5.2), предназначенной для определения биоритмов.
|
(с)Все права защищены По всем интересующим вопросам прошу писать на электронный адрес |