Скачиваний:
3
Добавлен:
03.06.2024
Размер:
143.04 Кб
Скачать

Цель работы: реализовать и проверить на тестовом примере специальный алгоритм на графе

Вариант № 15

На рисунке 1 представлен алгоритм согласно варианту.

Рисунок 1 – Специальный алгоритм

Ход работы

Cоздан и визуализирован взвешенный ориентированный двудольный граф (Рисунок 2).

Рисунок 2 – Граф

Листинг 1 – Создание и визуализация графа

# Визуализация двудольного графа

def visualize_bipartite_graph(G):

    pos = {}

    men = [node for node, data in G.nodes(data=True) if data['bipartite'] == 0]

    women = [node for node, data in G.nodes(data=True) if data['bipartite'] == 1]

    pos.update((node, (0, i)) for i, node in enumerate(men))  # x=0 для мужчин

    pos.update((node, (1, i)) for i, node in enumerate(women))  # x=1 для женщин

    plt.figure(figsize=(10, 8))

    nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=3000, font_size=10, font_weight='bold', arrows=True)

    edge_labels=dict([((u,v,),d['weight'])

             for u,v,d in G.edges(data=True)])

    nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, label_pos=0.3, font_size=10)

    plt.title("Двудольный граф предпочтений")

    plt.show()

# Создание двудольного графа

G = nx.DiGraph()

men_nodes = ['A1', 'A2', 'A3', 'A4', 'A5']

women_nodes = ['B1', 'B2', 'B3', 'B4', 'B5']

for man in men_nodes:

    G.add_node(man, bipartite=0)

for woman in women_nodes:

    G.add_node(woman, bipartite=1)

edges = [

    ('A1', 'B1', 3), ('A1', 'B2', 3),

    ('A2', 'B2', 4), ('A2', 'B3', 2),

    ('A3', 'B3', 3), ('A3', 'B4', 6),

    ('A4', 'B4', 2), ('A4', 'B5', 1),

    ('A5', 'B5', 3), ('A5', 'B1', 5),

    ('B1', 'A1', 8), ('B1', 'A5', 2),

    ('B2', 'A1', 2), ('B2', 'A2', 2),

    ('B3', 'A2', 3), ('B3', 'A3', 1),

    ('B4', 'A3', 5), ('B4', 'A4', 1),

    ('B5', 'A4', 2), ('B5', 'A5', 9)

]

for edge in edges:

    G.add_edge(edge[0], edge[1], weight=edge[2])

# Визуализация начального двудольного графа

visualize_bipartite_graph(G)

Задача о марьяже или задача о стабильных браках — математическая задача из области кооперативных игр. Требуется найти стабильные соответствия между элементами двух множеств, имеющих свои предпочтения. В более простой формулировке: составить брачные пары из женихов и невест таким образом, чтобы мужа из одной семьи и жену из другой не тянуло друг к другу сильнее, чем к своим законным супругам.

Для решения этой задачи используется алгоритм Гэйла-Шепли. Алгоритм Гейла-Шепли гарантирует нахождение стабильного паросочетания, при котором нет двух людей, которые предпочитают друг друга своим текущим партнерам.

Пошаговое выполнение алгоритма на созданном графе:

Шаг 1:

Свободные мужчины: A1, A2, A3, A4, A5.

A1 предложил B1, она свободна и приняла предложение.

Пары: A1-B1.

Шаг 2:

Свободные мужчины: A2, A3, A4, A5.

A2 предложил B2, она свободна и приняла предложение.

Пары: A1-B1, A2-B2.

Шаг 3:

Свободные мужчины: A3, A4, A5.

A3 предложил B4, она свободна и приняла предложение.

Пары: A1-B1, A2-B2, A3-B4.

Шаг 4:

Свободные мужчины: A4, A5.

A4 предложил B4, но она предпочитает своего текущего партнера A3. A4 делает следующее предложение.

Пары: A1-B1, A2-B2, A3-B4.

Шаг 5:

Свободные мужчины: A4, A5.

A4 предложил B5, она свободна и приняла предложение.

Пары: A1-B1, A2-B2, A3-B4, A4-B5.

Шаг 6:

Свободные мужчины: A5

A5 предложил B1, но она предпочитает своего текущего партнера A1. A5 делает следующее предложение.

Пары: A1-B1, A2-B2, A3-B4, A4-B5.

Шаг 7:

Свободные мужчины: A5.

A5 предложил B5, она предпочитает его текущему партнеру A4. A4 теперь свободен.

Пары: A1-B1, A2-B2, A3-B4, A5-B5.

Шаг 8:

Свободные мужчины: A4.

