Сдавался/использовался | Волгоград, 2005 руководитель: А.А. Теткин |
Загрузить архив: | |
Файл: Моделирование работы карьера.doc.zip (213kb [zip], Скачиваний: 1) скачать |
ФГОУ СПО «Волгоградский технологический коледж»
«Проект защитил
с оценкой »
А.И. Сухинин
30.05.05
Моделирование работы карьера
Курсовой проект
КП 11. 230105. 51. 0249 ПЗ
Разработчик А.И. Сухинин
30.05.05
Рук.проекта А.А. Теткин
30.05.05
Содержание
1. Введение………………………………………………………………………3
2. Сущность понятия «имитационное моделирование»………………………4
3. Описание системы……………………………………………….……………6
5. Анализ результатов работы программы……………………………....……24
6. Заключение……………………………………………………………..30
7. Список использованной литературы…………………………………….…31
ЕСЛИ НУЖНА ПРОГРАММА НА С++ ОБРАЩАЙТЕСЬ: saneek93@mail.ru
Оформление и правка возможна
1. Введение
В общем, моделирование можно считать универсальным методом описания физических, технических, организационных и других систем. До появления вычислительной техники в основном исследовались и применялись аналитические модели. Любой закон, описывающий поведение некоторой системы, связывающий характеризующие ее величины, должен рассматриваться как модель этой системы. Например, закон Ома, описывающий величины физических процессов в электрической цепи является моделью. Эта модель абстрагируется от частных характеристик конкретного процесса и определяется соотношение существующих его параметров. Таким образом, характеризующей чертой любой модели является абстрагирование. Указанная простейшая модель может быть отнесена к классу аналитических моделей, поскольку является аналитическим соотношением, допускающим непосредственное получение числовых результатов после постановки известных числовых параметров в соответствующее выражение. Такими же моделями независимо от уровня их сложности являются математические соотношения, описывающие связь параметров различных физических и технических систем. Например, система дифференциальных уравнений описывающие потоки жидкостей движения тел в различных средах, системы уравнений, описывающие связь электронных характеристик, сложных радиоэлектронных устройств, системы уравнений для расчетно-сложных механических конструкций и т.д.
Однако с усложнением системы в построении моделей, в которых мы нуждаемся, их точное аналитическое описание становится все более проблематичными. Кроме того, есть необходимость в изучении поведения системы в условиях изменяющихся случайным образом внешних воздействий. Эти два фактора усложнение и случайный характер воздействий приводит к необходимости создания другого класса моделей так называемых имитационных.
Целью данной курсовой работы является разработка имитационной модели с замкнутой системы с раздельными очередями и приоритетами т.е моделирование работы карьера. Основой для разработки модели в данной курсовой работе является метод имитационного моделирования. Так же курсовая работа предполагает создание программы на языке C++, обеспечивающей ввод исходной информации, ее обработку, реализацию алгоритма имитации процесса и выдачу необходимой информации.
Имитационное моделирование – это разработка и выполнение на компьютере программной системы, отражающей структуру и функционирование (поведение) моделируемого объекта или явления во времени. Такую программную систему называют имитационной моделью этого объекта или явления. Объекты и сущности имитационной модели представляют объекты и сущности реального мира, а связи структурных единиц объекта моделирования отражаются в интерфейсных связях соответствующих объектов модели. Таким образом, имитационная модель – это упрощенное подобие реальной системы, либо существующей, либо той, которую предполагается создать в будущем. Имитационная модель обычно представляется компьютерной программой, выполнение программы можно считать имитацией поведения исходной системы во времени.
В русскоязычной литературе термин «моделирование» соответствует американскому «modeling» и имеет смысл создание модели и ее анализ, причем под термином «модель» понимается объект любой природы, упрощенно представляющий исследуемую систему. Слова «имитационное моделирование» и «вычислительный (компьютерный) эксперимент» соответствуют англоязычному термину «simulation». Эти термины подразумевают разработку модели именно как компьютерной программы и исполнение этой программы на компьютере.
Итак, имитационное моделирование – это деятельность по разработке программных моделей реальных или гипотетических систем, выполнение этих программ на компьютере и анализ результатов компьютерных экспериментов по исследованию повеления моделей. Имитационное моделирование имеет существенные преимущества перед аналитическим моделированием в тех случаях, когда:
Во многих случаях имитационное моделирование – это единственный способ получить представление о поведении сложной системы и провести ее анализ.
Имитационное моделирование реализуется посредством набора математических инструментальных средств, специальных компьютерных программ и приемов, позволяющих с помощью компьютера провести целенаправленное моделирование в режиме «имитации» структуры и функций сложного процесса и оптимизацию некоторых его параметров. Набор программных средств и приемов моделирования определяет специфику системы моделирования – специального программного обеспечения.
В отличие от других видов и способов математического моделирования с применением ЭВМ имитационное моделирование имеет свою специфику: запуск в компьютере взаимодействующих вычислительных процессов, которые являются по своим временным параметрам – с точностью до масштабов времени и пространства – аналогами исследуемых процессов.
Имитационное моделирование как особая информационная технология состоит из следующих основных этапов:
3. Описание системы
В карьере самосвалы доставляют руду от трех экскаваторов к измельчителю. После выгрузки руды у измельчителя самосвал всегда возвращается к одному и томуже экскаватору, за которым он закреплен. Используются самосвалы грузоподъемностью 20 и 50 т. За каждым экскаватором закреплены два 20-тонных и один
50-тонный самосвал. Очередь к измельчителю — общая. От грузоподъемности
самосвала зависят времена его погрузки, движения до измельчителя, разгрузки и
возвращения к своему экскаватору. Для 20-тонных самосвалов задаются следую-
щие параметры: время погрузки распределено экспоненциально с математиче-
ским ожиданием 5 условных единиц времени, время поездки до измельчителя
постоянно и равно 2,5, время разгрузки распределено экспоненциально с мате-
матическим ожиданием 2, время возвращения к экскаватору постоянно и равно
1,5. Для 50-тонных самосвалов соответственно имеем: время погрузки распреде-
лено экспоненциально с математическим ожиданием 10, время поездки до из-
мельчителя постоянно и равно 3, время разгрузки распределено экспоненциаль-
но с математическим ожиданием 4, время возвращения постоянно и равно 2.
Очереди к каждому экскаватору организованы по принципу «первым пришел —
первым обслужен». В очереди к измельчителю приоритет имеют большегрузные
самосвалы. Схема работы карьера приведена на рис. 1.
Рис. 1. Сxeмaработы карьера
Требуется проанализировать функционирование всей системы в течение 480 единиц времени для определения загрузки экскаваторов и измельчителя и длины
очередей к ним.
В постановке задачи единица измерения времени не указана. Поэтому предположим, что все значения даны в минутах, и в качестве единицы модельного време-
ни примем секунду. Постоянные величины задаем сразу в секундах, а результаты ГСЧ экспоненциального распределения умножим на 60 и округлим до ближайшего целогочисла. Разумеется, в качестве масштабирующего коэффициента можно выбратьи любое другое значение, которое экспериментатор считает подходящим.
Система, которую мы собрались моделировать, обладает особенностью, — она является замкнутой. Это означает, что какие-либо внешние входные и выходные потоки отсутствуют, количество объектов всистеме постоянно и со временем не меняется. Поэтому в замкнутой системе отсутствует и понятие времени пребывания заявки в ней. В замкнутых системахвсегда существует установившийся режим. Аналитическое моделирование замкнутых систем гораздо более трудоемко по сравнению с моделированием открытых, поэтому для их изучения имитационные модели особенно полезны. При отсутствии внешнего входного потока число заявок во всех очередях имеетфиксированную верхнюю границу, поэтому в данной задаче нет необходимостидля моделирования очередей использовать списки. Очереди будем представлятьс помощью массивов.
Совокупность экскаваторов можно назвать пунктом погрузки, измельчитель — пунктом разгрузки, самосвалывыполняют ту же самую работу, система является замкнутой, число заявок постоянно. Однако существуют и отличия, привносящие в задачу новые, пока чтоне встречавшиеся нам особенности. Перечислим их:
Приведем полные списки полей данных классов.
Неизменяемые поля:
Изменяемые поля:
Неизменяемые поля:
Изменяемые поля:
Класс Emptier
Неизменяемые поля:
Изменяемые поля:
Класс HeavyCarне имеет моделирующих методов, за исключением метола run( ), так как все события, происходящие с самосвалом, моделируются методами других классов. С пунктом погрузки могут происходить следующие события:
Для каждого из этих событий нужно написать метод-обработчик. По смыслу выполняемых действий первый метод параметров не имеет, второй получает указа-
тель на объект класса HeavyCar, третий и четвертый методы получают целочис-
ленный порядковый номер эксковатора заершившего погрузку или отдых.
Возможные события для пункта разгрузки — прибытие очередного самосвала
и окончание разгрузки самосвала одним из эксковаторов.
В класс Emptierдобавляется служебный метод choice( ), реализующий дисциплину обслуживания с приоритетом.Этот метод вызывается из метода Complete( ) в момент завершения обслуживаниязаявки и выбирает номер по порядку в очереди для той заявки, которую следуетвыбрать для обслуживания в соответствии с заданным приоритетом. Сдвиг очереди при этом, разумеется, происходит не от первой заявки, а от той, котораябыла выбрана.
Нужно также определить смысл понятия «производительность» для данной си-
стемы.Делать это будем так: при окончании разгрузки 50-тонного самосвала увеличивать счетчик наединицу, а при окончании разгрузки 20-тонного самосвала — на 0,4. Таким образом, производительность будем измерять общим количеством разгруженных 50-тонных самосвалов (которое в итоге может оказаться дробным). Для подсчетамассы руды в тоннах это значение достаточно будет умножить на 50.
Условие завершения всех начатых работ, чтобы не усложнять программу, учтемтолько при вычислении основной характеристики — количества разгруженных
самосвалов. Это сделано следующим образом. В момент завершения рабочего
дня, то есть истечения восьмичасового интервала времени, проверяется состоя-
ние каждого самосвала. Если самосвал находится в очереди на погрузку или дви-
жется порожняком, он оставляется без внимания. При любом другом состоянии
он должен «дойти» до разгрузки, поэтому к числу разгруженных самосвалов
прибавляется единица.
Присоздания имитационной модели очереди с разнотипными заявками (работа порта) был выбран язык программирования C++ и написана программа на этом языке, позволяющая в полной мере отразить функционирование системы.
Листинг программы файл 8.h. Описание классов
#include
#include
#include
#include
using namespace std;
#include "random.h"
FILE *qu1;
FILE *qu2;
int completed=0;
float weight=0; //счетчикперевезенноймассыруды
float *que1_ave=NULL; //средние длины очередей к экскаваторам
float que2_ave=0; //средняя длина очереди к измельчителю
long int total;
int *ro_fuller=NULL; // коэффициентызагрузкиэкскаваторов
float ro_emptier=0;
long int path_full=0L;
long int path_empty=0L;
long int take=0L;
long int give=0L;
class HeavyCar
{
int direct; //время в пути от экскаватора к измельчителю
int back; //время в пути от измельчителя к экскаватору
int id;
int pr; //1 - для 20-тонного, 2 - для 50-тонного самосвала
int host; // номер экскаватора, к которому приписан самосвал
int state;
int to_pfull;
int to_pempty;
void *f;
void *e;
public:
friend class Fuller;
friend class Emptier;
HeavyCar(int a, int b, int c); //метод-конструктор
void putFuller(Fuller *f1);
void putEmptier(Emptier *e1);
void run();
void Print();
int State() { return(state); }
};
class Fuller
{
const static int perf20=2; //интенсивностьзагрузки 20-тонных
//самосвалов
const static int perf50=1; //интенсивность загрузки 50-тонных
//самосвалов
int *to_full;
HeavyCar **serving;
HeavyCar **queue;
public:
//Следующие поля данных объявлены открытыми, так как их значения //потребуются конструктору класса Emptier для подсчета общего количества //самосвалов в системе
const static int volume=3; //количествоэкскаваторов
const static int N20=2; //количество 20-тонных самосвалов
//у одного экскаватора
const static int N50=1; //количество 50-тонных самосвалов
//у одного экскаватора
Fuller();
~Fuller();
void PutCars(HeavyCar **h); //метод для начального заполнения пункта
//загрузкисамосвалами
void Complete(int i);
void Arrival(HeavyCar *h);
void Print();
int qLength(int i);
int State(int i);
void run();
};
class Emptier
{
const static int volume=1; //количествоизмельчителей
const static int perf20=5; //интенсивность разгрузки 20-тонных
//самосвалов
const static int perf50=25; //интенсивность разгрузки 50-тонных
//самосвалов
const static int order=2; // правило выбора заявок из очереди
int *to_empty;
HeavyCar **serving;
HeavyCar **queue;
int S; //общее количество самосвалов -
//максимальная длина очереди
public:
Emptier(Fuller *f);
~Emptier();
void Complete(int i);
void Arrival(HeavyCar *h);
void Print();
int qLength();
int Busy();
int FirstAvail();
void run();
int choice(); //выбор самосвала из очереди
//в соответствии с приоритетом
};
//Метод-конструктор. Параметры: а - уникальный идентификатор, b - вид //самосвала, с - номер экскаватора, к которому приписан самосвал
HeavyCar::HeavyCar(int a, int b, int c)
{
id=a;
pr=b;
host=c;
if (pr==1)
{
direct=150; //2,5 * 60 тактов модельного времени
back=90; //1,5 * 60 тактов модельного времени
}
else
{
direct=180;
back=120;
}
state=1;
to_pfull=-1;
to_pempty=-1;
}
void HeavyCar::putFuller(Fuller *f1)
{
f=f1;
}
void HeavyCar::putEmptier(Emptier *e1)
{
e=e1;
}
void HeavyCar::Print()
{
switch(state)
{
case 1: printf("Самосвал %d находитсявочередикэкскаваторуn", id); break;
case 2: printf("Самосвал %d загружаетсяn", id); break;
case 3: printf("Самосвал %d движется с грузом. Прибудет к измельчителю через %.1f минутn", id, ((float)to_pfull)/60); break;
case 4: printf("Самосвал %d находится в очереди к измельчителюn", id); break;
case 5: printf("Самосвал %d разгружаетсяn", id); break;
case 6: printf("Самосвал %d движется порожний. Прибудет к экскаватору через %.1f минутn", id, ((float)to_pempty)/60); break;
}
}
void HeavyCar::run()
{
if (state==3)
{
to_pfull--;
if (to_pfull==0)
{
to_pfull=-1;
((Emptier*)e)->Arrival(this);
}
}
else if (state==6)
{
to_pempty--;
if (to_pempty==0)
{
to_pempty=-1;
((Fuller*)f)->Arrival(this);
}
}
else;
switch(state)
{
case 1: take++; break; //вочередикэкскаватору
case 2: take++; break; //напогрузке
case 3: path_full++; break; //впутикизмельчителю
case 4: give++; break; //в очереди к измельчителю
case 5: give++; break; //наразгрузке
case 6: path_empty++; break; //впутикэкскаватору
}
return;
}
Fuller::Fuller()
{
int i, j;
to_full=new int[volume];
serving=new HeavyCar *[volume];
queue=new HeavyCar *[volume*(N20+N50)];
for(i=0;i { to_full[i]=-1; serving[i]=NULL; } } Fuller::~Fuller() { delete[] to_full; delete [] serving; delete [] queue; } //В этот метод передается массив указателей, подготовленный в функции //main() void Fuller::PutCars(HeavyCar **h) { int i, j; float mu; //Обход всех экскаваторов, заполнение очередей и постановка одного //самосвала на погрузку for(i=0;i { //Первый самосвал из каждой очереди ставим на погрузку if (h[i*(N20+N50)]->pr==1) mu=(float)perf20/10; else mu=(float)perf50/10; to_full[i]=(int)(get_exp(mu)*60); serving[i]=h[i*(N20+N50)]; serving[i]->state=2; //Остальные самосвалы ставим в очереди for(j=0;j<(N20+N50-1);j++) queue[i*(N20+N50)+j]=h[i*(N20+N50)+j+1]; queue[i*(N20+N50)+N20+N50-1]=NULL; } } void Fuller::Print() { int i, j; for(i=0;i { printf("В очереди к %d экскаватору - %d самосваловn", i+1, qLength(i)); for(j=0;j<(N20+N50);j++) { if (queue[i*(N20+N50)+j]==NULL) break; printf("%d-йвочереди - самосвал № %dn", j+1, queue[i*(N20+N50)+j]-> id); } } for(i=0;i { if (to_full[i]>0) printf("%d-йэкскаваторработает. Он обслуживает самосвал № %d. До окончания загрузки осталось %d минутn",i+1,serving[i]->id,to_full[i]/60); else printf("%d-й экскаватор простаивает.n",i+1); } return; } int Fuller::State(int i) { if (serving[i]!=NULL) return(1); //1 - экскаваторработает return(0); //0 - экскаваторнеработает } int Fuller::qLength(int i) { int j; for(j=0;j<(N20+N50);j++) if (queue[i*(N20+N50)+j]==NULL) return(j); return(N20+N50); } void Fuller::Arrival(HeavyCar *h) { int k, p; float mu; //Определяем тип самосвала, интенсивность его загрузки и экскаватор k=h->host; if (h->pr==1) mu=(float)perf20/10; else mu=(float)perf50/10; //Экскаватор занят, ставим самосвал в очередь к нему if (State(k)==1) { p=qLength(k); queue[k*(N20+N50)+p]=h; queue[k*(N20+N50)+p]->state=1; } //Экскаватор свободен, ставим самосвал на обслуживание else { to_full[k]=(int)(get_exp(mu)*60); serving[k]=h; serving[k]->state=2; } } void Fuller::Complete(int i) { int j; float mu; to_full[i]=-1; serving[i]->state=3; serving[i]->to_pfull=serving[i]->direct; serving[i]=NULL; if (qLength(i)==0) return; //Очередь не пуста, ставим на освободившийся экскаватор новый самосвал if (queue[i*(N20+N50)]->pr==1) mu=(float)perf20/10; else mu=(float)perf50/10; to_full[i]=(int)(get_exp(mu)*60); serving[i]=queue[i*(N20+N50)]; queue[i*(N20+N50)]->state=2; //Сдвигаем очередь for(j=0;j<(N20+N50-1);j++) queue[i*(N20+N50)+j]=queue[i*(N20+N50)+j+1]; queue[i*(N20+N50)+N20+N50-1]=NULL; } void Fuller::run() { int i; for(i=0;i { if (to_full[i]>0) to_full[i]--; if (to_full[i]==0) Complete(i); } for(i=0;i { fprintf(qu1,"%d ", qLength(i)); que1_ave[i]=que1_ave[i]*(1-1.0/(total+1))+((float)qLength(i))/(total+1); ro_fuller[i]=ro_fuller[i]+State(i); } fprintf(qu1, "n"); } Emptier::Emptier(Fuller *f) { int i; //Вот для чего поля volume, N20 и N50 были объявлены открытыми S=(f->volume)*(f->N20+f->N50); to_empty=new int[volume]; serving=new HeavyCar *[volume]; queue=new HeavyCar *[S]; for(i=0;i { to_empty[i]=-1; serving[i]=NULL; } for(i=0;i queue[i]=NULL; } Emptier::~Emptier() { delete [] to_empty; delete [] serving; delete [] queue; } void Emptier::Print() { int i; printf("В очереди к измельчителю - %d самосваловn", qLength()); for(i=0;i { if (queue[i]==NULL) break; printf("%d-й в очереди - самосвал № %dn", i+1, queue[i]->id); } for(i=0;i { if (to_empty[i]>0) printf("%d-йизмельчительработает. Он обслуживает самосвал № %d. До окончания погрузки %d минутn",i+1,serving[i]->id,to_empty[i]/60); else printf("%d-й измельчитель простаиваетn",i+1); } return; } int Emptier::qLength() { int i; for(i=0;i if (queue[i]==NULL) return(i); return(S); } int Emptier::Busy() { int k=0; for(int i=0;i if (to_empty[i]>0) k++; return(k); } int Emptier::FirstAvail() { for(int i=0;i if (to_empty[i]==-1) return(i); return(-1); } void Emptier::Arrival(HeavyCar *h) { int k, p; float mu; if (h->pr==1) mu=(float)perf20/10; else mu=(float)perf50/100; k=FirstAvail(); //Ставим прибывший самосвал в очередь if (k==-1) { p=qLength(); queue[p]=h; queue[p]->state=4; } //Ставим прибывший самосвал на обслуживание else { to_empty[k]=(int)(get_exp(mu)*60); serving[k]=h; serving[k]->state=5; } } void Emptier::Complete(int i) { int j, who; float mu; serving[i]->state=6; serving[i]->to_pempty=serving[i]->back; if (qLength()==0) { to_empty[i]=-1; serving[i]=NULL; } //Очередь не пуста else { //Выбираем из очереди самосвал и ставим на разгрузку who=choice(); if (queue[who]->pr==1) mu=(float)perf20/10; else mu=(float)perf50/100; to_empty[i]=(int)(get_exp(mu)*60); serving[i]=queue[who]; serving[i]->state=5; //Сдвигаемочередь for(j=who;j<(S-1);j++) queue[j]=queue[j+1]; queue[S-1]=NULL; } } //Реализацияприоритета int Emptier::choice() { int i; //Приоритетов нет. Первый в очереди - на разгрузку if (order==0) return(0); if (order==2) //приоритет 50-тоннымсамосвалам { for(i=0;i { if (queue[i]==NULL) return(0); if (queue[i]->pr==2) return(i); } return(0); } else //приоритет 20-тонным самосвалам { for(i=0;i { if (queue[i]==NULL) return(0); if (queue[i]->pr==1) return(i); } return(0); } } void Emptier::run() { for(int i=0;i { if (to_empty[i]>0) to_empty[i]--; if (to_empty[i]==0) { //Разгружен 20-тонныйсамосвал if (serving[i]->pr==1) weight+=0.4; else weight+=1; //разгружен 50-тсамосвал Complete(i); completed++; } } fprintf(qu2,"%dn", qLength()); que2_ave=que2_ave*(1-1.0/(total+1))+((float)qLength())/(total+1); ro_emptier=ro_emptier*(1-1.0/(total+1))+(((float)Busy())/volume)/(total+1); } Листинг программы файл random.h #include #include #include float get_exp(float mu) { //генератор случайных чисел, распределенных //экспоненциально intr_num; floatroot, right; r_num=rand(); //получение случайного целогочисла right=((float)r_num)/(RAND_MAX+1); //проекция на интервал (0;1) root=-log(1-right)/mu; //вычисление значения обратной функции return(root); } intget_uniform(inta, intb){ //Генерация равномерно распределенной величины a+b int x, y; x=rand()%(b+1); y=rand()%2; if (y==0) return(a-x); return(a+x); } float get_triangle(float A, float B, float C){ int r_num; float root, right; r_num=rand(); //получение случайного целого //числа right=((float)r_num)/(RAND_MAX+1); //проекция на интервал (0;1). //Константа RAND_MAX=32767 (215-1) определена в cstdlib if (right<(C-A)/(B-A)) root=A+sqrt(right*(B-A)*(C-A)); else root=B-sqrt((1-right)*(B-A)*(B-C)); return(root); } float get_pareto(float A, float B){ int r_num; float root, right; r_num=rand(); //получение случайного целого числа right=(float)r_num/RAND_MAX+1; //проекция на интервал (0;1) root=A/(pow(1-right, (float) 1.0/B)); //вычисление значения обратной функции return(root); } Прежде чем рассмотреть листинг функции main(), коротко остановимся на порядке создания объектов и их инициализации, который в даннойзадаче является отнюдь не произвольным. В первую очередь должен быть создан Листингпрограммыфункция main() #include "stdafx.h" #include "iostream" #define N 28800 #include "8.h" int main(){ int i, a, b, c, j; HeavyCar **masCar=NULL; srand((unsigned)time(0)); Fuller fu; Emptier em(&fu); a=fu.N20; b=fu.N50; c=fu.volume; que1_ave=new float[c]; ro_fuller=new int[c];
объект класса Fuller, так как именно он владеет информацией о количестве экс-
каваторов и самосвалов, необходимой конструкторам других объектов. Эта ин-
формация может быть представлена либо в виде констант, как в приведенном
варианте программы, либо в виде значений полей данных, передаваемых в каче-
стве аргументов конструктору класса Fuller. Затем создаем объект класса Emptier,
передавая конструктору указатель на уже имеющийся объект Fuller. Далее за-
полняем массив указателей на объекты HeavyCar, зная их количество и типы
опять-таки из существующего объекта Fuller. Для каждого созданного объекта
HeavyCarустанавливаем связи с имеющимися объектами Fullerи Emptier. В за-
вершение для объекта Fullerвызываем метод PutCars( ), передавая в качестве па-
раметра сформированный массив указателей на HeavyCar. Только после этого все
объекты можно считать полностью проинициализированными и готовыми для
запуска основного моделирующего цикла.