using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApplication1
{
class Program
{
class Share
{
public decimal Weight { get; set; }
public decimal Amount { get; set; }
public decimal Tax { get; set; }
public decimal ShippingFee { get; set; }
}
static void Main(string[] args)
{
//假設有一筆訂單,金額32767, 稅金438, 運費600
//要依1.2 : 1.5 : 2拆成三份
//建立三個分配結果物件,設定權重
List<Share> shares = new List<Share>();
shares.Add(new Share() { Weight = 1.2M });
shares.Add(new Share() { Weight = 1.5M });
shares.Add(new Share() { Weight = 2M });
//執行分配,參數依序傳入待分配金額,小數位數,權重依據,取回結果
//其中權重依據及取回結果可使用Lambda表示式
shares
.PerfectDivide<Share>(32767, 0, o => o.Weight, (o, v) => o.Amount = v)
.PerfectDivide<Share>(438, 1, o => o.Weight, (o, v) => o.Tax = v)
.PerfectDivide<Share>(600, 0, o => o.Weight, (o, v) => o.ShippingFee = v);
foreach (var share in shares)
Console.WriteLine("{0:N0}\t{1:N1}\t{2:N0}",
share.Amount, share.Tax, share.ShippingFee);
Console.Read();
}
}
/// <summary>
/// 無尾差之完美分贓演算法
/// </summary>
public static class PerfectDivider
{
/// <summary>
/// 依提供比例/權重進行無尾差分配計算
/// </summary>
/// <param name="weights">權重陣列</param>
/// <param name="amt">待分配的數量</param>
/// <param name="decimals">小數位數</param>
/// <returns>分配結果陣列</returns>
public static decimal[] Divide(decimal[] weights, decimal amt, int decimals)
{
decimal weightSum = weights.Sum(o => o);
decimal[] result = new decimal[weights.Length];
for (int i = 0; i < weights.Length; i++)
{
result[i] = Math.Round(amt*weights[i]/weightSum, decimals,
MidpointRounding.AwayFromZero);
amt -= result[i];
weightSum -= weights[i];
}
return result;
}
/// <summary>
/// 以LINQ方式實不同權重之無尾差分配
/// </summary>
/// <typeparam name="T">物件型別</typeparam>
/// <param name="ienum">IEnumerable集合</param>
/// <param name="amt">待分配量</param>
/// <param name="decimals">小數位數</param>
/// <param name="getWeight">回傳權重數字之委派Func<T, decimal></param>
/// <param name="resultCallback">傳入分配結果之委派Action<T, decimal></param>
/// <returns>繼續傳回IEnumerable</returns>
public static IEnumerable<T> PerfectDivide<T>(
this IEnumerable<T> ienum,
decimal amt, int decimals, Func<T, decimal> getWeight,
Action<T, decimal> resultCallback)
{
decimal[] weights = ienum.Select(getWeight).ToArray();
decimal[] results = Divide(weights, amt, decimals);
for (int i = 0; i < results.Length; i++)
resultCallback(ienum.ElementAt(i), results[i]);
return ienum;
}
}
}