再帰関数を使用すると、RAMが大量に消費されるため、長さと整数が大きくなると適切ではないことがわかりました。また、ジェネレーター/再開可能関数(「yields」値)の使用は遅すぎて、クロスさせるには大きなライブラリが必要でした。 -プラットホーム。
したがって、これがC ++の非再帰的ソリューションであり、ソートされた順序でパーティションを生成します(これは順列にも理想的です)。これは、パーティションの長さが4以上の場合に試した、一見巧妙で簡潔な再帰的ソリューションよりも10倍以上高速であることがわかりましたが、長さが1〜3の場合、パフォーマンスは必ずしも優れているとは限りません(短いことは気にしません)。どちらのアプローチでも高速であるため、長さ)。
// Inputs
unsigned short myInt = 10;
unsigned short len = 3;
// Partition variables.
vector<unsigned short> partition(len);
unsigned short last = len - 1;
unsigned short penult = last - 1;
short cur = penult; // Can dip into negative value when len is 1 or 2. Can be changed to unsigned if len is always >=3.
unsigned short sum = 0;
// Prefill partition with 0.
fill(partition.begin(), partition.end(), 0);
do {
// Calculate remainder.
partition[last] = max(0, myInt - sum); // Would only need "myInt - sum" if partition vector contains signed ints.
/*
*
* DO SOMETHING WITH "partition" HERE.
*
*/
if (partition[cur + 1] <= partition[cur] + 1) {
do {
cur--;
} while (
cur > 0 &&
accumulate(partition.cbegin(), partition.cbegin() + cur, 0) + (len - cur) * (partition[cur] + 1) > myInt
);
// Escape if seeked behind too far.
// I think this if-statement is only useful when len is 1 or 2, can probably be removed if len is always >=3.
if (cur < 0) {
break;
}
// Increment the new cur position.
sum++;
partition[cur]++;
// The value in each position must be at least as large as the
// value in the previous position.
for (unsigned short i = cur + 1; i < last; ++i) {
sum = sum - partition[i] + partition[i - 1];
partition[i] = partition[i - 1];
}
// Reset cur for next time.
cur = penult;
}
else {
sum++;
partition[penult]++;
}
} while (myInt - sum >= partition[penult]);
私が書いたところは、ここに「パーティション」で何かをします。実際に値を消費する場所です。(最後の反復で、コードはループの残りの部分を実行し続けますが、これは終了条件を常にチェックするよりも優れていることがわかりました-より大きな操作用に最適化されています)
0,0,10
0,1,9
0,2,8
0,3,7
0,4,6
0,5,5
1,1,8
1,2,7
1,3,6
1,4,5
2,2,6
2,3,5
2,4,4
3,3,4
長さと整数が特定の制限を超えないことがわかっているので、「unsigned short」を使用しました。適切でない場合は、変更してください:)コメントを確認してください。そこにある1つの変数(cur)は、1または2の長さを処理するために署名する必要があり、それに対応するifステートメントがあります。また、コメントで、パーティションベクトルがintに署名している場合は、別の行があることにも注意しました。それは単純化することができます。
すべての構成を取得するために、C ++では、この単純な順列戦略を使用します。これは、ありがたいことに重複を生成しません。
do {
// Your code goes here.
} while (next_permutation(partition.begin(), partition.end()));
DO SOMETHING WITH "partition" hereスポットにネストすれば、準備完了です。
構成を見つける代わりに(ここのJavaコードhttps://www.nayuki.io/page/next-lexicographical-permutation-algorithmに基づいて)次のようになります。next_permutation()よりもパフォーマンスが優れていることがわかりました。
// Process lexicographic permutations of partition (compositions).
composition = partition;
do {
// Your code goes here.
// Find longest non-increasing suffix
i = last;
while (i > 0 && composition[i - 1] >= composition[i]) {
--i;
}
// Now i is the head index of the suffix
// Are we at the last permutation already?
if (i <= 0) {
break;
}
// Let array[i - 1] be the pivot
// Find rightmost element that exceeds the pivot
j = last;
while (composition[j] <= composition[i - 1])
--j;
// Now the value array[j] will become the new pivot
// Assertion: j >= i
// Swap the pivot with j
temp = composition[i - 1];
composition[i - 1] = composition[j];
composition[j] = temp;
// Reverse the suffix
j = last;
while (i < j) {
temp = composition[i];
composition[i] = composition[j];
composition[j] = temp;
++i;
--j;
}
} while (true);
そこにいくつかの宣言されていない変数があります。コードの早い段階で、すべてのdo-loopの前に宣言してください:i
、、、、(unsigned shorts)、およびj
(pos
と同じタイプと長さ)。forの宣言は、パーティションコードスニペットのforループで使用するために再利用できます。また、このコードが前述のパーティションコード内にネストされていることを前提とした、使用されているような変数にも注意してください。temp
composition
partition
i
last
繰り返しますが、「あなたのコードはここにあります」は、あなたがあなた自身の目的のために構成を消費する場所です。
参考までに、ここに私のヘッダーがあります。
#include <vector> // for std::vector
#include <numeric> // for std::accumulate
#include <algorithm> // for std::next_permutation and std::max
using namespace std;
これらのアプローチを使用すると速度が大幅に向上しますが、かなりの整数とパーティションの長さの場合でも、CPUに腹を立てることになります:)