Algorithm 谁欠谁钱

Algorithm 谁欠谁钱,algorithm,math,optimization,graph,Algorithm,Math,Optimization,Graph,假设你有n个人,每个人都欠对方钱。一般来说,应该可以减少需要进行的交易量。i、 e.如果X欠Y 4英镑,Y欠X 8英镑,则Y只需支付X 4英镑(1笔交易而非2笔交易) 当X欠Y时,这变得更加困难,但Y欠Z,Z也欠X。我可以看出,你可以很容易地计算出一个特定的周期。当我把它看作一个完全连通的图时,它对我很有帮助,边是每个人欠下的金额 这个问题似乎是NP完全问题,但我可以使用什么样的优化算法来减少总的交易量?不必那么高效,因为N对我来说很小 编辑: 这个问题的目的是能够在会计系统中对每个人在登录时说

假设你有n个人,每个人都欠对方钱。一般来说,应该可以减少需要进行的交易量。i、 e.如果X欠Y 4英镑,Y欠X 8英镑,则Y只需支付X 4英镑(1笔交易而非2笔交易)

当X欠Y时,这变得更加困难,但Y欠Z,Z也欠X。我可以看出,你可以很容易地计算出一个特定的周期。当我把它看作一个完全连通的图时,它对我很有帮助,边是每个人欠下的金额

这个问题似乎是NP完全问题,但我可以使用什么样的优化算法来减少总的交易量?不必那么高效,因为N对我来说很小

编辑:


这个问题的目的是能够在会计系统中对每个人在登录时说“你可以通过简单地向某人支付X金额,而向其他人支付Y金额来删除M笔交易”。因此,银行解决方案(如果每个人都在同一时间付款,尽管是最佳的)在这里不能真正使用。

我认为您需要构建一个不同的数据结构(树,每次一个人是根节点),它将检查每个人可以“杀死”多少“交易”,然后选择最好的一个,进行循环,它不是o(N),我认为它是N^2,它不会给你最好的结果。这只是一种策略。

人们是否需要通过支付他们实际欠个人的钱来还清债务?如果不是这样,以下方法似乎很容易奏效:

对于每个人,计算出他们应该支付或应该收到的净金额

有人欠了钱净支付某人谁应该收到钱净分钟(金额欠,金额收到)。在此之后,两个参与者中至少有一个不欠任何债务,也不应该收到任何东西,因此可以从问题中删除


假设我遗漏了什么,应用的约束条件是什么(或产生的严重误差)?

想想看,我会先查看有向图中的每个循环,然后用循环中最小边的值减少循环中的每个边,然后完全删除最小边。冲洗并重复。

任意指定一人担任银行家

其他人将所有传出交易的总和减去传入交易(存款或取款)转移给该人

最多会有(n-1)个事务,这相当小。它很快。这很简单

考虑到所有转账的人都必须参与交易*,最坏的情况下,它肯定是最佳情况的两倍**

*银行家本身是个例外。快速优化是为了确保指定的银行家不是持有中立立场的人

**进一步解释我的上限逻辑:

假设最佳情况是A向B提供$1,C向D提供$1,E为中性=两个事务

根据这个逻辑,如果E是指定银行,A给E美元,E给B美元,C给E美元,E给D美元=四笔交易

通过优化,确保你们并没有选择一个中立的人作为银行家,而是选择一个中立的人。
A给B一美元,C给A一美元。A给D一美元=三笔交易。

这个问题可以用Warshall算法解决

for(i=0;i<n;i++)
  for(j=0;j<n;j++)
    if ( i!= j && owes[i][j] > owes[j][i] )
owes[i][j] -= (owes[i][j] - owes[j][i]), owes[j][i] = 0;

for(k=0;k<n;k++)
 for(i=0;i<n;i++)
  for(j=0;j<n;j++)
  {
if( k == i || i == j || k == j ) continue;
if ( owes[j][k] > owes[i][j] )
{
int diff = owes[j][k] - owes[i][j]; 
owes[i][j] = 0;
owes[i][k ] += diff;
owes[j][k] -= diff;
} 
}

对于(i=0;i我认为您必须删除所有圆,以最小边值减少边,并删除值为0的边。之后,您将得到没有圆的图形。我认为您必须找到顶点,它们没有指向它们的指针(男人的wich只欠别人钱).这个人必须付钱,因为没有人为他们付钱。所以我的观点是,你必须找到他们必须付钱的人。


for each debt in debts
  debt.creditor.owed -= debt.amount
  debt.deptor.owed += debt.amount
end

for each person in persons
  if person.owed > 0 then
    deptors.add person
  else if person.owed < 0 then
    creditors.add person
  end
end

deptors.sort_by_owed_desc
creditor.sort_by_owed_asc

for each debtor in deptors
  while debtor.owed > 0
    creditor = creditors.top
    amount = min( debtor.owed, -creditor.owed)
    creditor.owed += amount
    debtor.owed -= amount
    if creditor.owed == 0 then
      creditors.remove_top
    end
    write debtor.name " owes " creditor.name " " amount "€"
  end
end
债务。债权人。欠款-=债务。金额 debt.deptor.owed+=debt.amount 结束 每人 如果person.owed>0,则 deptors.addperson 否则,如果person.down<0,则 添加人 结束 结束 deptors.sort\u by\u own\u desc 债权人。按欠款分类 各债务人的债务 而债务人所欠的债务>0 债权人 金额=最小值(债务人欠下,-债权人欠下) 债权人欠款+=金额 债务人所欠-=金额 如果债权人欠款==0,则 1.取下顶部 结束 填写债务人名称“欠”债权人名称“金额”€ 结束 结束
这是我使用的Python解决方案;它与Gunner的帖子的想法相同,只是有几行改动:

for i in N:
    for j in N:
        if i!=j and owes[i][j] > owes[j][i]:
            owes[i][j] -= owes[j][i]
            owes[j][i] = 0
for k in N:
    for i in N:
        for j in N:
            if k == i or i == j or k == j:
                continue
            if owes[j][k] > owes[i][j]:
                owes[i][k] += owes[i][j]
                owes[j][k] -= owes[i][j]
                owes[i][j] = 0;
工作是一种享受

您可以使用以下工具进行测试:

owes = [[0,2,11], [4,0,7], [2,3,0]]
N = range(len(owes))

我有一个用matlab编写的问题的解决方案。它基于一个谁欠谁什么的矩阵。(I,j)中的数字意味着人j欠人I这个数字

B欠A 2 A欠B 1

当然,在这种情况下,B只给出1是很平常的

但是,通过我编写的算法,我可以保证在这种情况下,当N是个人数2时,不会发生超过N-1个事务

这是我写的代码

function out = whooweswho(matrix)
%input sanitation
if ~isposintscalar(matrix)
    [N M] = size(matrix);
    if N ~= M
        error('Matrix must be square');
    end

    for i=1:N
        if matrix(N,N) ~= 0
            error('Matrix must have zero on diagonals');
        end
    end
else
    %construction of example matrix if input is a positive scalar
    disp('scalar input: Showing example of NxN matrix randomly filled');
    N = matrix;
    matrix = round(10*N*rand(N,N)).*(ones(N,N)-eye(N))
end

%construction of vector containing each persons balance
net = zeros(N,1);
for i=1:N
  net(i) = sum(matrix(i,:))-sum(matrix(:,i));
end

%zero matrix, so it can be used for the result
matrix = zeros(size(matrix));

%sum(net) == 0 always as no money dissappears. So if min(net) == 0 it
%implies that all balances are zero and we are done.
while min(net) ~= 0
  %find the poorest and the richest.
  [rec_amount reciever] = max(net);
  [give_amount giver] = min(net);
  %balance so at least one of them gets zero balance.
  amount =min(abs(rec_amount),abs(give_amount));
  net(reciever) = net(reciever) - amount;
  net(giver) = net(giver) + amount;
  %store result in matrix.
  matrix(reciever,giver) = amount;
end
%output equivalent matrix of input just reduced.
out = matrix;

结束

我已经创建了一个Android应用程序来解决这个问题。你可以在旅途中输入费用,它甚至建议你“下一个应该由谁支付”。最后它计算“谁应该向谁发送多少”。我的算法计算所需的最低交易数量,你可以设置“交易容差”这可以进一步减少交易(你不在乎1美元的交易)试一试,这叫做结算:

我的算法说明:

我有解决n-1交易问题的基本算法,但它不是最优的。它是这样工作的:从付款中,我计算每个成员的余额。余额是他支付的减去他应该支付的。我根据余额对成员进行排序。然后我总是取最穷的