ねえ、私は正の数の配列をk個の部分に分割して、各部分が(ほぼ)同じ合計になるようにするアルゴリズムを見つけるための助けを探しています...
1,2,3,4,5,6,7,8,9 en k = 3アルゴリズムは、この1,2,3,4,5 | 6,7|8,9の順序で分割する必要があります。要素を変更することはできません...欲張りアルゴリズムを見つけるのは簡単ですが、常に最適なソリューションを返すバックトラックバージョンを探しています...
誰もが何かヒントを得ましたか?
ねえ、私は正の数の配列をk個の部分に分割して、各部分が(ほぼ)同じ合計になるようにするアルゴリズムを見つけるための助けを探しています...
1,2,3,4,5,6,7,8,9 en k = 3アルゴリズムは、この1,2,3,4,5 | 6,7|8,9の順序で分割する必要があります。要素を変更することはできません...欲張りアルゴリズムを見つけるのは簡単ですが、常に最適なソリューションを返すバックトラックバージョンを探しています...
誰もが何かヒントを得ましたか?
これは、リストなどの動的データ構造を使用しないソリューションです。それらは完全に不要であり、実際にはアルゴリズムが必要以上に遅くなります。
ここでKをパーティションの数、Nを配列内の要素の数とします。
int start[K];
void register() {
/* calculate the error between minimum and maximum value partitions */
/* partition boundaries are start[0], start[1], start[2], ... */
/* if lower error than previously stored, remember the best solution */
}
void rec(int s, int k) {
if (k == K) register();
for (int i = s; i < N; i++) {
start[k] = i;
rec(i + 1, k + 1);
}
}
/* entry */
start[0] = 0;
rec(1, 1);
/* then output the best solution found at register() */
注:これはO(n K)アルゴリズムです。これは一般的なNP完全分割問題と同等ではないため、指数関数的ではありません。ここでは、特定の合計セットの任意のサブセットではなく、線形配列の連続したセグメントを探しています。
最適なソリューションとはどういう意味ですか?最適なパーティションまでの各パーティション距離の合計を最小化するものを意味すると思います。最適なパーティションは、要素の合計がパーティションの数で割った合計に等しいパーティションです。
効率を気にしないのであれば、この大まかなアプローチで十分かもしれません。アルゴリズムをテストして正しいかどうかを確認していませんので、注意してください。
void FindPartitions(int[] numbers, int i, IList<int>[] partitions, int currentPartition, IList<int>[] bestSolution, ref int minDistance)
{
if (i == numbers.Length)
{
int sum = numbers.Sum();
int avg = sum / partitions.Length;
int currentDistance = 0;
foreach (var partition in partitions)
currentDistance += Math.Abs(partition.Sum() - avg);
if (currentDistance < minDistance)
{
minDistance = currentDistance;
for (int j = 0; j < partitions.Length; j++)
bestSolution[j] = new List<int>(partitions[j]);
}
}
else
{
partitions[currentPartition].Add(numbers[i]);
FindPartitions(numbers, i + 1, partitions, currentPartition, bestSolution, ref minDistance);
partitions[currentPartition].RemoveAt(partitions[currentPartition].Count - 1);
if (currentPartition < partitions.Length - 1)
FindPartitions(numbers, i, partitions, currentPartition + 1, bestSolution, ref minDistance);
}
}
これがJavascriptの再帰的アルゴリズムです。この関数は、各ワーカーに割り当てられる合計を返します。入力配列bookLoadsが正の数の配列であり、可能な限り公平にk個の部分に分割するとします(たとえば、k個のワーカー間で)
これが実用的なフィドルです: https ://jsfiddle.net/missyalyssi/jhtk8vnc/3/
function fairWork(bookLoads, numWorkers = 0) {
// recursive version
// utilities
var bestDifference = Infinity;
var bestPartition = {};
var initLoads = {};
var workers = Array.from({length: numWorkers}, (val, idx) => {
initLoads[idx] = 0;
return idx;
});
var bookTotal = bookLoads.reduce((acc, curr) => {return acc + curr}, 0);
var average = bookTotal / bookLoads.length;
// recursive function
function partition(books = bookLoads, workers, loads={}) {
// if only one worker give them all the books
if (workers.length == 1 && books.length > 0) {
var onlyWorker = workers[0];
loads[onlyWorker] += books.reduce((acum, curr, idx, arr) => {
return acum + curr;
},0);
books = [];
}
// base case
if (books.length == 0) {
var keys = Object.keys(loads);
var total = 0;
for (let key = 0; key < keys.length; key++) {
// square so that difference shows
total += Math.pow(Math.abs(average - loads[keys[key]]), 2);
}
if (total < bestDifference) {
bestDifference = total;
bestPartition= loads;
}
return bestPartition;
}
var currBookLoad = books[0];
// add book to curr worker 1
var newWorker1Loads = Object.assign({}, loads);
var worker1 = workers[0];
newWorker1Loads[worker1] = newWorker1Loads[worker1] + currBookLoad || currBookLoad;
partition(books.slice(1), workers, newWorker1Loads)
// give to next worker
var newNextWorkerLoads = Object.assign({}, loads);
var worker2 = workers[1];
newNextWorkerLoads[worker2] = newNextWorkerLoads[worker2] + currBookLoad || currBookLoad;
partition(books.slice(1), workers.slice(1), newNextWorkerLoads)
return bestPartition;
}
// function call
return partition(bookLoads, workers, initLoads)
}
fairWork([3,1,2,3], 3)
//Result will be: Object {0: 3, 1: 3, 2: 3}
fairWork([1,2,3,4,5,6,7,8,9], 3)
//Result will be: Object {0: 15, 1: 13, 2: 17}