A4 предложил B5, но она предпочитает своего текущего партнера A5. A4 делает следующее предложение.

Пары: A1-B1, A2-B2, A3-B4, A5-B5.

Стабильные пары: A1-B1, A2-B2, A3-B4, A5-B5.

A4 больше не может сделать предложение и остается свободным.

Алгоритм был реализован средствами ЭВМ (Рисунок 3).

Рисунок 3 – Решение на Python

Листинг 2 – Функция алгоритма

def gale_shapley(graph, men, women):

    # Начальные параметры

    free_men = list(men)

    engaged = {}

    women_partner = {woman: None for woman in women}

    men_next_proposal = {man: 0 for man in men}

    men_prefs = {man: sorted(graph.neighbors(man), key=lambda x: -graph[man][x]['weight']) for man in men}

    women_prefs = {woman: sorted(graph.neighbors(woman), key=lambda x: -graph[woman][x]['weight']) for woman in women}

    step = 0

    while free_men:

        step += 1

        man = free_men[0]

        man_prefs = men_prefs[man]

        # Если мужчина сделал предложение всем женщинам в своем списке, удаляем его из свободных

        if men_next_proposal[man] >= len(man_prefs):

            free_men.remove(man)

            continue

        # Мужчина делает предложение следующей женщине в своем списке предпочтений

        woman = man_prefs[men_next_proposal[man]]

        print(f"Шаг {step}:")

        print(f"  Свободные мужчины: {free_men}")

        print(f"  Предпочтения мужчин:")

        for m, prefs in men_prefs.items():

            print(f"    {m}: {prefs} (следующий: {prefs[men_next_proposal[m]] if men_next_proposal[m] < len(prefs) else 'нет'})")

        print(f"  Предпочтения женщин:")

        for w, prefs in women_prefs.items():

            print(f"    {w}: {prefs}")

        if women_partner[woman] is None:

            # Если женщина свободна, они обручаются

            women_partner[woman] = man

            engaged[man] = woman

            free_men.remove(man)

            print(f"  {man} предложил {woman}, она свободна и приняла предложение.")

        else:

            # Если женщина уже обручена, она выбирает между текущим партнером и новым предложением

            current_partner = women_partner[woman]

            woman_prefs = women_prefs[woman]

            if woman_prefs.index(man) < woman_prefs.index(current_partner):

                # Женщина предпочитает нового партнера

                engaged.pop(current_partner)

                women_partner[woman] = man

                engaged[man] = woman

                free_men.remove(man)

                free_men.append(current_partner)

                print(f"  {man} предложил {woman}, она предпочитает его текущему партнеру {current_partner}. {current_partner} теперь свободен.")

            else:

                # Женщина отвергает новое предложение

                men_next_proposal[man] += 1

                print(f"  {man} предложил {woman}, но она предпочитает своего текущего партнера {current_partner}. {man} делает следующее предложение.")

        print("Пары:",engaged)

        print("\n")

       

    return engaged

Полный код программы представлен в Листинге 3.

Вывод: в ходе выполнения практической работы была решена задача о марьяже с помощью алгоритма Гейла-Шепли. Этот алгоритм был реализован средствами языка программирования Python, был осуществлен вывод изначального взвешенного ориентированного двудольного графа, применение алгоритма на граф и вывод получившихся стабильных пар. Алгоритм Гейла-Шепли используется для нахождения стабильного выбора.

Листинг 3 – Полный код программы

import matplotlib.pyplot as plt

import networkx as nx

