将数量分配到桶中 - 不均匀

Distribute quantities into buckets - Not evenly

我一直在四处寻找解决方案,但我认为由于我的思考方式,我的搜索短语可能有点偏向于不完全相关的主题。

我有一个数字,比如说950,000。这表示整个系统中的 [widgets] 清单。我有大约 200 "buckets" 个,每个都应该收到此库存的一部分,这样就没有剩余的小部件了。

我希望每个桶接收不同的金额。我现在没有任何可靠的代码可以展示,但这里有一些伪代码可以说明我一直在想的事情:

//List<BucketObject> _buckets is a collection of "buckets", each of which has a "quantity" property for holding these numbers.

int _widgetCnt = 950000;
int _bucketCnt = _buckets.Count;     //LINQ
//To start, each bucket receives (_widgetCnt / _bucketCnt) or 4750.

for (int _b = 0; b< _bucketCnt - 1; i++)
{
     int _rndAmt = _rnd.Next(1, _buckets[i].Quantity/2); //Take SOME from this bucket...
     int _rndBucket = _rnd.Next(0,_bucketCnt - 1);    //Get a random bucket index from the List<BucketObject> collection.

     _buckets.ElementAt(_rndBucket).Quantity += _rndAmt;
     _buckets.ElementAt(i).Quantity -= _rndAmt;
}

这是 statistically/mathematically 处理此问题的正确方法,还是有处理此问题的分配公式?更重要的是,虽然这个伪代码会 运行 200 次(因此每个桶都有机会改变它的数量),但它必须 运行 X 次,具体取决于小部件的类型(目前目前只有 11 种口味,但预计未来会显着增加)。

{编辑} 该系统用于商品交易游戏。 200 家商店的数量必须不同,因为库存将决定该站点的价格。发行版不可能是均匀的,因为那样会使所有价格都相同。随着时间的推移,价格自然会失衡,但库存必须从失衡开始。并且所有库存必须至少在范围上相似(即,没有一家商店可以有 1 件商品,而另一家有 900,000 件)

当然,有一个解决方案。您可以使用 Dirichlet Distribution 来完成这样的任务。 属性的分布是

总和i xi = 1

所以解决方案是从 Dirichlet 中抽取 200 个(等于桶的数量)随机值,然后将每个值乘以 950,000(或任何总库存),这将得到每个桶的项目数。如果你想要非均匀采样,你可以在 Dirichlet 采样中调整 alpha

当然,每个桶的项目都应四舍五入 up/down,但这很微不足道

我在某个地方的 C# 中有 Dirichlet 采样,如果你很难实现它 - 告诉我,我会挖掘出来

更新

我找到了一些代码,.NET Core 2,下面是摘录。我曾经用样本 alphas 对 Dirichlet RN 进行采样,让它们都不同是微不足道的。

//
// Dirichlet sampling, using Gamma sampling from Math .NET
//

using MathNet.Numerics.Distributions;
using MathNet.Numerics.Random;

static void SampleDirichlet(double alpha, double[] rn)
{
    if (rn == null)
        throw new ArgumentException("SampleDirichlet:: Results placeholder is null");

    if (alpha <= 0.0)
        throw new ArgumentException($"SampleDirichlet:: alpha {alpha} is non-positive");

    int n = rn.Length;
    if (n == 0)
        throw new ArgumentException("SampleDirichlet:: Results placeholder is of zero size");

    var gamma = new Gamma(alpha, 1.0);

    double sum = 0.0;
    for(int k = 0; k != n; ++k) {
        double v = gamma.Sample();
        sum  += v;
        rn[k] = v;
    }

    if (sum <= 0.0)
        throw new ApplicationException($"SampleDirichlet:: sum {sum} is non-positive");

    // normalize
    sum = 1.0 / sum;
    for(int k = 0; k != n; ++k) {
        rn[k] *= sum;
    }
}