Алгоритм упрощения взвешенного направленного графа долгов

Я использовал небольшой скрипт python, который я написал, чтобы управлять долгами среди моих соседей по комнате. Это работает, но есть некоторые недостающие функции, одна из которых-упрощение излишне сложных долговых структур. Например, если следующий взвешенный ориентированный график представляет некоторых людей, а стрелки представляют долги между ними (Алиса должна Бобу $ 20 и Чарли $5, Боб должен Чарли $10 и т. д.):

graph1

понятно, что этот график должен быть упрощен до следующий график:

graph1-simplified

нет смысла в $10, пробираясь от Алисы к Бобу, а затем от Боба к Чарли, если Алиса может просто дать его Чарли напрямую.

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

  1. ни один узел не имеет ребер, указывающих как внутрь, так и наружу (нет бесполезных денег, переходящих из рук в руки)
  2. все узлы имеют тот же "поток" через них, что и в исходном графике (он идентичен с точки зрения того, где заканчиваются деньги).

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

  • Боб: +10
  • Алиса: -25
  • Чарли: +15

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

Это:

graph2

должна быть упрощена в этот:

graph2-simplified

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

3 ответов


простой алгоритм

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

  • начальное состояние: Дебет: (A: 25), кредит: (B: 15, C: 10)
  • первая транзакция, A: 15 - > B: Дебет: (A: 10), Кредит: (C: 10)
  • вторая транзакция, A: 10 - > C: Дебет: (), Кредит: ()

транзакции определяют ребра вашего графика. Для n вовлеченных лиц будет не более n-1 транзакций=ребра. В начале общая длина обоих списков равна n. На каждом шаге по крайней мере один из списков (дебет/кредит) становится короче на один, а в последнем оба списка исчезают сразу.

проблема в том, что, в общем, этот график не должен быть похож на исходный график, который, как я понимаю ваше намерение, является требованием. (Есть это? Бывают случаи, когда оптимальное решение состоит из добавления новых ребер. Представьте себе, что A должен B и B должен C ту же сумму денег, A должен платить C напрямую, но этого края нет в графике долгов.)

меньше сделок

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

редактировать: спасибо j_random_hacker за указание на то, что решение с менее чем n-1 ребрами возможно, если есть группа лиц, общая задолженность которых соответствует кредиту другой группы лиц: тогда задача может быть разбита на две подзадачи с общей стоимостью N-2 ребер для графика транзакций. К сожалению,подмножество сумма проблема NP-трудно.

проблема потока?

возможно, это также может быть преобразован в мин-стоимость проблема потока. Если вы хотите просто упростить исходный график, вы строите на нем поток, граничные емкости-это исходные суммы дебета/кредита. Дебиторы служат узлами притока (через разъем узел, обслуживающий всех дебиторов с ребрами емкости, равными их общему долгу), кредиторы используются в качестве узлов оттока (с аналогичным узлом соединителя).

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


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

Верхоеф, Т. (2004). Эффективное урегулирование многочисленных долгов: приглашение к вычислительной науке. Информатика в образовании, 3(1), 105-126.


Я действительно столкнулся с этой проблемой в точно такой же ситуации, как и вы:)

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

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

Если человек те, кто должен деньги, не могут заплатить все сразу, они могут просто дать Стиву то, что они могут себе позволить, и снять эту сумму с общей суммы долга против их имени. Точно так же, если вы должны больше денег, чем у Стива в настоящее время, вы можете взять все деньги, которые у него в настоящее время есть, и снять эту сумму с общей суммы долга против вашего имени.

Если все согласны в начале выплатить Стиву только полную сумму, то каждый нетто-цветок делает ровно один депозит, а каждый нетто-должник человек делает ровно один вывод (хотя для этого может потребоваться несколько проверок Стива, чтобы увидеть, есть ли у него в настоящее время достаточно наличных денег). Хорошо, что Стив всегда рядом и никогда не бывает слишком занят, чтобы разобраться с финансами. К сожалению, он очень доверчив, поэтому Элис, Боб и Чарли должны уже доверять друг другу, чтобы не воспользоваться им.