我遵循一种算法(最好是抽象的或非常清晰的Python或PHP代码),根据以下约束条件,允许在短期和长期内公平分配收入:
i
人必须接收floor(revenue*cut[i])
或ceil(revenue*cut[i])
。 编辑:我要强调ceil(revenue*cut[i])
不一定等于1+floor(revenue*cut[i])
,这是一些算法失败的地方,包括一个算法(参见我对安德烈答案的评论)。 < / LI>
这是一个清晰的例子。假设其中有三个人分配收入,一个必须得到31%,另一个得到21%,第三个得到100-31-21 = 48%。
现在想象一下有80个硬币的收入。第一个人应该收到floor(80*31/100)
或ceil(80*31/100)
个硬币,第二个人应该收到floor(80*21/100)
或ceil(80*21/100)
和第三个人,floor(80*48/100)
或{{ 1}}。
现在想象一下,有120个硬币的新收入。每个人应该再次收到相应削减的楼层或天花板,但由于总收入(200)是分母(100)的倍数,每个人现在应该有他们的确切削减:第一个人应该有62个硬币,第二个人应该有42个硬币,第三个人应该有96个硬币。
我想我有一个算法可以找出两个人分配收入的算法。它是这样的(35%和65%):
ceil(80*48/100)
我认为这个简单的算法符合我的所有约束,但我没有找到将其扩展到两个以上的人而不会违反至少一个的方法。任何人都可以找出多人的延伸(或者证明其不可能)吗?
答案 0 :(得分:1)
我认为这有效:
totalReceived
,即进入系统的总金额。totalReceived
。person[i].newTotal = floor(totalReceived * cut[i])
。 编辑:此处有错误;感谢评论者。这会分配部分资金。跟踪多少,知道剩下多少“新钱”。为了清楚起见,我们正在跟踪剩余的总金额中剩余多少,并且直观地我们在每一步都重新分配全部金额,尽管实际上没有人的资金在一个步骤和下一步。numPeople
。对于i
范围从0
到leftOverMoney - 1
的{{1}},请将person[i].newTotal
增加1.实际上,我们会为每个leftOverMoney
人提供一美元。person[i].newTotal - person[i].total
。person[i].total = person[i].newTotal
。答案 1 :(得分:1)
为了适应约束4,必须使用当前付款作为分配的基础。在向投资者分配四舍五入的股息后,剩余的美元可以分别给予任何合格的投资者一个。如果投资者应分得一小部分,即ceil(revenuecut [i])&gt;地板(revenuecut [I])。
约束5可以通过决定合格投资者中谁应该获得剩余资金来解决。通过将剩余的美元奖励给累积了最多(长期)分配错误的合格投资者,将最接近最大长期公平性。
以下内容源自我用于基金经理客户的算法。 (出于法医原因,我总是将Pascal用于财务应用程序。)我已将数据库应用程序中的代码片段转换为基于数组的方法,以提高清晰度。
会计数据结构中包含投资者表。关于每个投资者的信息包括他的份额分数(投资者份额除以基金总股份),迄今为止赚取的金额,以及迄今为止分配的金额(实际支付给投资者)。
Investors : array [0..InvestorCount-1] of record
// ... lots of information about this investor
ShareFraction : float; // >0 and <1
EarnedToDate : currency; // rounded to dollars
DistributedToDate : currency; // rounded to dollars
end;
// The sum of all the .ShareFraction in the table is exactly 1.
为这些计算创建了一些临时数据,然后将其释放。
DistErrs : array [0..InvestorCount-1] of record
TotalError : float; // distribution rounding error
CurrentError : float; // distribution rounding error
InvestorIndex : integer; // link to Investors record
end;
UNDISTRIBUTED_PAYMENTS : currency;
CurrentFloat : float;
CurrentInteger : currency;
CurrentFraction : float;
TotalFloat : float;
TotalInteger : currency;
TotalFraction : float;
DD, II : integer; // array indices
FUND_PREVIOUS_PAYMENTS : currency;
是一个参数,源自基金历史。如果它不作为参数提供,则可以计算为所有Investors []的总和.EnnedToDate。
FUND_CURRENT_PAYMENT : currency;
是一个参数,源自当前基金价值的增长。
FUND_TOTAL_PAYMENTS : currency;
是一个参数,源自当前的基金价值。这是先前支付的基金总额加上基金当前付款。
这三个值形成具有两个自由度的家属系统。可以提供任何两个,并且从其他人计算第三个。
// Calculate how much each investor has earned after the fund current payment.
UNDISTRIBUTED_PAYMENTS := FUND_CURRENT_PAYMENT;
for II := 0 to InvestorCount-1 do begin
// Calculate theoretical current dividend whole and fraction parts.
CurrentFloat := FUND_CURRENT_PAYMENT * Investors[II].ShareFraction;
CurrentInteger := floor(CurrentFloat);
CurrentFraction := CurrentFloat - CurrentInteger;
// Calculate theoretical total dividend whole and fraction parts.
TotalFloat := FUND_TOTAL_PAYMENTS * Investors[II].ShareFraction;
TotalInteger := floor(TotalFloat);
TotalFraction := TotalFloat - TotalInteger;
// Distribute the current whole dollars to the investor.
Investors[II].EarnedToDate := Investors[II].EarnedToDate + CurrentInteger;
// Track total whole dollars distributed.
UNDISTRIBUTED_PAYMENTS := UNDISTRIBUTED_PAYMENTS - CurrentInteger;
// Record the fractional dollars in the errors table and link to investor.
DistErrs[II].CurrentError := CurrentFraction;
DistErrs[II].TotalError := TotalFraction;
DistErrs[II].InvestorIndex := II;
end;
此时UNDISTRIBUTED_PAYMENTS
始终小于InvestorCount
。
// Sort DistErrs by .TotalError descending.
在现实世界中,数据保存在不在数组中的数据库中,因此通过使用DistErrs
作为键在TotalError
上创建索引来完成排序。
// Distribute one dollar each to the first UNDISTRIBUTED_PAYMENTS eligible
// investors (i.e. those whose current share ceilings are higher than their
// current share floor), in order from greatest to least .TotalError.
for DD := 0 to InvestorCount-1 do begin
if UNDISTRIBUTED_PAYMENTS <= 0 then break;
if DistErrs[DD].CurrentError <> 0 then begin
II := DistErrs[DD].InvestorIndex;
Investors[II].EarnedToDate := Investors[II].EarnedToDate + 1;
UNDISTRIBUTED_PAYMENTS := UNDISTRIBUTED_PAYMENTS - 1;
end;
end;
在后续流程中,每个投资者都需要支付他的.EarnedToDate
和他的.DistributedToDate
之间的差额,并调整他的.DistributedToDate
以反映该付款。