Задача 22 ЕГЭ по информатике - многопроцессорные системы

 Задача 22 ЕГЭ по информатике - многопроцессорные системы

Хотите готовиться со мной к ЕГЭ?
Пишите:
ydkras@mail.ru
Немного обо мне.

 В прошлом году в ЕГЭ по информатике появился новый тип задач - многопроцессорные системы (задача 22). 

Смысл задачи достаточно простой. Имеется набор процессов, которые могут выполняться параллельно. Однако одни процессы не зависят от других и могут быть запущены в любой момент времени, другие же процессы должны дожидаться завершения некоторых других процессов, от которых они зависят. (Подразумевается, что одни процессы могут вырабатывать какие-то выходные данные, которые другие процессы используют как входные и соответственно не могут начать выполнение, пока "процессы-поставщики" не завершатся.)

 

Простое решение

Для примера возьмем задачу из демонстрационного варианта ЕГЭ на 2023 г. К задаче прилагается файл Excel со следующей таблицей:



ABC
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процесса(ов)
A
2140
3230
4311; 2
5473
6563
7635
8714; 6
9827
10970
111080
121169
1312610


Если в столбце C стоит 0, то этот процесс не зависит от других процессов и может быть запущен в самом начале выполнения совокупности процессов. В противном случае в столбце C через точку с запятой перечисляются номера процессов, от которых зависит данный процесс.

В данном случае процессы 1, 2, 9 и 10 - независимые. Процесс 3 зависит от процессов 1 и 2 (т.е. может быть запущен не ранее, чем завершатся процессы 1 и 2), процесс 4 зависит от процесса 3 и т.д.

Данная задача легко решается вручную.

В столбце D будем записывать время начала каждого процесса, а в столбце E - время его завершения.

Для процессов  1, 2, 9 и 10 сразу же записываем, что их время начала - 0. Время завершения - очевидно, время начала плюс время выполнения процесса. Записываем в ячейку E2 формулу =D2+B2 и копируем эту формулу в ячейки E3, E10 и E11.

Теперь наша таблица выглядит так:


ABCDE
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процесса(ов)
A
НачалоКонец
214004
323003
4311; 2

5473

6563

7635

8714; 6

9827

1097007
11108008
121169

1312610

Теперь можно заполнять времена начала зависимых процессов. 

Процесс 3 зависит от процессов 1 и 2. Мы видим, что процесс 1 завершается в момент времени 4, а процесс 2 - в момент 3. Значит, самое раннее время, когда можно запустить процесс 3 - это момент 4 (максимум из 4 и 3). Вписываем в ячейку D4 число 4, а в ячейку E4 - нашу формулу для вычисления времени окончания процесса. (Можно скопировать эту формулу нажатием клавиш Ctrl-C, а потом вставлять её в нужные ячейки, нажимая Ctrl-V.)

Процесс 3 завершается в момент времени 5.

От процесса 3 зависят процессы 5 и 6, ставим момент завершения процесса 3 (т.е. 5) как время начала процессов 4 и 5. Копируем формулу для времени завершения процесса в столбец D для процессов 4 и 5.

Теперь наша таблица вынлядит так:


ABCDE
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процесса(ов)
A
НачалоКонец
214004
323003
4311; 245
5473512
6563511
7635

8714; 6

9827

1097007
11108008
121169

1312610

Продолжаем заполнять столбец с временами начала процессов и вставляем формулу для времени окончания в ячейки столбца E.

В итоге наша таблица приобратвет следующий вид:


ABCDE
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процесса(ов)
A
НачалоКонец
214004
323003
4311; 245
5473512
6563511
76351114
8714; 61415
98271517
1097007
11108008
121169713
1312610814


Наша таблица заполнена целиком. Из неё видно, что самое позднее время завершения - у процесса 8, это время равно 17 миллисекундам. В задаче спрашивалось, какое минимальное время завершения у данной совокупности процессов. Теперь мы вилим, что ответ на задачу - 17.

Очевидно, что можно не искать максимальное число в столбце  E глазами, а вписать в какую-нибудь свободную ячейку простую формулу =МАКС(E2:E13)

Просто? Да, пока просто. Но...

 

Сложности и как их побороть

Разобранная выше задача - очень простая. Но в задачах этого типа возможны многочисленные усложнения. Вот некоторые из тех, которые встречались мне в таких задачах.

Номера процессов могут быть, скажем, пятизначными числами, и вам придется искать в таблице процесс номер 10688, да ещё при этом не перепутать его с процессом номер 10668.

Число процессов может быть достаточно большим (скажем, 100), и тогда ручное заполнение таблицы потребует значительного времени. 

В вышеприведенной задаче таблицу можно было заполнять сверху вниз. Но в других задачах вполне может оказаться, что процесс 3 зависит от процесса 12, который находится ниже в таблице, и тогда вам придется потратить немало времени на то, чтобы искать, какой процесс мы можем обработать на очередном шаге.

