Je trouve que l'utilisation d'une fonction récursive n'a pas été bonne pour agrandir longueurs et entiers car il mâche trop de RAM, et l'utilisation d'une fonction générateur/resumable (qui donne des valeurs) était trop lente et nécessitait une grande bibliothèque pour la rendre multiplateforme.
est donc ici une solution non récursive en C++ qui produit les partitions dans ordre de tri (ce qui est idéal pour les permutations aussi). J'ai trouvé cela plus de 10 fois plus rapide que des solutions récursives apparemment intelligentes et concises que j'ai essayées pour des longueurs de partition de 4 ou plus, mais pour des longueurs de 1-3, la performance n'est pas forcément meilleure (et je m'en fiche longueurs parce qu'ils sont rapides avec l'une ou l'autre approche).
// 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]);
Là où j'ai écrit faire quelque chose avec "partition" ICI. est où vous consommeriez réellement la valeur. (Sur la dernière itération le code continuera à exécuter le reste de la boucle mais je trouve cela mieux que de vérifier en permanence des conditions de sortie - il est optimisé pour les opérations plus importantes)
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
Oh I » J'ai utilisé "short non signé" car je sais que ma longueur et mon entier ne dépasseront pas certaines limites, changez cela si cela ne vous convient pas :) Vérifiez les commentaires; une variable là (cur) a dû être signée pour gérer des longueurs de 1 ou 2 et il y a une instruction if correspondante qui va avec, et j'ai aussi noté dans un commentaire que si votre vecteur de partition a signé ints il y a une autre ligne cela peut être simplifié.
Pour obtenir toutes les compositions, en C++ je voudrais utiliser cette simple stratégie de permutation qui ne heureusement produit pas de doublons:
do {
// Your code goes here.
} while (next_permutation(partition.begin(), partition.end()));
Nest que dans le faire quelque chose avec « partition » ICI place, et tu es bon à faire.
Une alternative à la recherche des compositions (basée sur le code Java ici https://www.nayuki.io/page/next-lexicographical-permutation-algorithm) est la suivante. J'ai trouvé que cela fonctionne mieux que 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);
Vous remarquerez certaines variables non déclarées là, il suffit de les déclarer plus tôt dans le code avant que toutes vos boucles do-: i
, j
, pos
et temp
(short non signés), et composition
(même type et la longueur comme partition
). Vous pouvez réutiliser la déclaration de i
pour son utilisation dans une boucle for dans l'extrait de code de partitions. Notez également les variables telles que last
utilisées qui supposent que ce code est imbriqué dans le code de partitions donné précédemment.
Encore une fois "Votre code va ici" est l'endroit où vous consommez la composition pour vos propres besoins.
Pour référence, voici mes en-têtes.
#include <vector> // for std::vector
#include <numeric> // for std::accumulate
#include <algorithm> // for std::next_permutation and std::max
using namespace std;
En dépit de l'augmentation massive de la vitesse à l'aide de ces approches, pour des entiers non négligeables et longueurs de partition ce sera toujours vous rendre fou à votre CPU :)
Si pas de permutations (2, 1, 1) être dans cette liste? –
Je savais que j'avais oublié quelque chose. Merci, ajouté. – deleted77
Les permutations de partitions entières sont appelées "compositions". –