def gale_shapley(graph, men, women):

    # Начальные параметры

    free_men = list(men)

    engaged = {}

    women_partner = {woman: None for woman in women}

    men_next_proposal = {man: 0 for man in men}

    men_prefs = {man: sorted(graph.neighbors(man), key=lambda x: -graph[man][x]['weight']) for man in men}

    women_prefs = {woman: sorted(graph.neighbors(woman), key=lambda x: -graph[woman][x]['weight']) for woman in women}

    step = 0

    while free_men:

        step += 1

        man = free_men[0]

        man_prefs = men_prefs[man]

        # Если мужчина сделал предложение всем женщинам в своем списке, удаляем его из свободных

        if men_next_proposal[man] >= len(man_prefs):

            free_men.remove(man)

            continue

        # Мужчина делает предложение следующей женщине в своем списке предпочтений

        woman = man_prefs[men_next_proposal[man]]

        print(f"Шаг {step}:")

        print(f"  Свободные мужчины: {free_men}")

        print(f"  Предпочтения мужчин:")

        for m, prefs in men_prefs.items():

            print(f"    {m}: {prefs} (следующий: {prefs[men_next_proposal[m]] if men_next_proposal[m] < len(prefs) else 'нет'})")

        print(f"  Предпочтения женщин:")

        for w, prefs in women_prefs.items():

            print(f"    {w}: {prefs}")

        if women_partner[woman] is None:

            # Если женщина свободна, они обручаются

            women_partner[woman] = man

            engaged[man] = woman

            free_men.remove(man)

            print(f"  {man} предложил {woman}, она свободна и приняла предложение.")

        else:

            # Если женщина уже обручена, она выбирает между текущим партнером и новым предложением

            current_partner = women_partner[woman]

            woman_prefs = women_prefs[woman]

            if woman_prefs.index(man) < woman_prefs.index(current_partner):

                # Женщина предпочитает нового партнера

                engaged.pop(current_partner)

                women_partner[woman] = man

                engaged[man] = woman

                free_men.remove(man)

                free_men.append(current_partner)

                print(f"  {man} предложил {woman}, она предпочитает его текущему партнеру {current_partner}. {current_partner} теперь свободен.")

            else:

                # Женщина отвергает новое предложение

                men_next_proposal[man] += 1

                print(f"  {man} предложил {woman}, но она предпочитает своего текущего партнера {current_partner}. {man} делает следующее предложение.")

        print("Пары:",engaged)

        print("\n")

       

    return engaged

# Визуализация двудольного графа

def visualize_bipartite_graph(G):

    pos = {}

    men = [node for node, data in G.nodes(data=True) if data['bipartite'] == 0]

    women = [node for node, data in G.nodes(data=True) if data['bipartite'] == 1]

    pos.update((node, (0, i)) for i, node in enumerate(men))  # x=0 для мужчин

    pos.update((node, (1, i)) for i, node in enumerate(women))  # x=1 для женщин

    plt.figure(figsize=(10, 8))

    nx.draw(G, pos, with_labels=True, node_color='lightblue', node_size=3000, font_size=10, font_weight='bold', arrows=True)

    edge_labels=dict([((u,v,),d['weight'])

             for u,v,d in G.edges(data=True)])

    nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, label_pos=0.3, font_size=10)

    plt.title("Двудольный граф предпочтений")

    plt.show()

# Создание двудольного графа

G = nx.DiGraph()

men_nodes = ['A1', 'A2', 'A3', 'A4', 'A5']

women_nodes = ['B1', 'B2', 'B3', 'B4', 'B5']

for man in men_nodes:

    G.add_node(man, bipartite=0)

for woman in women_nodes:

    G.add_node(woman, bipartite=1)

edges = [

    ('A1', 'B1', 3), ('A1', 'B2', 3),

    ('A2', 'B2', 4), ('A2', 'B3', 2),

    ('A3', 'B3', 3), ('A3', 'B4', 6),

    ('A4', 'B4', 2), ('A4', 'B5', 1),

    ('A5', 'B5', 3), ('A5', 'B1', 5),

    ('B1', 'A1', 8), ('B1', 'A5', 2),

    ('B2', 'A1', 2), ('B2', 'A2', 2),

    ('B3', 'A2', 3), ('B3', 'A3', 1),

    ('B4', 'A3', 5), ('B4', 'A4', 1),

    ('B5', 'A4', 2), ('B5', 'A5', 9)

]

for edge in edges:

    G.add_edge(edge[0], edge[1], weight=edge[2])

# Визуализация начального двудольного графа

visualize_bipartite_graph(G)

# Выполнение алгоритма Гейла-Шепли

result = gale_shapley(G, men_nodes, women_nodes)

print("\nСтабильные пары:", result)

# Визуализация стабильного паросочетания

def visualize_matching(G, matching):

    matching_graph = nx.DiGraph()

    for man in men_nodes:

        matching_graph.add_node(man, bipartite=0)

    for woman in women_nodes:

        matching_graph.add_node(woman, bipartite=1)

    for man, woman in matching.items():

        matching_graph.add_edge(man, woman)

    pos = {}

    pos.update((node, (0, i)) for i, node in enumerate(men_nodes))  # x=0 для мужчин

    pos.update((node, (1, i)) for i, node in enumerate(women_nodes))  # x=1 для женщин

    plt.figure(figsize=(8, 6))

    nx.draw(matching_graph, pos, with_labels=True, node_color='lightgreen', node_size=3000, font_size=10, font_weight='bold', arrows=True)

    edge_labels = {(man, woman): f"({man} -> {woman})" for man, woman in matching.items()}

    nx.draw_networkx_edge_labels(matching_graph, pos, edge_labels=edge_labels, font_color='red')

    plt.title("Стабильное паросочетание")

    plt.show()

visualize_matching(G, result)

Соседние файлы в папке Шелест ЛР