Каждый процесс в разобранной выше задаче зависит максимум от двух процессов. Но, вообще говоря, нет ограничений на количество процессов, от которых зависит данный процесс.

Выход из положения очевиден: надо заполнять нашу таблицу полностью автоматически. Давайте научимся это делать.

Будем решать одну из задач, предлагавшихся на ЕГЭ по информатике в 2023 г.:


ABC
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процессов
A
21200
32260
434112
54280
65292
76255;7;8;15
87314
98321
1092812;20
1110347
12113018
1312290
14133211;12
15143311;16
1615399
1716337;15
18173613
1918370
20192412;17
2120331;3;4;13

Как видим, количество процессов тут больше, чем в первой задаче, некоторые процессы зависят от четырех других процессов, и заполнять таблицу по порялку сверху вниз не получится: процесс 3, например, зависит от процесса 12, поэтому строку процесса 3 мы не сможем заполнить до того, как будет заполнена строка процесса 12.

Шаг 1. Разбиваем столбец зависимости процессов.

В столбце C номера всех процессов, от которых зависит данный процесс, перечислены через точку с запятой. Разнесем каждый номер в отдельный столбец.

Выделим ячейки C2:C21 и вызовем мастер разбиения текста по столбцам (в LibreOffice Calc он находится в меню "Данные"). Обязательно проверяем, что выбран разделитель "точка с запятой". После выполнения данного мастера наша таблица выглядит так:

 


ABCDEF
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процессов
A



21200


32260


434112


54280


65292


762557815
87314


98321


109281220

1110347


12113018


1312290


1413321112

1514331116

1615399


171633715

18173613


1918370


2019241217

21203313413


Шаг 2. Предварительная подготовка.

Итак, теперь у нас в столбцах C, D, E и F - номера процессов, от которых зависит данный процесс (или пустые ячейки, или число 0 в столбце C, если процесс независимый).

Отведем столбцы G и H для времени начала и конца каждого процесса, точно так же, как мы делали это ранее. 

Со временем конца всё очень просто: надо в соответствующую ячейку вписать формулу для суммирования времени начала данного процесса и его продолжительности. Давайте не будем тянуть и сделаем это. В ячейку H2 впишем формулу =G2+B2, скопируем её и вставим в ячейки H3:H21.

Наша таблица приобретает такой вид:


ABCDEFGH
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процессов
A



НачалоКонец
21200



20
32260



26
434112



41
54280



28
65292



29
762557815
25
87314



31
98321



32
109281220


28
1110347



34
12113018



30
1312290



29
1413321112


32
1514331116


33
1615399



39
171633715


33
18173613



36
1918370



37
2019241217


24
21203313413
33

Пока время завершения каждого процесса совпадает с его длительностью (при суммировании в качестве значения пустой ячейки берется ноль).

Возьмем для примера строку 21 (процесс 20) и предположим, что в ячейке I21 находится время завершения процесса 1, в ячейке J21 - время завершения процесса 3, в ячейке K21 - время завершения процесса 4 и в ячейке L21 - время завершения процесса 13. (Номера процессов 1, 3, 4 и 13 перечислены в ячейках C21:F21.) Тогда время начала процесса 21 можно определить с помощью простой формулы =МАКС(I21:L21)

Впишем в ячейку G2 формулу =МАКС(I2:L2) и размножим её по ячейкам G3:G21. Получим таблицу:


ABCDEFGH
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процессов
A



НачалоКонец
21200


020
32260


026
434112


041
54280


028
65292


029
762557815025
87314


031
98321


032
109281220

028
1110347


034
12113018


030
1312290


029
1413321112

032
1514331116

033
1615399


039
171633715

033
18173613


036
1918370


037
2019241217

024
21203313413033

Как видно, применение функции МАКС к группе пустых клеток дает в результате ноль. Это очень удобно для наших целей.

Пора сделать решающий шаг.

Шаг 3. Заполнение времен окончаний процессов с помощью функции ВПР.

Чтобы вычислить время окончание какого либо процесса, зная его номер, надо найти в таблице строку, где этот номер находится в столбце А, и взять из этой строки ячейку в столбце H. Именно такие операции и делает функция ВПР (Вертикальный ПРосмотр).

Функция ВПР имеет четыре параметра. Первый из них - "что ищем", второй - "где ищем", третий - "что выдаём как результат". Четвертый параметр в нашем случае должен быть равен нулю.

Запишем в ячейку I2 следующую формулу:

=ЕСЛИ(C2>0;ВПР(C2;$A$2:$H$21;8;0);"")

и размножим её во все ячейки диапазона I2:L21 - и решение готово (почти).

