最初にn
乱数を生成x[i]
し、それらを合計してから合計で割るbudget
と、 が得られk
ます。次に、k*x[i]
各配列要素に割り当てます。シンプルでO(n)です。
各要素に少なくとも値が必要な場合は、上記のアルゴリズムを開始する前に、min
すべての要素を埋めてmin
(または使用してk*x[i] + min
) 下請けすることで、上記のアルゴリズムを変更できます。n*min
budget
整数を扱う必要がある場合は、実数値k
と丸めを使用して問題に取り組むことができますk*x[i]
。次に、丸め誤差の累積を追跡し、単位全体に達した場合は、計算値から累積誤差を加算または減算する必要があります。また、残りの値を最後の要素に割り当てて、全体に到達する必要がありbudget
ます。
PS: このアルゴリズムは、純粋関数型言語の easy で使用できることに注意してください。これが、メンバーごとに乱数を生成し、後で何らかの処理を行うこのアルゴリズムのファミリー全体が好きな理由です。Erlang での実装例:
-module(budget).
-export([distribute/2, distribute/3]).
distribute(Budget, N) ->
distribute(Budget, N, 0).
distribute(Budget, N, Min) when
is_integer(Budget), is_integer(N), N > 0,
is_integer(Min), Min >= 0, Budget >= N*Min ->
Xs = [random:uniform() || _ <- lists:seq(1,N) ],
Rest = Budget - N*Min,
K = Rest / lists:sum(Xs),
F = fun(X, {Bgt, Err, Acc}) ->
Y = X*K + Err,
Z = round(Y),
{Bgt - Z, Y - Z, [Z + Min | Acc]}
end,
{Bgt, _, T} = lists:foldl(F, {Rest, 0.0, []}, tl(Xs)),
[Bgt + Min | T].
C++ の同じアルゴリズム (?? わかりません。)
private int[] distribute(int budget, int n, int min) {
int[] subBudgets = new int[n];
double[] rands = new double[n];
double k, err = 0, sum = 0;
budget -= n * min;
for (int i = 0; i < n; i++) {
rands[i] = random.nextDouble();
sum += rands[i];
}
k = (double)budget/sum;
for (int i = 1; i < n; i++) {
double y = k*rands[i] + err;
int z = floor(y+0.5);
subBudgets[i] = min + z;
budget -= z;
err = y - z;
}
subBudgets[0] = min + budget;
return subBudgets;
}