7
votes

Comment puis-je améliorer mon algorithme pour générer des combinaisons d'un multiset?

Comment puis-je optimiser le suivant () code> et hasnext () code> méthodes dans le générateur suivant qui produit des combinaisons d'un multiviseur délimité? (J'ai posté ceci sur C ++ ainsi que Java car le code est compatible C ++ et n'a pas d'éléments spécifiques à Java qui ne convertissent pas directement en C ++.

Les zones spécifiques de l'algorithme qui sont problématiques sont l'ensemble du Hasnext () Code> Méthode qui peut être inutilement compliqué et la ligne: p>

si (actuel [xslot]> 0) aiitemsedué [actuel [xslot]] -; code] > p>

qui a une déclaration if-instruction, je pense être supprimée d'une manière ou d'une autre. J'avais une version antérieure de l'algorithme qui avait une partie de la backtracking avant la déclaration de retour et avait donc un beaucoup plus simple ( ) code> test, mais je ne pouvais pas obtenir cette version à travailler. p>

Le fond de cet algorithme est qu'il est très difficile de trouver. Par exemple, à Knuth 7.2.1.3 Il dit simplement que Il peut être fait (et donne un exercice pour prouver que l'algorithme est possible), mais ne donne pas d'algorithme. De même, j'ai une demi-douzaine de textes avancés sur Combinatoriques (y compris Papadimitriou et Kreher / Stimson. ) Et aucun d'entre eux ne donne un algorithme pour générer des combinaisons d'un multiset. Kreher le laisse comme un "exercice pour le lecteur". Quoi qu'il en soit, si vous pouvez améliorer l'algorithme comme ci-dessus ou fournir une référence à une mise en œuvre de travail plus efficace que la mienne, je l'apprécierais. S'il vous plaît donner uniquement des algorithmes itératifs (pas de récursion, s'il vous plaît). P>

    private static void combination_multiset_test(){
        int[] aiItems = { 4, 3, 2, 1, 1 };
        int iSlots = 4;
        java.util.Iterator<int[]> iterator = combination( aiItems, iSlots );
        if( iterator == null ){
            System.out.println( "null" );
            System.exit( -1 );
        }
        int xCombination = 0;
        while( iterator.hasNext() ){
            xCombination++;
            int[] combination = iterator.next();
            if( combination == null ){
                System.out.println( "improper termination, no result" );
                System.exit( -1 );
            }
            System.out.println( xCombination + ": " + Arrays.toString( combination ) );
        }
        System.out.println( "complete" );
    }


1: [4, 1, 1, 1, 2]
2: [4, 1, 1, 1, 3]
3: [4, 1, 1, 1, 4]
4: [4, 1, 1, 2, 2]
5: [4, 1, 1, 2, 3]
6: [4, 1, 1, 2, 4]
7: [4, 1, 1, 3, 4]
8: [4, 1, 2, 2, 3]
9: [4, 1, 2, 2, 4]
10: [4, 1, 2, 3, 4]
11: [4, 2, 2, 3, 4]
complete


7 commentaires

OK Clarification sur le ce que vous entendez par un multiset borné est. Mais pourriez-vous également clarifier la contrainte iSlots ? Est-ce le nombre de types dans le résultat? Ou est-ce la somme totale de la multiplicité dans le résultat? ou...


Le nombre de machines à sous est le nombre d'éléments de la combinaison. Si vous connaissez la relation binomiale C (N, K), k est le numéro de la machine à sous.


Vous voulez donc une itération sur chaque sous-ensemble d'un multiset d'une certaine cardinalité, ou une itération sur chaque sous-ensemble d'un multiset d'une certaine cardinalité ou moins? Étant donné que la sortie de votre code de test ne semble pas correspondre à votre description de votre problème.


Le code qui est la cible de l'amélioration est documenté avec un paramètre: "@param ctslots le nombre de machines à sous dans lesquelles les éléments vont". Ceci est similaire à la notation C (N, K) si vous êtes familiarisé avec la notation combinatoire "Choisir". k est le nombre de machines à sous. Ici, j'utilise la variable "ctslots" (le nombre de machines à sous)


Oh mon Dieu, votre SUB SUPPORT SUB MULTIS ne correspond pas au format de votre entrée multiples d'entrée, mais êtes plutôt [# des éléments, 1 index basé sur l'élément 0, 1 Index basé sur l'élément 1, ...] ?! Pourquoi?!


@Tylerdurden Veuillez vérifier ma solution modifiée.


@Tylerdurden J'ai une bibliothèque Java sur Github qui fera combinaisons et permutations pour plusieurs séries. Ces classes tentent d'exploiter le changement de commande pour améliorer l'efficacité.


3 Réponses :


1
votes

J'écrirais une simple classe d'assistance qui fait incrément , highbit et for_each_bit .

Je vais d'abord envelopper un < Code> non signé INT , et limitez-le à 32 bits, et peut-être l'étendre par std :: bitset ou a std :: vecteur si je se sentait ambitieux - mais en ayant ces 3 méthodes pour commencer, je peux le tester et le faire fonctionner et le faire fonctionner.

incrément est facile, sur une nue 32 bits int. < / p>

highbit renvoie la position du bit du bit de jeu le plus élevé.

for_each_bit a cette signature en C ++: < pré> xxx

appelle func avec l'index de chaque bit de jeu dans num .

qui devrait prendre au plus Quelques minutes à écrire.

jeter hasnext , suivez le concept d'itérateur - vous avez un Begin sous-ensemble et un fin < / Code> Sous-ensemble, et l'extrémité n'est pas valide pour extraire la valeur de. Dérofier à ces itérateurs produit le sous-ensemble en question (ou produit une usine pour ledit sous-ensemble).

fin est maintenant facile à travailler - si highbit est> = le nombre d'éléments de votre ensemble, vous avez passé la fin des permutations.

commence est zéro ou 1, selon si vous voulez le sous-ensemble vide à inclure.

Suivant incrémente votre Bignum .

Produire le sous-ensemble implique simplement appeler for_each_bit et mettez cet élément de votre ensemble dans le sous-ensemble.

Suivant, améliorez incrément pour permettre un accès aléatoire, et vous pouvez ensuite mettre en œuvre itérant sur les sous-ensembles en parallèle! < / p>

Cela résout le problème défini. Pour résoudre le problème Mutltiset, résolvez d'abord le problème défini dérivé (où vous prétendez qu'il n'y a que 0 ou 1 de chaque élément) et itérale sur cela. Ensuite, sur chaque itération de l'ensemble dérivé, accumulez un std :: vecteur du nombre maximum de chaque élément.

puis faites quelque chose comme ceci: < Pré> xxx

exemple en direct: http://ideone.com/8GN1XX

Dans cet exemple en direct, j'ai ignoré l'optimisation de l'itération définie d'abord et directement itéré sur le multiviseur.

(limitations: plus de max Taille_t éléments de chaque type, et pas plus de capacité maximale de std :: vecteur différents types d'éléments).

Je n'ai pas besoin de "nombre d'éléments distincts dans le Multiset ", donc je ne l'ai pas utilisé.

et voici la version itérative de l'algorithme récursif ci-dessus, à l'aide de la technique habituelle" Tournez la pile de récupération implicite en une pile explicite de la pile d'itération ": xxx

http://ideone.com/x2zp2f


7 commentaires

Je ne comprends pas bien ce que vous obtenez ici, mais vous semblez faire la même erreur que Mel Nicholson ci-dessus, ne pas comprendre la différence entre un ensemble et un multiviseur. La génération de combinaisons d'un multiset est un problème beaucoup plus difficile que de générer les combinaisons d'un ensemble.


@Tylerdurden n'a pas remarqué qu'il s'agissait d'un multiset, mais la génération de combinaisons d'un multiset est une chose triviale à faire au-dessus de la génération de combinaisons d'un ensemble. Prétendre que vous avez 1 de chaque élément au lieu de n et génèrent des combinaisons de l'ensemble dérivé. Pour chaque combinaison de l'ensemble dérivé, itérale sur le produit croisé des comptes de chaque élément (à l'exclusion du vide). Avez-vous besoin du code pour itérair sur les produits croisés des comptes de chaque élément?


Je suppose que je ne vois pas comment aller de la combinaison définie à la combinaison multiset. Si tel est tellement "trivial", pourquoi aucun livre n'a cet algorithme? De plus, si vous pensez que c'est si trivial pourquoi ne montrez-vous pas l'algorithme? Je ne suis intéressé que par les implémentations plus efficaces que la mise en œuvre que j'ai déjà montrée.


Comme indiqué ci-dessus, je cherche un algorithme itératif (non récursif).


@Tylerdurden Version itérative ajoutée. Comme la plupart des réécrites itératives d'algorithmes récursives, il est beaucoup plus facile de comprendre de manière récursive, mais il est fonctionnellement équivalent. Je viens d'enregistrer la profondeur de la taille des index et conserva la pile manuellement. Je vais revenir en arrière et ajouter des commentaires maintenant.


C'est très fantaisie, cependant, il génère le powerset pour le multiviseur et non les combinaisons dans un nombre fixe de machines à sous. Je sais que vous pouvez obtenir les combinaisons en filtrant le powerset, mais le but ici est une efficacité. Nous voulons générer uniquement les combinaisons dans un nombre fixe de machines à sous, et non le Powerset dans chaque nombre possible de machines à sous.


@Tylerdurden Oh, vous voulez les sous-ensembles d'un multiset d'une certaine cardinalité! Je me demandais ce que vous vouliez dire par emplacements .



0
votes

Cet article fournit un algorithme itératif efficace pour générer des permutations multiset à la page 8

Cet article fournit un autre algorithme itératif, également à la page 8


1 commentaires

Oui, je suis familier avec ces deux papiers. Ils iTent des permutations, pas des combinaisons.



1
votes

EDIT : DONE RÉALISATION DE RÉPONSE EN SELON DE LA QUESTION CLÉCIFIÉE

IDEA MACE: À nouveau, la sélection résultante peut être codée similaire à une personnalisation système numérique . On pourrait incrémenter un compteur et interpréter ce compteur comme une sélection.

Cependant, comme il existe une restriction supplémentaire de la taille de la sélection == cible . Un moyen naïf de mettre en œuvre la restriction est de simplement vérifier la taille de la sélection résultante et de sauter ceux qui ne satisfont pas la restriction. Mais c'est lent.

Donc, tout ce que j'ai fait était de faire un peu d'incrément intelligent qui saute à Le choix avec une taille correcte directement.

Désolé, le code est en python. Mais je l'ai fait d'une manière comparable à l'interface Java Itérator. Le format d'entrée et de sortie est le suivant: xxx

le code: xxx

Notez que les éléments L'argument n'est pas vraiment utilisé.

Le affirme dans le __ init __ est de préciser mon hypothèse sur l'entrée. < p> Le affirmer dans le __ remplissage est de montrer une propriété pratique de auto.ans dans le contexte que __ remplir est appelé.

Voici un joli squelette pour tester le code: xxx

résultat de l'entrée ([1,3, 1,2,4], 4) : xxx

performances chaque suivant () appelle O (h) h est le nombre de types d'éléments (taille de Haves liste).


3 commentaires

La valeur (4 + 1) * (3 + 1) * (1 + 1) * (2 + 1) de votre exemple est le nombre d'entrées dans le Powerset (y compris NULL). Le problème ici est de générer des combinaisons dans un nombre fixe de machines à sous, de ne pas générer le powerset.


Ah je vois, maintenant je pense que je comprends ce que vous voulez - - la même chose que je répondis, mais contraint d'avoir une taille totale spécifique? Attends un peu ... je pense que je peux modifier ...


Haha. Je vais écrire une description plus intuitive de l'algorithme plus tard. Mais pour l'instant, je voudrais souligner que mon algorithme n'utilise que 1 matrice auxiliaire pour garder une trace des progrès de l'itération actuels que ce tableau est également la réponse! Tandis que vous utilisez un supplémentaire.