Небольшие пояснения. Если в ячейке C2 находится положительное число, то для заполнения ячейки I2 мы используем функцию ВПР, иначе ячейка будет пустой. Функция ВПР ищет в самом левом столбце области  $A$2:$H$21 (т.е. в столбце A) содержимое ячейки, указанной первым её параметром. Если такая строка будет найдена, то функция возвращает результат из 8-го столбца области $A$2:$H$21 - т.е. из столбца H. (Если строка найдена не будет, то возникнет ошибка: именно поэтому мы вызываем ВПР только в том случае, если ячейка поиска >0, тем самым исключая пустые ячейки и ячейки с нулевыми значениями.)

Чтобы получить ответ, впишем в ячейку A23 (или любую другую свободную) формулу =МАКС(H2:H21) и увидим в этой ячейке число 265. 


ABCDEFGHIJKL
1ID процесса BВремя выполнения процесса B (мс)ID процессов A


НачалоКонецТк1Тк2Тк3Тк4
21200


020



32260


026



434112


297029


54280


028



65292


265526


762557815199224555952199
87314


285928


98321


205220


109281220

13216029132

1110347


599359


12113018


376737


1312290


029



1413321112

67996729

1514331116

23226567232

1615399


160199160


171633715

19923259199

18173613


9913599


1918370


037



2019241217

13515929135

212033134139913220702899
22











23265











265 - это и есть правильный ответ для данной задачи.


Более сложные вопросы

В разобранных задачах вопрос формулировался примерно так: каково минимальное время завершения совокупности всех процессов? Мы видели, что ответ на этот вопрос - максимальное из времен завершения процессов. 

Однако вопросы, как известно, бывают разные. Так, в одной из задач спрашивалось, сколько процессов начнутся не ранее чем через 100 миллисекунд с момента начала выполнения. Ответить на этот вопрос также несложно. Сначала найдем для каждого из процессов время его начала и время завершения, как было описано выше. А затем найдем, сколько раз в столбце "Начало выполнения" встречаются числа, большие либо равные 100. Это можно сделать с помощью функции СЧЕТЕСЛИ.

А вот пример более сложного вопроса: какова суммарная длительность всех промежутков времени, в течение которых выполнялось ровно 3 процесса?

Попробуем решить эту задачу. Исходные данные для неё следующие:


ABC
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процессов
A
2140
3220
4351; 2
5473
6563
7625
8754; 6
9826
10970
111090
121169
1312610

Начнем с того, что найдем для каждого процесса время его начала и время завершения (см. предыдущий раздел). В результате получается такая таблица:


ABCDEFGH
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процессов
A

НачалоКонецТк1Тк2
2140
04

3220
02

435124942
5473
9169
6563
9159
7625
151715
8754617221617
9826
171917
10970
07

111090
09

121169
7137
1312610
9159

Теперь для каждого момента времени найдем, сколько процессов выполняются в данный момент. Это можно сделать с помощью функции СЧЁТЕСЛИМН.

В столбце A, начиная с ячейки А16, запишем моменты времени: 0, 1, 2 и т.д. до 25 (из таблицы видно, что максимальный нужный нам момент времени - 22, но я люблю делать всё с небольшим запасом). Для ускорения процесса можно в ячейку A16 занести число 0, в ячейку A17 - формулу =A16+1 и размножить эте формулу в нижерасположенные ячейки в столбце A.

В ячейку B16 запишем формулу

=СЧЁТЕСЛИМН($E$2:$E$13;"<="&A16;$F$2:$F$13;">"&A16)

и размножим её ниже по столбцу B. (Важно: для нижней границы мы используем нестрогое неравенство "<=", а для верхней - строгое ">".) Знак амперсанда "&" используется для того, чтобы осуществлять сравнение не с конкретно заданным числом, а со значением из указанной ячейки.

Получаем следующее:


ABCDEFGH
1ID
процесса
B
Время
выполнения
процесса
B (мс)
ID
процессов
A

НачалоКонецТк1Тк2
2140
04

3220
02

435124942
5473
9169
6563
9159
7625
151715
8754617221617
9826
171917
10970
07

111090
09

121169
7137
1312610
9159
14







15tКол-во





1604





1714





1823





1933





2043





2153





2263





2373





2483





2594





26104





27114





28124





29133





30143





31152





32161





33172





34182





35191





36201





37211





38220





39230





40240





41250





Мы видим, что в моменты 0 и 1 выполняются четыре процесса, в моменты с 2 по 8 - три и т.д.

Вписываем в любую свободную ячейку формулу =СЧЁТЕСЛИ(B16:B41;3) и видим ответ: число 9.

 

А как же Питон?

В задаче 9 ЕГЭ по информатике вполне работающий способ решения - сохранить данные из электронной таблицы в текстовом файле (формат csv) и обработать этот файл с помощью совсем небольшой и несложной программы на питоне.

