好的。我非常确定原始算法(如所写)和发布的代码(如所写)并不能完全回答@Mathias概述的测试用例问题。
我打算使用此算法进行稍微更具体的应用。而不是像原始问题中所示,使用(@amt / @SumAmt)
来计算百分比。我有一定的固定金额需要根据每个项目定义的百分比分割或分配到多个项目上。分割百分比总和为100%,但直接乘法经常会导致小数点(强制四舍五入为整个$时)不等于我正在分开的总金额。这是问题的核心。
我相当确定@Dav的原始答案在(如@Mathias所描述的)四舍五入值在多个切片间相等的情况下不起作用。这个原始算法和代码的问题可以通过一个测试用例来概括:
拿$100并使用33.333333%将其分成3份。
使用@jtw发布的代码(假设这是原始算法的准确实现)会给出错误答案,将$33分配给每个项目(导致总和为$99),因此未能通过测试。
我认为一个更准确的算法可能是:
- 从0开始设置一个运行总数
- 对于组中的每个项目:
- 计算未舍入的分配金额,如
([要拆分的金额] * [%要拆分])
- 将累积余数计算为
[余数]+([未舍入的金额]-[舍入的金额])
- 如果
Round([余数],0)> 1
或当前项目是列表中的最后一个项目,则将该项目的分配设置为[舍入的金额]+ Round ([余数],0)
- 否则将项目的分配设置为
[舍入的金额]
- 重复下一个项目
在T-SQL中实现,它看起来像这样:
Drop Table #SplitList
Create Table #SplitList ( idno int , pctsplit decimal(5, 4), amt int , roundedAmt int )
Insert Into #SplitList Values (1, 0.50, 75, 0)
Insert Into #SplitList Values (2, 0.50, 75, 0)
Declare @R Float
Declare @Results Float
Declare @unroundedAmt Float
Declare @idno Int
Declare @roundedAmt Int
Declare @amt Float
Declare @pctsplit Float
declare @rowCnt int
Select @R = 0
select @rowCnt = 0
Declare SplitList Cursor For
Select idno, pctsplit, amt, roundedAmt From #SplitList Order By amt Desc
Open SplitList
Fetch Next From SplitList Into @idno, @pctsplit, @amt, @roundedAmt
While @@FETCH_STATUS = 0
Begin
select @unroundedAmt = ( @amt * @pctsplit )
select @roundedAmt = Round( @unroundedAmt, 0 )
Select @R = @R + @unroundedAmt - @roundedAmt
select @rowCnt = @rowCnt + 1
if ( round(@R, 0 ) >= 1 ) or ( @@CURSOR_ROWS = @rowCnt ) Begin
select @Results = @roundedAmt + round( @R, 0 )
select @R = @R - round( @R, 0 )
End
else Begin
Select @Results = @roundedAmt
End
If Round(@Results, 0) <> 0
Begin
Update #SplitList Set roundedAmt = @Results Where idno = @idno
End
Fetch Next From SplitList Into @idno, @pctsplit, @amt, @roundedAmt
End
Close SplitList
Deallocate SplitList
Select * From #SplitList
Select Sum(roundedAmt), max( amt ),
case when max(amt) <> sum(roundedamt) then 'ERROR' else 'OK' end as Test
From #SplitList
这将为测试用例生成最终结果集:
idno pctsplit amt roundedAmt
1 0.3333 100 33
2 0.3333 100 34
3 0.3333 100 33
据我所知(代码中有多个测试用例),这种情况都可以很好地处理。