В прошлых статьях мы не только разобрались с работой модели OSI, но и научились пользоваться функциями модуля ipaddress в Python. Теперь можем смело переходить к разбору алгоритмов решения 10 задания ЕГЭ по информатике.
Данное задание направлено на проверку умений определять параметры по заданному IP-адресу и маске подсети, рассчитывать количество возможных адресов в сети, а также на применение побитовых операций для работы с IP-адресами и масками.
Работать в данном задании предстоит всего с тремя значениями: адрес сети, адрес узла и маска подсети. Два из них даются в условии, а одно требуется найти. Задания 13 можно разделить на 7 типов, в зависимости от данных значений:
Даны адрес узла и маска. Чаще всего в таких заданиях требуется найти наибольший адрес сети, который может быть назначен компьютеру;
Даны два адреса узлов, находящихся в одной сети. Требуется найти маску (наибольшее/наименьшее количество единиц/нулей в маске) или количество адресов в этой сети;
Даны адрес узла и адрес сети. Обычно требуется найти маску подсети (наибольшее/наименьшее количество единиц/нулей);
Даны адрес сети и маска. Требуется найти количество адресов в сети, удовлетворяющих условию, например, количество нулей/единиц в IP-адресе кратно какому-то числу и т.д.;
Даны маска и адрес узла с неизвестным байтом. Требуется найти значение отсутствующего байта адреса узла, при котором будет выполняться условие (количество единиц/нулей в левых двух байтах меньше/больше количества в правых двух байтах);
Даны маска и адрес сети с неизвестным байтом. Требуется найти значение отсутствующего байта адреса сети, при котором будет выполняться условие (количество единиц/нулей в левых двух байтах меньше/больше количества в правых двух байтах);
Даны адрес сети и маска с неизвестным байтом. Требуется найти значение отсутствующего байта маски, при котором будет выполняться условие (количество единиц/нулей в левых двух байтах меньше/больше количества в правых двух байтах);
Типов слишком много для одной статьи, так что мы разобьём их разбор на три части. В данной статье рассмотрим первые 2 типа заданий, в следующей поговорим про ручные и программные методы решения 3-4 типов, а напоследок разберём все задания с неизвестным байтом.
Алгоритм решения
Решать 10 задания мы будем по большей части программными методами с помощью встроенного в Python модуля ipaddress. Если вы еще не умеете работать с данным модулем, то настоятельно рекомендуем сначала ознакомиться с прошлой статьёй.
Во всех формулировках заданий далее мы будем опускать общую часть и приводить только основную информацию, нужную для решения.
Тип 1
Начнём с самого простого типа, задания которого удивительно часто стали попадаться на экзамене.
Рассмотрим такую формулировку:
Сеть задана IP-адресом одного из входящих в неё узлов 98.81.154.195 и сетевой маской 255.252.0.0.
Найдите наибольший в данной сети IP-адрес, который может быть назначен компьютеру.
В ответе укажите найденный IP-адрес без разделителей.
Например, если бы найденный адрес был равен 111.22.3.44, то в ответе следовало бы записать 11122344
Здесь буквально всё решение занимает лишь пару строчек. Сначала импортируем функцию ip_interface и создаём объект IP-интерфейса:
from ipaddress import ip_interface
net = ip_interface('98.81.154.195/255.252.0.0')
Вспоминаем, что последний (с индексом -1) адрес является широковещательным. Значит, нам нужно взять предпоследний (с индексом -2) и вывести на экран:
from ipaddress import ip_interface
net = ip_interface('98.81.154.195/255.252.0.0')
print(net.network[-2])
Мы получаем такой адрес: 98.83.255.254. Осталось лишь удалить из него точки и записать в ответ.
Пример 1
Рассмотрим задание немного посложнее:
Сеть задана IP-адресом одного из входящих неё узлов 190.202.83.62 и сетевой маской 255.255.252.0.
Найдите наибольший IP-адрес в данной сети, который может быть назначен компьютеру. В ответе укажите сумму числовых значений октетов найденного IP-адреса.
Например, если бы найденный адрес был равен 100.20.3.4, то в ответе следовало бы записать: 127.
Уже интереснее, здесь нужно не только найти адрес, но и просуммировать значения его октетов. С первой частью мы справимся без проблем:
from ipaddress import ip_interface
net = ip_interface('190.202.83.62/255.255.252.0')
last_ip = net.network[-2]
Теперь нужно как-то из переменной last_ip достать значения, разделённые точками. Сделаем это через списочное включение:
from ipaddress import ip_interface
net = ip_interface('190.202.83.62/255.255.252.0')
last_ip = net.network[-2]
octets = [int(octet) for octet in str(last_ip).split(".")]
Что у нас здесь происходит? Для начала мы приводим объект IP-адреса к строчному типу данных: str(last_ip). Затем разделяем строку по символу точки: str(last_ip).split(".").
Проходимся по полученному списку и каждую строку в нём приводим к целочисленному типу данных: int(octet) for octet in str(last_ip).split("."). В конце собираем все целые числа в один список, чтобы проще было просуммировать их.
Для любителей функции map — те же действия можно записать так:
octets = map(int, str(last_ip).split("."))
Останется лишь вывести на экран сумму чисел в списке octets:
from ipaddress import ip_interface
net = ip_interface('190.202.83.62/255.255.252.0')
last_ip = net.network[-2]
octets = [int(octet) for octet in str(last_ip).split(".")]
print(sum(octets))
Запускаем программу и получаем число 729, которое и запишем в ответ.
Пример 2
И для закрепления рассмотрим еще одно задание:
Сеть задана IP-адресом одного из входящих в неё узлов 191.128.66.83 и сетевой маской 255.192.0.0.
Найдите в данной сети наибольший IP-адрес, который может быть назначен компьютеру.
В ответе укажите найденный IP-адрес без разделителей.
Например, если бы найденный адрес был равен 111.22.3.44, то в ответе следовало бы записать: 11122344.
Затем в цикле будем перебирать возможные префиксы маски. Так как нам нужно наименьшее количество адресов, то следует начать со значения 30 и идти на убывание с шагом 1;
Строим сеть по первому IP-адресу (любому из двух);
Проверяем, попадает ли второй в эту же сеть;
Дополнительно проверяем, что оба адреса не служебные — не адреса сети и не широковещательные.
Перебираем префиксы от большего к меньшему. Чем больше префикс, тем меньше сеть, поэтому первое подходящее значение сразу даст минимальное количество адресов:
from ipaddress import ip_network, ip_address
ip1 = ip_address('127.0.1.65')
ip2 = ip_address('127.0.1.101')
for mask in range(30, -1, -1):
Строим сеть по первому адресу:
from ipaddress import ip_network, ip_address
ip1 = ip_address('127.0.1.65')
ip2 = ip_address('127.0.1.101')
for mask in range(30, -1, -1):
net = ip_network(f'{ip1}/{mask}', strict=False)
Теперь проверим условия:
ip2 входит в сеть net;
ip1 и ip2 — не служебные. Здесь удобно будет проверить принадлежность к net.hosts.
from ipaddress import ip_network, ip_address
ip1 = ip_address('127.0.1.65')
ip2 = ip_address('127.0.1.101')
for mask in range(30, -1, -1):
net = ip_network(f'{ip1}/{mask}', strict=False)
if (ip2 in net and ip1 not in (net.network_address,
net.broadcast_address)
and ip2 not in (net.network_address,
net.broadcast_address)):
Как только выполнится условие — выводим количество адресов в этой сети и завершаем цикл:
from ipaddress import ip_network, ip_address
ip1 = ip_address('127.0.1.65')
ip2 = ip_address('127.0.1.101')
for mask in range(30, -1, -1):
net = ip_network(f'{ip1}/{mask}', strict=False)
if (ip2 in net and ip1 not in (net.network_address,
net.broadcast_address)
and ip2 not in (net.network_address,
net.broadcast_address)):
print(net.num_addresses)
break
Здесь требуется количество всех адресов, так что никаких «-2» мы не делаем. Запускаем программу и видим число 64. Его и запишем в ответ.
Пример 1
Теперь рассмотрим такое задание:
Два узла, находящиеся в одной сети, имеют IP-адреса 192.168.190.12 и 192.168.184.0.
Укажите наибольшее возможное количество единиц в маске этой сети.
Сразу вспоминаем, что количество единиц в маске — это как раз длина префикса, которую мы и перебираем в цикле. Значит, теперь нужно будет вывести на экран значение переменной mask. В остальном код практически без изменений:
from ipaddress import ip_network, ip_address
ip1 = ip_address('192.168.190.12')
ip2 = ip_address('192.168.184.0')
for mask in range(30, -1, -1):
net = ip_network(f'{ip1}/{mask}', strict=False)
if (ip2 in net and ip1 not in (net.network_address,
net.broadcast_address)
and ip2 not in (net.network_address,
net.broadcast_address)):
print(mask)
break
Запускаем его и получаем число 20, которое и будет ответом на это задание.
Пример 2
Напоследок решим задание посложнее:
Два узла, находящиеся в разных подсетях, имеют IP-адреса 192.168.34.17 и 192.168.45.91. Известно, что в масках обеих подсетей содержится одинаковое количество единиц.
Определите наименьшее возможное количество единиц в масках этих подсетей.
Первые два байта совпадают, поэтому можно начать с /16. Идём к большим префиксам: первое значение, при котором сети станут разными, будет минимальным ответом.
from ipaddress import ip_network, ip_address
ip1 = ip_address('192.168.34.17')
ip2 = ip_address('192.168.45.91')
for mask in range(16, 31):
Теперь строим сети с разными узлами, но одинаковой длиной префикса (или маской):
from ipaddress import ip_network, ip_address
ip1 = ip_address('192.168.34.17')
ip2 = ip_address('192.168.45.91')
for mask in range(16, 31):
net_1 = ip_network(f'{ip1}/{mask}', strict=False)
net_2 = ip_network(f'{ip2}/{mask}', strict=False)
Что нам нужно проверить здесь? Во-первых, что сети разные, то есть не равны друг другу. Во-вторых, что первый IP-адрес не является служебным и входит в первую сеть. Аналогично и со вторым:
from ipaddress import ip_network, ip_address
ip1 = ip_address('192.168.34.17')
ip2 = ip_address('192.168.45.91')
for mask in range(16, 31):
net_1 = ip_network(f'{ip1}/{mask}', strict=False)
net_2 = ip_network(f'{ip2}/{mask}', strict=False)
if (net_1 != net_2 and
ip1 not in (net_1.network_address, net_1.broadcast_address) and
ip2 not in (net_2.network_address, net_2.broadcast_address)):
Нам нужно наименьшее количество единиц в маске, следовательно и наименьшая длина префикса. Тогда, как только выполнятся все условия, выводим текущее значение переменной mask на экран и завершаем цикл:
from ipaddress import ip_network, ip_address
ip1 = ip_address('192.168.34.17')
ip2 = ip_address('192.168.45.91')
for mask in range(16, 31):
net_1 = ip_network(f'{ip1}/{mask}', strict=False)
net_2 = ip_network(f'{ip2}/{mask}', strict=False)
if (net_1 != net_2 and
ip1 not in (net_1.network_address, net_1.broadcast_address) and
ip2 not in (net_2.network_address, net_2.broadcast_address)):
print(mask)
break
Готово, теперь запускаем код и радуемся ответу на это задание — числу 21.
Итоги
Первые два типа задания 10 сводятся к перебору длины префикса. На каждом шаге строятся сети для адресов из условия, после чего проверяется нужное отношение между ними.
Шаблон решения
from ipaddress import ip_address, ip_network
ip1 = ip_address('*1')
ip2 = ip_address('*2')
for prefix in *3:
net1 = ip_network(f'{ip1}/{prefix}', strict=False)
net2 = ip_network(f'{ip2}/{prefix}', strict=False)
if *4:
print(*5)
break
Здесь:
*1 и *2 — IP-адреса,
*3 — диапазон префиксов и направление перебора,
*4 — условие одной или разных сетей,
*5 — требуемая величина: префикс, адрес сети или значение маски.
В следующей статье разберём типы 3 и 4: восстановим характеристики маски по адресу сети и научимся считать адреса с заданным свойством.
Разберём эту тему на бесплатном пробном занятии
Преподаватель покажет, как решать такие задачи, и подскажет, с чего начать подготовку. Группы до 8 человек, очно в Калининграде и онлайн.
Мы используем файлы cookie. Необходимые обеспечивают работу сайта: корзину и вход в личный кабинет. С вашего согласия мы также используем сервис Яндекс Метрика, чтобы анализировать посещаемость и улучшать сайт.
Подробнее — в политике конфиденциальности.