IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Algorithmes et structures de données Discussion :

un algo pour rendre la monnaie


Sujet :

Algorithmes et structures de données

  1. #21
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Citation Envoyé par méphistopheles Voir le message
    Serais-il possible de le traduire légerement en langage algo ?
    J'avoue ne pas y comprendre grand chose
    Rien de mystérieux, il explore l'intégralité de l'espace des solutions (c'est du NP-difficile de toute façon) et trie les solutions, la seule optimisation c'est qu'il exploite un peu la structure de la récursion pour éviter certaines additions.

    score compte le nombre de pièces dans une solution, merge mélange deux listes triées de solutions, forAGivenAmount renvoie la liste triée des solutions commençant par une certaine quantité de la pièce considérée par solve, maxAmount est juste la quantité maximale possible pour la pièce considérée (le minimum entre le nombre de pièce qu'on peut mettre dans la valeur à atteindre et le nombre de pièce réellement disponible).

    Le coût n'est pas aussi horrible qu'on pourrait le penser dans des systèmes raisonnables, et l'évaluation paresseuse signifie que si on ne demande que l'optimum, le tri n'est pas vraiment effectué, il n'y a qu'une recherche de minimum, un peu optimisée.

    --
    Jedaï

  2. #22
    Membre éprouvé
    Avatar de méphistopheles
    Profil pro
    Inscrit en
    Janvier 2005
    Messages
    1 551
    Détails du profil
    Informations personnelles :
    Âge : 36
    Localisation : France

    Informations forums :
    Inscription : Janvier 2005
    Messages : 1 551
    Points : 1 220
    Points
    1 220
    Par défaut
    Ok, c'est juste le langage qui me rebutais (c'est pas une syntaxe classique).

    à mon avis, il faut un algo qui utilise le glouton et bascule en NP-difficile lorsque qu'il manque une piece à l'algo glouton. (en reprenant du début) toutefois, il est peut être préférable de finir l'algo glouton pour s'en servir comme référence (si le nombre de pièces dans la branche devient superieur au minimum déja trouvé, on sort).
    Méphistophélès
    Si la solution ne résout pas votre problème, changez le problème...
    Cours et tutoriels C++ - FAQ C++ - Forum C++.

  3. #23
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par méphistopheles Voir le message
    heuu... les deux liens correspondent à la même adresse...
    Arg. Petite erreur de copier/coller.

    http://demonstrations.wolfram.com/Op...dyChangeMaking
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  4. #24
    Expert éminent
    Avatar de Jedai
    Homme Profil pro
    Enseignant
    Inscrit en
    Avril 2003
    Messages
    6 245
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Avril 2003
    Messages : 6 245
    Points : 8 586
    Points
    8 586
    Par défaut
    Genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    solve :: Value -> Reserve -> [Solution]
    solve 0 [] = [[]]
    solve value [] = []
    solve value ps = 
        if eagerEnough v ps 
        then solve' concat value ps
        else solve' (foldr merge []) ps
       where eagerEnough v ps = undefined
     
    solve' :: ([[Solution]] -> [Solution]) -> Value -> Reserve -> [Solution]
    solve' combine value ((pieceValue, amount) : pieces) = 
        combine $ map forAGivenAmount [maxAmount, maxAmount - 1 .. 0]
       where maxAmount = min (value `div` pieceValue) amount
             forAGivenAmount solAmount = 
                 map ((pieceValue,solAmount) :) 
                         $ solve (value - pieceValue * solAmount) pieces
     
    merge [] ys = ys
    merge xs [] = xs
    merge xs@(x:xs') ys@(y:ys') 
      | score x < score y = x : merge xs' ys
      | otherwise         = y : merge xs ys'
       where score = sum . map snd
    La fonction "eagerEnough" est laissée en exercice au lecteur courageux des articles cités par PseudoCode.

    --
    Jedaï

  5. #25
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Citation Envoyé par Jedai Voir le message
    La fonction "eagerEnough" est laissée en exercice au lecteur courageux des articles cités par PseudoCode.
    Heu... je vais en rester a ma version Java si ca ne te dérange pas.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  6. #26
    Membre expérimenté
    Avatar de coyotte507
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    1 327
    Détails du profil
    Informations personnelles :
    Âge : 33
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 1 327
    Points : 1 452
    Points
    1 452
    Par défaut Algo en n^(log10(2))*log10(n)???
    Bonjour,

    je viens avec la solution que j'ai trouvée...
    Je ne suis pas expert, donc bon, il y a peut-être une erreur mais je ne pense pas.


    Je vais essayer d'expliquer le principe de mon algorithme:


    Quand on rends des pièces, le risque le plus grand de ne pas pouvoir rendre les pièces est l'absence de petite monnaie.
    Ainsi, si je dois rendre un Euro et un centime, et que je n'ai pas de pièces de 1-2 centimes, c'est raté!

    Donc pour pouvoir rendre la monnaie, il faut déjà pouvoir éliminer les unités de centimes pour n'avoir que des dizaines de centimes.

    Il y a deux possibilités lorsqu'on veut éliminer des unités de centimes: soit on ramène directement à la dizaine la plus proche (28 centimes - 8), soit à la dizaine suivante (28 centimes - 18), mais avec des pièces différentes (c-à-d des pièces présentes dans le passage 28->20 ne le sont plus dans le passage 28->10), les autres possibilités ne sont pas intéressantes (on peut s'y ramener en enlevant des des dizaines par 2*5 ou 5*2 centimes)
    Si on prend l'exemple de 21:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    21 = 20 + 1
    21 = 10 + 5 + 3*2
    Le problème est encore présent aux étages supérieurs: dizaines de centimes, euros, dizaine d'euros.

    A chaque étage il faut envisager deux possibilités (passage à la prochaine dizaine, ou à la suivante), et il y a min(5, log10(n)) étages.

    Une fois qu'on est monté tout en haut: On réduit au maximum le nombre de centaines d'euros en utilisant des centaines d'euros, puis le nombre de dizaines d'euros en utilisant des 10aines d'euros, ....

    Ces réductions se faisant chacune en un temps constant sur un étage, elles prennent un temps de l'ordre de min(5, log10(n)) pour tout un parcours (il y a 2^(min(5, log10(n))) parcours)
    Ainsi l'algorithme prends 2^(log10(n)) * log10(n) en temps, soit n^(log10(2))*log10(n).
    Ceci dans le pire des cas, il peut y avoir moins de 2^log10(n) parcours, et à partir de n = 10000, le temps n'augmente plus...

    Comment réduire ??
    Considérons le cas où on a un nombre x de centimes, on enlève d'abord les pièces de 5 centimes au maximum. Si on a une pièce de 1 centime en réserve, on peut toujours se ramener à un nombre pair et finir avec des pièces de 2. Sinon, si le nombre sur lequel on est est impair, on reprend une pièce de 5 (si possible) de ce qu'on a enlevé, puis on achève par pièces de 2 et pièces de 1.

    Je l'ai testé, il marche à priori dans tous les cas, mais il y a sûrement moyen de réduire l'espace de stockage utilisé (par exemple avec l'allocation dynamique).

    C'est une récursion de profondeur log(n), où chaque fonction invoque dans le pire des cas deux fois elle-même. (pour celles qui remontent vers les étages supérieurs)

    La complexité de l'algo est max( n^(log10(2))*log10(n), 10000^(log10(2))*log10(10000) ), car à partir de n= 10000, l'algo s'exécute en un temps constant.

  7. #27
    Rédacteur
    Avatar de pseudocode
    Homme Profil pro
    Architecte système
    Inscrit en
    Décembre 2006
    Messages
    10 062
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Architecte système
    Secteur : Industrie

    Informations forums :
    Inscription : Décembre 2006
    Messages : 10 062
    Points : 16 081
    Points
    16 081
    Par défaut
    Dans les cas particulier de ce système monetaire (1,2,5,10,20,...) on peut optimiser l'algo que j'ai donné précédement (sortie rapide de la boucle du glouton + elaguage des branches)

    Code java : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
     
    int[] solve(int[] values, int[] dispos, int remainder) {
     
    	// sauvegarde de la meilleure solution
    	int bestscore = Integer.MAX_VALUE;
    	int[] bestsolution = null;
     
    	// recherche des solutions possibles
    	int[] solution = new int[values.length];
    	int index = 0, score = 0;
     
    	while(index>=0) {
     
    		// glouton à partir de la position courante
    		// avec une sortie immediate si on depasse le meilleur score
    		while(index<values.length && score<bestscore) {
    			solution[index]=Math.min( remainder/values[index] , dispos[index]);
    			remainder-=solution[index]*values[index];
    			score+=solution[index];
    			index++;
    		}
    		index--;
     
    		// c'est une meilleure solution : on la conserve.
    		if (remainder==0 && score<bestscore) {
    			bestscore=score;
    			bestsolution=Arrays.copyOf(solution, solution.length);
    		}
     
    		// **** backtrack *** // 
     
    		// annulation de la derniere opération effectuée
    		score-=solution[index];
    		remainder+=values[index]*solution[index];
    		solution[index]=0;
     
    		// recherche de la derniere selection non nulle
    		while(index>=0 && solution[index]==0) index--;
    		if (index<0) break;
     
    		// decrémente la selection
    		score--;
    		remainder+=values[index];
    		solution[index]--;
     
    		// poursuit l'exploration a partir de la position suivante
    		index++;
    	}
     
    	return bestsolution;
    }

    Avec l'exemple donné dans le '*.cpp' (dispo={0,1,3,0,10,10,1} et valeur=101) cela donne la solution avec 9 tours de boucle au lieu de 1176 avec mon précédant algo.
    ALGORITHME (n.m.): Méthode complexe de résolution d'un problème simple.

  8. #28
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Merci beaucoup à tous pour votre aide.
    J'ai testé tout ce que vous avez proposé, et suivi toutes les pistes que vous m'avez donné, mais je retombe toujours sur le même problème: ça rame trop. On travaille sur d'assez grosses sommes et on a des contraintes de temps assez dures.

    Du coup je suis parti sur une autre piste, qui me permet de "jongler" avec les contraintes (en gros, ça me permet de faire les calculs à des moments où j'ai le temps): tenir à jour un tableaux de toutes les sommes que je suis capable de rendre en fonction de l'argent dont je dispose.

    Encore
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  9. #29
    Rédacteur
    Avatar de Zavonen
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    1 772
    Détails du profil
    Informations personnelles :
    Âge : 76
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 1 772
    Points : 1 913
    Points
    1 913
    Par défaut
    Si ça t'intéresse j'ai implémenté un automate de distributeur de boissons (virtuel) en C. Il rend la monnaie avec l'algo glouton avec backtracking.
    Les sources sont à ta dispo.
    Ce qu'on trouve est plus important que ce qu'on cherche.
    Maths de base pour les nuls (et les autres...)

  10. #30
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Je te remercie, mais le problème c'est que la méthode que tu proposes est trop lente. Je dois calculer pour rendre la monnaie jusqu'à plus de 500€, et en plus, cet algo de résolution sera certainement utilisé lui-même dans une boucle pour des problème de gestion de BD.

    Mais finalement j'ai trouvé une méthode qui semble fonctionner super bien. En tout cas c'est super rapide, et pour l'instant, je n'ai pas trouvé de faille.

    Je me suis basé sur le principe que le système monétaire européen est canonique mais surtout, qu'il est structuré par le triplet 1,2,5. Je vous propose donc ma solution, en c++ (le code est affreux, c'est un brouillon. Il me reste à le mettre au propre et surtout à l'optimiser):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    #include <string>
    #include <vector>
    #include <iostream>
    #include <sstream>
    #include <iomanip>
     
     
    using namespace std;
     
    // système de monnaie
    vector<int> coinSystem;
     
    // affiche une solution
    void DisplayVector( const vector<int> & v )
    {
    	for ( size_t i = 0 ; i != coinSystem.size(); ++i ) 
    		cout << std::setfill( ' ' ) << std::setw(5) << coinSystem[i];
    	cout << endl;
    	for ( size_t i = 0 ; i != v.size(); ++i ) 
    		cout << std::setfill( ' ' ) << std::setw(5) << v[i];
    	cout << endl;
    }
     
    // remet à zéro toutes les valeurs d'un vecteur à partir d'un indice donné
    void ClearVector( vector<int> & v, size_t start )
    {
    	for ( size_t i=start; i<v.size(); ++i )
    		v[i]=0;
    }
     
    // calcule le montant total d'un vecteur
    int SumVector( const vector<int> & money )
    {
    	int sum = 0;
    	for ( size_t i = 0; i<money.size(); ++i )
    	{
    		sum += money[i] * coinSystem[i];
    	}
     
    	return sum;
    }
     
    // calcule de nombre de chiffres d'un nombre
    int NumDigit( int value )
    {
    	stringstream sst;
    	sst << value;
    	return (int) sst.str().size();
    }
     
    // algorithme glouton, à ceci près qu'on peut lui donner une solution partielle qu'il doit terminer
    int SolveOne( const unsigned int value, const vector<int> & availale, vector<int> & solution, size_t start = 0 )
    {
    	// on met la solution à zéro, sauf la partie que l'on ne souhaite pas modifier
    	ClearVector( solution, start );
     
    	// on fais une copie de l'argent disponible 
    	vector<int> available_copy = availale;
     
    	// on calcule de reste
    	int rest = value - SumVector( solution );
     
    	// algo glouton
    	size_t i = start;
    	while ( rest > 0 && i<coinSystem.size() )
    	{
    		if ( rest >= coinSystem[i] )
    		{
    			if ( available_copy[i]>0 )
    			{
    				rest -= coinSystem[i];
    				solution[i]++;
    				available_copy[i]--;
    			}
    			else ++i;
    		}
    		else ++i;
    	}
     
    	return rest;
    }
     
    // algo de résolution
    bool Solve( vector<int> & solution, const vector<int> & available, int value ) 
    {
    	int rest = 0;
    	int start = 0;
    	bool bImpossible = false;
     
    	do
    	{
    		// on essaie de trouver une solution avec l'algo glouton
    		rest = SolveOne( value, available, solution, start);
     
    		// si on n'a pas trouvé de solution
    		if ( rest != 0 )
    		{
    			// on trouve la plus petite valeur supérieure de la solution 
    			int valeur = 0;
    			int n = (int) coinSystem.size() - NumDigit( rest ) - 2;
    			do
    			{
    				valeur = solution[n];
    				if ( valeur == 0 ) --n;
    			}
    			while ( valeur == 0 && n>0 );
     
    			// si on n'a pas trouvé cette valeur, c'est qu'il n'y a pas de solution
    			// sinon on supprimer cette valeur à la solution et on essaie de trouver une autre solution
    			// sans modifier la partie droite de notre solution partielle
    			if ( n>0 )
    			{
    				solution[n]--;
    				rest+=coinSystem[n];
    				start = n+1;
    			}
    			else
    				bImpossible = true;
    		}
    	}
    	while ( rest > 0 && !bImpossible );
     
    	return ( rest == 0 );
    }
     
    void FillAvailable( vector<int> & dispos )
    {
    	vector<int>().swap( dispos );
    	dispos.push_back( 20 );	// 50 euros
    	dispos.push_back( 2 );	// 20 euros
    	dispos.push_back( 2 );	// 10 euros
    	dispos.push_back( 5 );	// 5 euros
    	dispos.push_back( 0 );	// 2 euros
    	dispos.push_back( 0 );	// 1 euro
    	dispos.push_back( 3 );	// 50 cts
    	dispos.push_back( 2 );	// 20 cts
    	dispos.push_back( 0 );	// 10 cts
    	dispos.push_back( 2 );	// 5 cts
    	dispos.push_back( 1 );	// 2 cts
    	dispos.push_back( 0 );	// 1 ct
    }
     
    void FillCoinSystem( vector<int> & values )
    {
    	vector<int>().swap( values );
    	values.push_back( 5000 );	// 50 euros
    	values.push_back( 2000 );	// 20 euros
    	values.push_back( 1000 );	// 10 euros
    	values.push_back( 500 );	// 5 euros
    	values.push_back( 200 );	// 2 euros
    	values.push_back( 100 );	// 1 euro
    	values.push_back( 50 );	// 50 cts
    	values.push_back( 20 );	// 20 cts
    	values.push_back( 10 );	// 10 cts
    	values.push_back( 5 );	// 5 cts	 	
    	values.push_back( 2 );	// 2 cts
    	values.push_back( 1 );	// 1 ct
    }
     
    int main(int argc, char* argv[])
    {
    	vector<int> available; 
     
    	FillCoinSystem( coinSystem );
    	FillAvailable( available );
     
    	for ( int i=1; i<50; i++ )
    	{
    		vector<int> solution( coinSystem.size() );
    		cout << "value a devolver: " << i << endl;
     
    		// resolution du problème
    		bool bHaySolution = Solve( solution, available, i );
    		if ( bHaySolution ) 
    		{
    			DisplayVector( solution);
    		} 
    		else 
    		{
    			cout << "no hay solucion" << endl;
    		}
     
    		if ( bHaySolution && SumVector( solution ) != i	)
    		{
    			cout << "error algo" << endl;
    		}
    	}
     
    	cin.get();
    	return 0;
    }
    edit: mouarf, je n'avais pas vu qu'il reste de l'espagnol dans le code. Avec les commentaires en français et les noms de fonctions et de variables en anglais... vive l'Europe!
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 2 PremièrePremière 12

Discussions similaires

  1. Algo pour enlever les yeux rouges
    Par cha266 dans le forum Algorithmes et structures de données
    Réponses: 26
    Dernier message: 25/04/2005, 11h14
  2. Déterminer Algo pour une formule mathématique
    Par jekyll_omiwane dans le forum Algorithmes et structures de données
    Réponses: 4
    Dernier message: 07/01/2005, 18h28
  3. Algo pour écrire un chiffre
    Par Skyw4lKR dans le forum Algorithmes et structures de données
    Réponses: 5
    Dernier message: 11/08/2004, 13h32
  4. [C#] Comment faire pour rendre invible une colonne(ListView)
    Par Jfrancois57 dans le forum Windows Forms
    Réponses: 3
    Dernier message: 05/05/2004, 13h27

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo