我想找到一种方法在 LibreOffice Calc 中计算需要支付的金额以平衡朋友之间的费用。
下面给出了一个玩具示例。减号表示该人必须接收,+号表示该人必须付款。
A 人 | B 人 | C 人 | D 人 | E 人 | F 人 | G人 | H 人 |
---|---|---|---|---|---|---|---|
+339.85 | -111.76 | +298.28 | -535.20 | +70.34 | -345.93 | +148.39 | +136.03 |
因此,我希望有一个表格显示每个人必须支付多少钱以及向谁支付,以便账户在人与人之间的交易数量最少的情况下保持平衡。
也许某个地方已经制作了一个电子表格来进行此类计算。我浏览了网络,但没有找到。如果您知道这样的电子表格,请给我发送一个链接,或者给我一些提示/指示,以便我可以构建一个允许进行此计算的电子表格。
让我们逻辑思考一下。
如果我们从整个列表中选择获胜最大的玩家和损失最大的玩家并仅在他们之间进行结算,那么其中一名玩家将不再参与进一步的交易。事实上,要么最大的输家将完全偿还他的债务,要么最大的赢家将全额收到他的奖金。
理想情况下,由于相互结算,这两个玩家都将被排除在进一步的交易之外。
剩下的就是和剩下的玩家进行同样的操作了。
您可以尝试使用公式来实现此算法 - 找到最小金额,然后使用 MATCH() 在列表中查找玩家的索引,找到最大金额,比较这些值,等等。然而,还有一个更简单的方法。如果将玩家名单按升序排列,第一个将是最大的输家,最后一个将是最大的赢家。
不幸的是,Calc 尚未实现 SORT() 函数。
但是,使用简单的 BASIC 代码,您可以使用任何各种排序算法对值数组进行排序。
由于玩家列表不是很大,插入排序就足够了。 按第二列的值对二维数组进行排序的过程的代码可能如下所示:
Sub InsertionSort(A() As Variant)
Dim LB As Long, I As Long, J As Long, aTempName As Variant, aTempValue As Variant
LB = LBound(A)
For I = LB+1 To UBound(A)
aTempName = A(I,1) : aTempValue = A(I,2)
For J = I-1 To LB Step -1
A(J+1, 1) = A(J, 1) : A(J+1, 2) = A(J, 2) : If aTempValue >= A(J,2) Then Exit For
Next J
A(J+1,1) = aTempName : A(J+1,2) = aTempValue
Next I
End Sub
创建交易列表的函数也不是很复杂。 检查源数据的总赢和总输是否一致,如果没有错误,则对原始数组进行排序,对数组中的第一个和最后一个玩家进行结算,重复所需的次数:
Function WhoToWhom(aData As Variant) As Variant
Dim aResult As Variant
Dim i As Long, j As Long, LB As Integer, UB As Integer
Dim dWin As Double, dLose As Double
LB = LBound(aData, 1)
UB = UBound(aData,1)
ReDim aResult(LB-1 To UB, 1 To 3)
dWin = 0
dLose = 0
For i = LB To UB
aResult(i,1) = "" : aResult(i,2) = "" : aResult(i,3) = ""
If aData(i,2) > 0 Then dWin = dWin + aData(i,2) Else dLose = dLose + aData(i,2)
Next i
If Fix((dWin+dLose)*100.0) <> 0 Then
aResult(LB-1,1) = "Win" : aResult(LB-1,2) = "Lose" : aResult(LB-1,3) = "Difference"
aResult(LB,1) = dWin : aResult(LB,2) = dLose : aResult(LB,3) = dWin+dLose
Else
aResult(LB-1,1) = "Who" : aResult(LB-1,2) = "Whom" : aResult(LB-1,3) = "Amount"
InsertionSort(aData)
While aData(LB,2) < 0
j = j + 1
aResult(j,1) = aData(LB, 1) : aResult(j,2) = aData(UB, 1)
If aData(UB, 2) > -aData(LB, 2) Then
aResult(j,3) = -aData(LB, 2)
aData(LB, 2) = 0
aData(UB, 2) = aData(LB, 2) + aData(UB, 2)
ElseIf aData(UB, 2) < -aData(LB, 2) Then
aResult(j,3) = aData(UB, 2)
aData(LB, 2) = aData(LB, 2) + aData(UB, 2)
aData(UB, 2) = 0
Else
aResult(j,3) = aData(UB, 2)
aData(LB, 2) = 0
aData(UB, 2) = 0
EndIf
InsertionSort(aData)
Wend
EndIf
WhoToWhom = aResult
End Function
该函数需要一个垂直扩展的玩家列表 - 第一列中的名称,第二列中的获胜或失败的金额。因此,要将示例传递给函数,您应该使用 TRANSPOSE() 函数
{=WHOTOWHOM(TRANSPOSE(A1:H2))}
由于这是一个 数组函数,请务必使用 Ctrl+Shift+Enter
完成输入有可能我的推理有误,有办法进一步减少交易次数。如果有请指正