А можно ли решить и данную задачу с помощью программы?  Разумеется, можно. Рассмотрим возможный способ её рашения.

Исходные данные.

Обрабатывать файл xls/xlsx программой на питоне, разумеется, мы не будем. Сохраним информацию из него в текстовом формате. Сперва удалим из таблицы первую строку с заголовками стоьбцов - она нам совершенно не нужна. 

Теперь скажем "Сохранить как...", выберем формат сохранения "Текст CSV" и запишем файл на диск под каким-нибудь именем. При сохранении в качестве разделителя полей выберем точку с запятой или пробел. 

На диск запишется файл со строками вида

19;24;"12;17"
20;33;"1;3;4;13"

Можно предварительно применить к третьему столбцу со списками процессов, от которых зависит данный процесс, операцию "Текст по столбцам" (см.выше), тогда файл после сохранения будет выгляденть несколько иначе: 

19;24;12;17;;;
20;33;1;3;4;13

 

Программа. 

Чтобы правильно обработать и тот, и другой формат, поступим следующим образом: заменим в прочитанной строке точки с запятой и двойные кавычки на пробелы, а потом применим операцию split(). 

Вот фрагмент программы, которая превратит наш текстовый файл в список lines, каждый элемент которого - это список целых чисел из одной строки нашего текстового файла:

f=open('22.csv')

lines=[list(map(int,s.replace('"',' ').replace(';',' ').split())) for s in f]

 

Так, две приведенные выше строки превратятся в следующие списки: 

 

[19,24,12,17]
[20,33,1,3,4,13]

 

Первый элемент (т.е. с индексом "0") этих списков - это номер процесса, второй - его продолжительность, а далее перечисляются процессы, от которых этот процесс зависит (либо ноль, если процесс не зависит от других).

 

На основе этих списков создадим словарь w. Ключом в элементе словаря будет номер процесса, а значением - список из остальных чисел (т.е. длительность процесса и номера процессов, от которых зависит данный процесс).

 

w={line[0]:line[1:] for line in lines}

 

Приведенные выше две строки превратятся в следующие элементы нашего словаря:


19:[24,12,17]

20:[33,1,3,4,13]

 

Напишем функцию tstart(p), определяющую время начала процесса с номером p. Если процесс независимый, т.е. элемент w[p][1] нашего словаря равен нулю, то время начала процесса - 0. В противном случае нам нужен максимум из времен завершения всех процессов, от которых зависит данный. Время завершения - это время начала процесса плюс его продолжительность. 

 

Вот наша функция:

 

def tstart(p): return 0 if w[p][1] == 0 else max(tstart(i) + w[i][0] for i in w[p][1:])

 

Нам осталось написать лишь одну строчку - определение времени завершения последнего процесса в нашей совокупности и его печать. Вот эта строчка:

 

print(max(tstart(p)+w[p][0] for p in w))

 

Приведем программу целиком:

 


def tstart(p): return 0 if w[p][1] == 0 else max(tstart(i) + w[i][0] for i in w[p][1:])
f=open('22.csv')
lines=[list(map(int,s.replace('"',' ').replace(';',' ').split())) for s in f]
w={line[0]:line[1:] for line in lines}
print(max(tstart(p)+w[p][0] for p in w))


Эти пять строчек - вот, собственно, и всё.


Более сложная задача.


А как можно решить последнюю из рассмотренных нами задач, где требовалось подсчитать суммарную продолжительность интервалов времени, когда выполнялось ровно три процесса?

 

Программа усложняется, но совсем немного. Нам надо дополнительно подсчиитать количество моментов времени с тремя процессами.

 

Приведем код программы:

 

def tstart(p): return 0 if w[p][1] == 0 else max(tstart(i) + w[i][0] for i in w[p][1:])

f=open('22.csv')

lines=[list(map(int,s.replace('"',' ').replace(';',' ').split())) for s in f]

w={line[0]:line[1:] for line in lines}

final=max(tstart(p)+w[p][0] for p in w)

kolp=[sum(1 if tstart(p) <= time < (tstart(p)+w[p][0]) else 0 for p in w) for time in range(final+1)]

print(kolp.count(3))


Нам пришлось построить список kolp, в котором элемент kolp[0] - это количество процессов, выполняемых в момент времени 0, kolp[1] - их количество в момент 1 и т.д. Перед этим мы определяем общее время выполнения всех процессов и запоминаем его в переменной final. Значение range(final+1) гарантирует, что мы проведем расчет для всех моментов времени, пока идет выполнение процессов. Затем мы печатаем количество миллисекундных интервалов, когда количество процессов равно трем.


(c) Ю.Д.Красильников, 2023 г.

Комментарии

Популярные сообщения из этого блога

Задача 9 (Excel) в 2023 г.

Питон и таблицы истинности

Задача 1 ЕГЭ по информатике - решаем на Питоне