Академический Документы
Профессиональный Документы
Культура Документы
высшего образования
Научный руководитель
Руководитель Департамента
Москва 2022
ОГЛАВЛЕНИЕ
ОГЛАВЛЕНИЕ ............................................................................................ 2
ВВЕДЕНИЕ .................................................................................................. 3
3. Поиск решения....................................................................................... 32
Заключение ................................................................................................. 38
ПРИЛОЖЕНИЯ ........................................................................................ 40
2
ВВЕДЕНИЕ
• Логистика закупок
• Сбытовая логистика
• Транспортная логистика
• Таможенная логистика
• Логистика запасов
• Складская логистика
• Информационная логистика
• Комплексная логистика
3
Не смотря на широкое функциональное разнообразие логистики, в
каждой из них можно выделить четыре фундаментальные задачи –
планирование, оптимизация, управление и контроль. Естественно, каждая из
них также имеет свои специфические задачи.
4
1. МОДЕЛИРОВАНИЕ СЕТЕВЫХ СТРУКТУР
5
Формулируются математические законы, связывающие объекты
системы. Эти законы записываются в виде некоторых функциональных
соотношений (алгебраических, дифференциальных и т.п.).
6
остаются непознанными. В этом плане логистический процесс остается для
экспериментатора «черным ящиком».
7
Имитационные модели позволяют достаточно просто учитывать случайные
воздействия и другие факторы, которые создают трудности при
аналитическом исследовании.
8
Описание достоинств и недостатков имитационного моделирования
можно завершить словами Р. Шеннона: «Разработка и применение
имитационных моделей в большей степени искусство, чем наука.
Следовательно, успех или неудача в большей степени зависит не от метода, а
от того, как он применяется».
• Автоперевозки
• Морские перевозки
• Железнодорожные перевозки грузов
• Мультимодальные перевозки
• Контейнерные перевозки
• Международные перевозки грузов
• Перевозка опасных грузов
9
• Рефрижераторные перевозки
• Перевозка негабаритных грузов
• Перевозка сборных грузов и др.
10
Для поиска решения описанных выше проблем необходим инструмент
позволяющий тщательно исследовать транспортно-логистическую систему.
Под это определение отлично подходят методы моделирования, описанные в
предыдущем параграфе. Самым простым решением может показаться
использование одного из готовых программных комплексов имитационного
моделирования. Рассмотрим более подробно наиболее популярные
программы в области имитационного моделирования в логистике.
11
Другой программой, которая разработана российскими программистами
для имитационного моделирования в логистике, является Дорожный
менеджер. Она узко специализирована для моделирования транспортных
потоков улично-дорожной сети города, региона и страны в целом. Она
предоставляет функционал, позволяющий увидеть обстановку на дороге,
учитывая факторы, влияющие на скорость и плотность транспортного потока.
На рис. 2 изображен процесс моделирования транспортных потоков в городе,
а именно определения загруженности дорог при задании минимальной и
максимальной скорости движения автотранспорта за определенный
промежуток времени.
12
Рисунок 3. Моделирование цепи поставок в программе «Supply Chain
Guru»
13
• Для того чтобы построить модель нужен глубокий и детальный
анализ, так как процессы в логистической системе имеют
вероятностный характер и поддаются моделированию только при
определенных условиях.
• Закономерности определяющие взаимоотношения элементов
моделируемой логистической системы остаются неизвестными,
даже при успешном подборе исходных параметров.
14
𝐺 = {𝑉, 𝐸}
∀𝑣𝑖 , 𝑣𝑗 ∈ 𝑉, 𝑣𝑖 ≠ 𝑣𝑗 ∃ 𝑔 ⊂ 𝐺 ∶ 𝑔𝑜 = 𝑣𝑖 , 𝑔𝑛 = 𝑣𝑗
15
• Полнота графа определяет наличие ребра, соединяющего любые
две вершины, то есть
∀𝑣𝑖 , 𝑣𝑗 ∈ 𝑉, 𝑣𝑖 ≠ 𝑣𝑗 ∃ 𝑒𝑖𝑗 ∈ 𝐸
• Степень вершины графа – количество вершин, с которым
выбранная вершина связана ребрами, другими словами –
количество инцидентных вершин. Вершины со степенью равной
нулю, называются изолированными, единице – висячими.
16
• Поиск максимального потока, применяющаяся на транспортных
сетях. Транспортная сеть – ориентированный граф, в котором для
каждого ребра 𝑒𝑢𝑣 определена пропускная способность 𝑐(𝑢, 𝑣) ≥
0, а также определены точки источника 𝑠 и стока 𝑡, при этом
любая вершина лежит на пути из 𝑠 в 𝑡. Поток сети - функция 𝑓 вида
𝑉 × 𝑉 → ℝ имеющая свойства:
a. ∀𝑢, 𝑣 ∈ 𝑉, 𝑓(𝑢, 𝑣) ≤ 𝑐(𝑢, 𝑣)
b. 𝑓(𝑢, 𝑣) = −𝑓(𝑣, 𝑢)
𝑉
c. ∀𝑣 ∈ 𝑉, 𝑣 ≠ 𝑠, 𝑣 ≠ 𝑡 ∑𝑤∈𝑉 f(u, w) = 0
17
вершина 𝑎. Требуется найти кратчайшие пути до всех остальных вершин этого
графа.
18
для работы с сетевыми структурами. Определен способ построения модели
транспортно-логистической системы на основе графа.
19
2. ЖЕЛЕЗНОДОРОЖНЫЕ ЛОГИСТИЧЕСКИЕ СИСТЕМЫ
20
В выбранный набор данных входит информация о железнодорожных
путях в 165 странах, включая США, Россию и все страны Европы, то есть
более чем 83% мировых железнодорожных систем.
21
Рисунок 5. Функция, преобразующая строку из поля shape в
последовательность точек.
22
Рисунок 6. Функция преобразования данных в граф.
23
Рисунок 8. Компоненты графа ж/д путей.
24
стран. Как видно, большая часть железнодорожных систем практически не
имеет несвязных компонентов. Это может говорить о высоком уровне
интереса к железнодорожному транспорту со стороны государства и высоком
уровне контроля над ним, что дало железнодорожному транспорту
возможность расшириться до масштабов страны.
25
• Транспортные издержки.
• Максимальная скорость поезда.
26
Не стоит применять такую логику напрямую, все же качество составов
и издержки в действительности не всегда распределяются по такому закону.
Для более корректного расчета параметров в общедоступных источниках была
найдена информация о максимальных скоростях поездов в каждой из стран,
входящих в исследуемую железнодорожную систему. Скорость передвижения
по каждому из ребер и транспортные издержки, будут рассчитаны на основе
найденных данных с учетом описанной выше тенденции и случайного
возмущения. Случайное возмущение сгенерировано с помощью модуля stats
библиотеки scipy.
27
Рисунок 10. Карта центральности ребер графа.
28
2.3. Постановка многокритериальной задачи оптимизации
Система ограничений:
𝑣𝑠 ≠ 𝑣𝑒
|𝑉(𝐺)| ≠ 0, |𝐸(𝐺)| ≠ 0
29
Построим новый граф, вершинами которого будут являться страны,
представленные подграфами, и найдем в нем путь из страны, в которой
находится точка начала в страну с точкой конца пути. Для этого используем
алгоритм Дейкстры поиска кратчайшего пути.
30
По результатам проведенного визуального анализа, можно сделать вывод
железнодорожные сети европейского континента связаны между собой, и
составляют одну крупномасштабную систему. Не смотря на различные
препятствия функционирования этой системы как единого целого, ее
моделирование может стать более актуальным в обозримом будущем под
действием всеобщего тренда глобализации.
31
3. ПОИСК РЕШЕНИЯ
32
Рисунок 13. Интерфейс менеджера железнодорожных сетей.
33
Рисунок 14. Интерфейс сущности Point.
34
Рисунок 15. Разработанное программное обеспечение.
1
𝑓(𝑠𝑝𝑒𝑒𝑑, 𝑑𝑖𝑠𝑡𝑎𝑛𝑐𝑒) = 𝑑𝑖𝑠𝑡𝑎𝑛𝑐𝑒 +
2 ∗ 𝑠𝑝𝑒𝑒𝑑
1 1
𝑓(𝑠𝑝𝑒𝑒𝑑, 𝑑𝑖𝑠𝑡𝑎𝑛𝑐𝑒, 𝑐𝑜𝑠𝑡, 𝑐𝑒𝑛𝑡𝑟𝑎𝑙𝑖𝑡𝑦) = + + 𝑑𝑖𝑠𝑡𝑎𝑛𝑐𝑒 + 𝑐𝑜𝑠𝑡
𝑠𝑝𝑒𝑒𝑑 𝑐𝑒𝑛𝑡𝑟𝑎𝑙𝑖𝑡𝑦
35
Центральность как таковая не влияет на привлекательность того или
иного ребра, но здесь она учтена как характеристика, линейно зависимая от
качества состава. Не смотря на линейную зависимость транспортных
издержек от центральности, они представлены отдельными членами, для учета
случайного возмущения. Так как нашей целью является максимизировать
скорость и качество состава, а алгоритм Дейкстры склонен выбирать ребра с
меньшим весом члены 𝑠𝑝𝑒𝑒𝑑 и 𝑐𝑒𝑛𝑡𝑟𝑎𝑙𝑖𝑡𝑦 находятся в знаменателе.
36
примерно в два раза лучше по времени, чем стандартный алгоритм Дейкстры
(оранжевый график), что является хорошим результатом.
37
ЗАКЛЮЧЕНИЕ
38
СПИСОК ИСТОЧНИКОВ
39
ПРИЛОЖЕНИЯ
40
Рисунок 19. Изображение ПО железнодорожных станции Германии.
railwaynet.py
import networkx as nx
import pandas as pd
import matplotlib
import contextlib
import pycountry
import pickle
import random
import bz2
matplotlib.use('Qt5Agg')
# region Constants
41
COLORS = [
'crimson', 'cyan',
'darkblue', 'darkcyan', 'darkgoldenrod',
'darkgray', 'darkgreen', 'darkkhaki',
'darkmagenta', 'darkolivegreen', 'darkorange',
'darkorchid', 'darkred', 'darksalmon',
'darkseagreen', 'darkslateblue', 'darkslategray',
'darkturquoise', 'darkviolet', 'deeppink',
'deepskyblue', 'dimgray', 'dodgerblue',
'firebrick', 'floralwhite', 'forestgreen',
'fuchsia', 'gainsboro', 'ghostwhite',
'gold', 'goldenrod', 'gray',
'green', 'greenyellow', 'honeydew',
'hotpink', 'indianred', 'indigo',
'ivory', 'khaki', 'lavender',
'lavenderblush', 'lawngreen', 'lemonchiffon',
'lightblue', 'lightcoral', 'lightcyan',
'lightgoldenrodyellow', 'lightgreen', 'lightgray',
'lightpink', 'lightsalmon', 'lightseagreen',
'lightskyblue', 'lightslategray', 'lightsteelblue',
'lightyellow', 'lime', 'limegreen',
'linen', 'magenta', 'maroon',
'mediumaquamarine', 'mediumblue', 'mediumorchid',
'mediumpurple', 'mediumseagreen', 'mediumslateblue',
'mediumspringgreen', 'mediumturquoise', 'mediumvioletred',
'midnightblue', 'mintcream', 'mistyrose',
'moccasin', 'navajowhite', 'navy',
'oldlace', 'olive', 'olivedrab',
'orange', 'orangered', 'orchid',
'palegoldenrod', 'palegreen', 'paleturquoise',
'palevioletred', 'papayawhip', 'peachpuff',
'peru', 'pink', 'plum',
'powderblue', 'purple', 'red',
'rosybrown', 'royalblue', 'saddlebrown',
'salmon', 'sandybrown', 'seagreen',
'seashell', 'sienna', 'silver',
'skyblue', 'slateblue', 'slategray',
'snow', 'springgreen', 'steelblue',
'tan', 'teal', 'thistle',
'tomato', 'turquoise', 'violet',
'wheat', 'white', 'whitesmoke',
'yellow', 'yellowgreen'
]
CONSOLE = Console()
PROGRESS_BAR_WIDTH = 100
# endregion
# region Types
@dataclass
class RailwayNetInfo:
42
nodes: int
edges: int
components: int
biggest_component_part: float
class RailwayNet(GeoGraph):
# region Construction
self.countries = set()
centrality=a.distance(countries_data[iso3]['capital']),
speed=(80 if countries_data is None else
countries_data[iso3]['speed'])+ NORM_RANDOM() * 5,
iso3=iso3
)
# endregion
# region PublicMethods
@lru_cache(maxsize=None)
def get_biggest_component(self):
return self.subgraph(max(nx.connected_components(self), key=len))
def get_points_dataframe(self):
points_dataframe = pd.DataFrame(
{
'lat': [node.coord_reverse[0] for node in self.nodes],
'lon': [node.coord_reverse[1] for node in self.nodes],
'iso3': [self.nodes[node]['iso3'] for node in self.nodes]
}
)
return points_dataframe
43
plt.figure(figsize=size)
edge_color=[COLORS[ord(self[u][v]['iso3'][0]) % len(COLORS)] for u, v in
self.edges]
self.draw(edge_color=edge_color, node_size=0)
plt.figure(figsize=size)
attr_list = [self[u][v][attr] for u,v in self.edges]
min_attr = min(attr_list)
ratio_derivative = max(attr_list) - min_attr
self.draw(edge_color=[green2red((attr - min_attr)/ratio_derivative) for
attr in attr_list], node_size=0)
if verbose:
res = "\n"
res += f" nodes: {nodes}\n"
res += f" edges: {edges}\n"
res += f" components: {components}\n"
res += f" biggest component part: {biggest_component_part:.6}\n"
print(res)
return RailwayNetInfo(
nodes=nodes,
edges=edges,
components=components,
biggest_component_part=biggest_component_part
)
# endregion
# region ServiceMethods
@staticmethod
def __get_coordinates_from_string(coordinates_string: str) ->
tuple[list[float], list[float]]:
""" function which formats (lat, long) data nicely """
tuple_string = coordinates_string[15:]
44
coordinate_strings_list = tuple_string.lstrip("( ").rstrip(")
").split(',')
lon = []
lat = []
# endregion
class PathEdgePoint:
def __init__(self, node: Point, iso3: str):
self.node = node
self.iso3 = iso3
class CountryNet(GeoGraph):
# region Construction
distance=country_attributes['capital'].distance(countries_data[neighbour]['capital
']),
speed=(country_attributes['speed'] +
countries_data[neighbour]['speed']) / 2,
)
# endregion
class RailwayNetManager(dict):
# region Constants
CACHED_LIST_OF_NETS_PATH = "./cached/graphs_list.bz2"
CACHED_FULL_GRAPH_PATH = "./cached/graph_full.bz2"
# endregion
# region Construction
45
def __init__(self, graph_data: pd.DataFrame, countries_data: pd.DataFrame):
self.graph_data = graph_data
self.countries_data =
RailwayNetManager.__countries_dataframe2dict(countries_data)
self.start_node = None
self.finish_node = None
save_file_to_cache(railway_nets,
RailwayNetManager.CACHED_LIST_OF_NETS_PATH)
super(RailwayNetManager, self).__init__(zip(self.countries_sorted,
railway_nets))
self.full_graph = None
self.full_graph = self.__get_full()
self.countries_graph = CountryNet(self.countries_data)
# endregion
# region PublicMethods
46
components.append(info.components)
biggest_component_part.append(info.biggest_component_part)
description = pd.DataFrame(
{
"country" : countries,
"nodes" : nodes,
"edges" : edges,
"components" : components,
"biggest_component_part" : biggest_component_part
}
)
with open("railwaynet_description.csv", "w") as f:
description.to_csv(f)
return description
def get_random(self):
countries_number = random.randrange(1, len(self.countries_sorted + 1))
countries_list = []
while len(countries_list) != countries_number:
country_chosen = random.choice(self.countries_sorted)
if country_chosen not in countries_list:
countries_list.append(country_chosen)
return self.get_nets(countries_list)
if recalculate_centrality:
self.__calculate_centrality(res)
return res
47
self.start_node = PathEdgePoint(node, iso3)
return True
elif self.finish_node is None:
self.finish_node = PathEdgePoint(node, iso3)
return True
return False
return False
def country_func(u,v,e_attrs):
return e_attrs['distance'] + 2 * e_attrs['speed']
def func(u,v,e_attrs):
return e_attrs['distance'] + 2 * e_attrs['speed'] + 0.5 *
e_attrs['centrality']
if self.start_node.iso3 == self.finish_node.iso3:
o_paths[1] = nx.dijkstra_path(
self.full_graph.get_biggest_component(),
self.start_node.node,
self.finish_node.node,
func)
else:
frm = None
to = None
for n in self.countries_graph.nodes:
if self.countries_graph.nodes[n]['iso3'] == self.start_node.iso3:
frm = n
if self.countries_graph.nodes[n]['iso3'] == self.finish_node.iso3:
to = n
o_paths[0] = cpath = nx.dijkstra_path(self.countries_graph, frm, to,
country_func)
countries_in_path = []
for p in cpath:
for n in self.countries_graph.nodes:
if n == p:
countries_in_path.append(self.countries_graph.nodes[n]['iso3'])
g = self.get_nets(countries_in_path)
o_paths[1] = nx.dijkstra_path(
g,
self.start_node.node,
self.finish_node.node,
func)
# endregion
# region ServiceMethods
48
full_graph =
try_load_cached_file(RailwayNetManager.CACHED_FULL_GRAPH_PATH)
if full_graph is None:
full_graph = self.get_nets(self.countries_sorted,
recalculate_centrality=False)
save_file_to_cache(full_graph,
RailwayNetManager.CACHED_FULL_GRAPH_PATH)
return full_graph
self.countries_data[g.edges[edge]['iso3']]['neighbours']))
g.edges[edge]['cost'] = 1 / g.edges[edge]['centrality'] +
NORM_RANDOM()
@staticmethod
def __countries_dataframe2dict(countries_data: pd.DataFrame) -> dict:
countries_dict = dict()
for iso3 in countries_data.iso3:
country = countries_data[countries_data.iso3 == iso3].iloc[0]
countries_dict[iso3] = {
'speed' : country.MaximumTrainSpeed,
'capital' : Point(country.CapitalLatitude,
country.CapitalLongitude)
}
return countries_dict
# endregion
# endregion
# region Functions
speed_data_path="./data/train_speed.csv"
speed_data = pd.read_csv(speed_data_path, sep=',')
speed_data['iso3'] = speed_data.CountryName.apply(lambda name:
pycountry.countries.search_fuzzy(name)[0].alpha_3)
speed_data = speed_data.drop(columns=['CountryName'])
# merge countries
49
countries_data = speed_data.merge(capitals_data)
return data
# endregion
geograph.py
import networkx as nx
class Color:
# region construction
# endregion
class Point:
# region Construction
# endregion
50
# region PublicMethods
# endregion
# region OverloadMethods
def __hash__(self):
return hash(self.coord)
# endregion
class GeoGraph(nx.Graph):
# region Construction
def __init__(self):
super(GeoGraph, self).__init__()
# endregion
# region PublicMethods
# endregion
editor.py
class Editor:
def __init__(self, railway_net_manager: RailwayNetManager):
pg.init()
51
# init application screen
self.size = self.w, self.h = (1900, 900)
self.screen = pg.display.set_mode(self.size)
self.screen.fill((255, 240, 250))
pg.display.flip()
# set renderers
self.gui_surface = pg.Surface((1900, 50))
self.graph_surface = pg.Surface((1900, 850))
self.gui_renderer = GUIRenderer(self.gui_surface,
self.railway_net_manager.countries_sorted)
self.graph_renderer = GraphRenderer(
self.graph_surface,
self.railway_net_manager.full_graph.get_biggest_component(),
COLORS,
self.search_range
)
def find_path_routine(self):
self.railway_net_manager.find_path(self.current_paths)
self.graph_renderer.render()
animate = False
if self.current_paths[1] is not None:
if self.current_paths[0] is not None:
self.current_country = "full"
self.graph_renderer.update_graph(self.railway_net_manager.full_graph.get_biggest_c
omponent())
self.graph_renderer.render(animate=True)
self.graph_renderer.update_path(self.current_paths[1])
self.graph_renderer.render()
self.graph_renderer.update_graph(self.railway_net_manager.full_graph.get_biggest_c
omponent())
else:
52
self.graph_renderer.update_graph(self.railway_net_manager.get_net(result))
self.graph_renderer.render()
def run(self):
self.graph_renderer.render(animate=True)
self.path = None
self.running = True
while self.running:
for event in pg.event.get():
if event.type == pg.QUIT:
self.running = False
if event.type == pg.KEYDOWN:
if event.key == pg.K_ESCAPE:
self.running = False
self.manage_guirenderer_event(event)
if self.current_country != "full":
self.manage_graphrenderer_event(event)
self.gui_renderer.render()
self.screen.blit(self.gui_surface, (0,0))
self.screen.blit(self.graph_surface, (0,50))
pg.display.update()
graphrenderer.py
import pygame as pg
class GraphRenderer:
53
# region Construction
def __init__(
self,
surface: pg.Surface,
graph: RailwayNet,
colors: list[str],
search_range: int = 2,
bg_color: tuple[int, int, int] = (0,0,0)
):
self.surface = surface
self.size = self.w, self.h = self.surface.get_size()
self.bg_color = bg_color
self.colors = colors
self.graph = graph
self.points_data = None
self.path = None
self.path_points_data = None
self.search_tree = None
self.search_range = search_range
self.update_points_positions()
self.update_search_tree()
# endregion
# region PublicMethods
self.vertical_bounds = self.points_data.lat.max(),
self.points_data.lat.min()
self.horizontal_bounds = self.points_data.lon.min(),
self.points_data.lon.max()
self.vertical_span = self.vertical_bounds[1] - self.vertical_bounds[0]
self.horizontal_span = self.horizontal_bounds[1] -
self.horizontal_bounds[0]
self.points_data['x'] = self.points_data.lon.apply(
lambda lon : self.world2local(lon)
)
self.points_data['y'] = self.points_data.lat.apply(
lambda lat : self.world2local(lat, horizontal=False)
)
self.points_data['color'] = self.points_data.iso3.apply(
lambda iso3: self.colors[ord(iso3[0]) % len(self.colors)]
)
self.points_data['radius'] = [1] * self.points_data.shape[0]
54
def update_graph(self, graph: RailwayNet) -> None:
self.graph = graph
self.update_points_positions()
self.update_search_tree()
if self.path is not None:
self.update_path_points_positions()
def update_path_points_positions(self):
if self.path_points_data is None:
self.path_points_data = pd.DataFrame()
self.path_points_data['x'] = [self.world2local(point.coord[0]) for point
in self.path]
self.path_points_data['y'] = [self.world2local(point.coord[1],
horizontal=False) for point in self.path]
55
'red',
(self.path_points_data.x.iloc[i],
self.path_points_data.y.iloc[i]),
(self.path_points_data.x.iloc[i + 1],
self.path_points_data.y.iloc[i + 1]),
2
)
# endregion
# region ServiceMethods
# endregion
guirenderer.py
import pygame as pg
import pygame_gui as pgg
class GUIRenderer:
# region Construction
self.countries = countries
self.country_buttons = dict()
button_width = self.w / (len(self.countries) + 2) - 1
button_height = self.h
self.country_buttons = dict()
self.reset_button = pgg.elements.UIButton(
relative_rect=pg.Rect((0, 0), (button_width, button_height)),
text="R",
manager=self.manager)
self.country_buttons["full"] = pgg.elements.UIButton(
relative_rect=pg.Rect((button_width, 0), (button_width,
button_height)),
text="full",
manager=self.manager)
56
for i, country in enumerate(self.countries):
self.country_buttons[country] = \
pgg.elements.UIButton(
relative_rect=pg.Rect(((i + 2) * button_width, 0),
(button_width, button_height)),
text=country,
manager=self.manager)
# endregion
# region PublicMethods
# endregion
main.py
# region Main
editor = Editor(manager)
editor.run()
# endregion
if __name__ == "__main__":
main()
57