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

 C Discussion :

échantillonnage pondéré à partir de deux tableaux


Sujet :

C

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Avril 2009
    Messages : 46
    Points : 48
    Points
    48
    Par défaut échantillonnage pondéré à partir de deux tableaux
    Bonjour,
    je cherche en C l'équivalent de la fonction "sample" de R qui permet d'échantilloner un élément d'un tableau en tenant compte des poids des éléments du tableau.
    Ainsi, dans R, la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sample(c("a","b","c"), size=1, prob=c(1,2,4))
    va renvoyer 1 élément (size=1) du tableau ["a", "b", "c"] (mais ça pourrait être des nombres, peu importe) qui ont pour poids relatifs [1, 2, 4], signifiant que "c" a deux fois plus de chances d'être tiré que "b" qui lui même a deux fois plus de chance d'être tiré que "a".

    Je ne sais pas si vous connaissiez une fonction rapide équivalente, mais je ne trouve pas sur internet. Me suis enfin lancé dans un projet un peu ambitieux que j'aimerai être rapide (d'où le C), mais je me suis trop habitué à R où beaucoup de fonctions sont déjà disponibles facilement.
    Ce n'est pas évoqué non plus dans "numerical recipes in C" ...

    Merci beaucoup d'avoir lu mon message en tout cas !
    Bises,
    M.

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Bonjour,
    pour les nombres aléatoire en C, on utilise srand() et rand().
    Pour ton problème précis, il suffit de compter dans l'ordre.

    Pour une distribution de poids {1, 2, 4}, il y a 7 tirages valides. Le premier correspond au premier élément, les deux suivant au deuxièmes, et les quatres derniers au troisieme.

    par exemple, tu peux utiliser une boucle simple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int sample(int source[], int taille_source, int poids[], int taille_poids) {
        index_max = pour tout i de [0, taille_poids[, somme(poids[i])
        index = nombre aléatoire dans [0, index_max[
     
        n = 0;
        tant que index > poids[n] && n < taille_source-1
            index -= poids[n]
            ++n
        return source[n]
    }
    Reste à traduire en C, protéger ton code, et écrire un commentaire précis disant quels précautions prendre à l'appel.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Avril 2009
    Messages : 46
    Points : 48
    Points
    48
    Par défaut
    Bonjour, et un grand merci.
    Ce n'est pas bête en effet, je comprends l'idée, dommage que je n'y ai pas pensé.
    Tu penses que la structure de ce code serait la même si les poids relatifs n'étaient pas des entiers mais des nombres à virgules?
    M.

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    D'après toi?
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Expert éminent sénior
    Avatar de Sve@r
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Février 2006
    Messages
    12 689
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Oise (Picardie)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Février 2006
    Messages : 12 689
    Points : 30 983
    Points
    30 983
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par leternel Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int sample(int source[], int taille_source, int poids[], int taille_poids) {
        index_max = pour tout i de [0, taille_poids[, somme(poids[i])
        index = nombre aléatoire dans [0, index_max[
     
        n = 0;
        tant que index > poids[n] && n < taille_source-1
            index -= poids[n]
            ++n
        return source[n]
    }
    Bonjour

    Il me semble que les deux tableaux étant de même taille, un des deux paramètres "taille_source" et "taille_poids" est de trop.
    Par ailleurs, à quoi sert le tableau réllement ? Juste à avoir un élément à renvoyer ? Dans ce cas, autant ne renvoyer que l'indice de l'élément en question et hop, le tableau n'a plus de raison d'être.
    Et enfin si les poids "utiles" sont tous positifs, alors on peut utiliser le poids "0" comme indicateur de fin de tableau ce qui permet aussi de faire sauter le paramètre de sa taille. Et si une pondération à 0 est théoriquement possible, alors utiliser "-1" comme flag...
    Dès lors, ça doit vachement simplifier la fonction...

    Citation Envoyé par mycaweb Voir le message
    Je cherche en C l'équivalent de la fonction "sample" de R qui permet d'échantilloner un élément d'un tableau en tenant compte des poids des éléments du tableau.
    Ainsi, dans R, la ligne
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sample(c("a","b","c"), size=1, prob=c(1,2,4))
    va renvoyer 1 élément (size=1)
    La fonction de Leternel ne renvoie qu'un élément. Si vraiment tu veux pouvoir reproduire le comportement complet (et pouvoir renvoyer plusieurs éléments) il te faut alors allouer dans la fonction un tableau dédié à stocker les éléments sélectionnés puis renvoyer le tableau alloué. Et dans ce cas, ne pas oublier de libérer ensuite le tableau alloué. Ce n'est pas que ce soit très difficile mais peut-être un peu démesuré par rapport à ton besoin (à toi de voir)...

    Citation Envoyé par mycaweb Voir le message
    Tu penses que la structure de ce code serait la même si les poids relatifs n'étaient pas des entiers mais des nombres à virgules?
    Là, t'as raté une occasion de briller...
    Mon Tutoriel sur la programmation «Python»
    Mon Tutoriel sur la programmation «Shell»
    Sinon il y en a pleins d'autres. N'oubliez pas non plus les différentes faq disponibles sur ce site
    Et on poste ses codes entre balises [code] et [/code]

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Avril 2009
    Messages : 46
    Points : 48
    Points
    48
    Par défaut
    Hello,
    me revoilà avec un peu plus d'expériences après cette semaine à faire du C.
    J'ai réfléchi à la solution proposée ici avec plus d'armes que la semaine passée, et je me suis dis que ça avait l'air d'être très lent pour les très longues listes.
    Intuitivement, je me suis dis qu'on pouvait accélérer l'algorithme en m'y prenant autrement, en faisant des tirages multinomiaux.
    L'idée est d'avoir le nombre de succès pour chacun des trois éléments de ma liste {"a","b","c"} dont les poids sont{1,2,4} après N expériences.
    Par exemple, si N = 1,000, je m'attendrai à quelque chose ressemblant à:
    nombre de succès de tirer 'a': 140
    nombre de succès de tirer 'b': 280
    nombre de succès de tirer 'c': 580

    Donc au tableau {140, 280, 580}.

    La suite serai de faire un tableau de longueur N (=1000 ici), contenant 140 "a", 280 "b" et 580 "c", mais dont les positions sont désordonnées aléatoirement.

    J'ai donc codé ce truc en C, mais suis surpris par la raclée que m'inflige R quand je compare les temps sur 500 exécutions, presque 400 fois plus rapide.

    Ici, les listes ne sont pas {"a", "b", "c"}, et les poids ne sont pas {"1", "2", "4"}, mais l'idée est la même:
    En R:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    system.time(for(i in 1:500){x=sample(c(2, 4, 6, 8, 10), 20, replace=T, c(1.2, 0.6, 0.3, 0.15, 0.0))})
    utilisateur     système      écoulé 
          0.003       0.000       0.003
    EN C:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    ~/Documents/C/sample$ time for i in $(seq 1 1 500); do ./randomSampling 123 >/dev/null ; done
     
    real	0m1.092s
    user	0m0.175s
    sys	0m0.973s
    Or, ça sera une fonction que je compte appeler 5 millions de fois dans mon programme final (un simulateur nécessitant beaucoup de brassage aléatoire en tenant compte de poids).

    Pensez vous que c'est mon idée qui n'est pas bonne? Qu'elle est mal exécutée? La solution de leternel est-elle vraiment plus rapide?

    Merci beaucoup pour vos lumières!

    (voici mon code final à compiler lancer ainsi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    ~/Documents/C/sample$ gcc p.c -L/usr/local/lib  -lgsl -lgslcblas -lm -o randomSampling
    ~/Documents/C/sample$ ./randomSampling 123
    )

    M.
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <gsl/gsl_math.h>
    #include <gsl/gsl_rng.h>
    #include <gsl/gsl_randist.h>
    #include <gsl/gsl_statistics.h>
    #include <gsl/gsl_permutation.h>
     
    void weightedSample(gsl_rng* r, double* liste, double* weights, double* target, int sizeOfListe, int nTrials);
     
    int main(int argc, char* argv[]){
    	int i =0;
    	int seed = atoi(argv[1]);
     
            // Random generator
            const gsl_rng_type *T;
            gsl_rng *r;
            gsl_rng_env_setup();
     
            T=gsl_rng_default;
            r=gsl_rng_alloc(T);
            gsl_rng_set(r, seed);
     
    	int K = 5; // longueur de liste et de weights
    	double liste[5] = {2, 4, 6, 8, 10};	// vector of elements from which to choose
    	double weights[5] = {1.2, 0.6, 0.3, 0.15, 0.05};	// A vector of probability weights for obtaining the elements of the vector being sampled
     
    	unsigned int N = 20; // nombre d'essais
    	double target[20] = {0};	// 
     
    	weightedSample(r, liste, weights, target, K, N);	// performs N weighted_random_sampling + shuffling of 'liste' (size K), stock the output in 'target'
     
    	for(i=0; i<N; i++){
    		printf("%.lf ", target[i]);
    	}
    	printf("\n");
     
    	return(0);
    }
     
    void weightedSample(gsl_rng* r, double* liste, double* weights, double* target, int sizeOfListe, int nTrials){
    	int i = 0;
    	int* n = NULL;
    	int* sampledListe = NULL;
    	n = malloc(sizeOfListe * sizeof(double));	// will contain the number of succes after N nTrials for each of the sizeOfListe elements of liste
    	sampledListe = malloc(nTrials * sizeof(int));	// if n={0, 3, 1, 1}, sampledListe={1, 1, 1, 4, 5}
     
    	gsl_ran_multinomial(r, sizeOfListe, nTrials, weights, n);	// return in 'n' the number of success for the sizeOfListe elements of liste
     
    	int nValues = 0;
    	int tmp = 0;
    	int tmp2 = 0;
    	for(i=0; i<sizeOfListe; i++){ // loop along the list called 'n' resulting from gsl_ran_multinomial
    		nValues = n[i];
    		if(nValues != 0){
    			tmp2 = 0;
    			do{
    				sampledListe[tmp] = i;
    				tmp++;
    				tmp2++;
    			}while(tmp2 < nValues);
    		}
    	}
     
    	// shuffle values of the sampledListe: obtaining something like {1, 4, 1, 5, 1} from the initial {1, 1, 1, 4, 5}
    	gsl_permutation* p = gsl_permutation_alloc (nTrials);
    	gsl_permutation_init (p);
    	gsl_ran_shuffle(r, p -> data, nTrials, sizeof(size_t));
     
    	tmp = 0;	
    	for(i=0; i<nTrials; i++){
    		tmp=gsl_permutation_get(p, i);
    		target[i]=liste[sampledListe[tmp]];
    	}
     
    	free(n);
    	free(sampledListe);
    	gsl_permutation_free(p);
    }

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Juin 2009
    Messages
    4 481
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 481
    Points : 13 679
    Points
    13 679
    Billets dans le blog
    1
    Par défaut
    Bonjour,

    Tu devrais déjà commencer par demander à gcc d'activer ses optimisations : rajoute l'option -02 ou -03 et retente.

    Une fois que cela sera fait, il faudra (sans doute ? peut-être ?) optimiser le code

  8. #8
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2009
    Messages
    46
    Détails du profil
    Informations personnelles :
    Localisation : Suisse

    Informations forums :
    Inscription : Avril 2009
    Messages : 46
    Points : 48
    Points
    48
    Par défaut
    Viens de réaliser que je ne comparais pas les mêmes choses.
    Dans R, je chronométrais le temps pour lancer 500 fois la fonction "sample", alors que dans C, je chronométrais 500 fois tout mon script (1: générer les donner, 2: allouer l'espace; 3: lancer la fonction "sample").

    Si j'intègre la boucle sur la fonction spécifiquement, c'est beaucoup mieux.
    Pour 500,000 exécution de la fonction sample, j'ai:
    avec R:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    system.time(for(i in 1:5e5){x=sample(c(2, 4, 6, 8, 10), 20, replace=T, c(1.2, 0.6, 0.3, 0.15, 0.0))})
    utilisateur     système      écoulé 
          3.025       0.002       3.028
    en C:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    :~/Documents/C/sample$ time ./randomSampling 123 
     
    real	0m0.603s
    user	0m0.599s
    sys	0m0.004s
    c.f le code en dessous
    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
    #include <stdio.h>
    #include <stdlib.h>
    #include <gsl/gsl_math.h>
    #include <gsl/gsl_rng.h>
    #include <gsl/gsl_randist.h>
    #include <gsl/gsl_statistics.h>
    #include <gsl/gsl_permutation.h>
     
    void weightedSample(gsl_rng* r, double* liste, double* weights, double* target, int sizeOfListe, int nTrials);
     
    int main(int argc, char* argv[]){
    	int i =0;
    	int seed = atoi(argv[1]);
     
            // Random generator
            const gsl_rng_type *T;
            gsl_rng *r;
            gsl_rng_env_setup();
     
            T=gsl_rng_default;
            r=gsl_rng_alloc(T);
            gsl_rng_set(r, seed);
     
    	int K = 5; // longueur de liste et de weights
    	double liste[5] = {2, 4, 6, 8, 10};	// vector of elements from which to choose
    	double weights[5] = {1.2, 0.6, 0.3, 0.15, 0.05};	// A vector of probability weights for obtaining the elements of the vector being sampled
     
    	unsigned int N = 20; // nombre d'essais
    	double target[20] = {0};	// 
     
    	int z = 0;
    	for(z=0; z<500000; z++){	// lance 500,000 fois la fonction sample
    		weightedSample(r, liste, weights, target, K, N);	// performs N weighted_random_sampling + shuffling of 'liste' (size K), stock the output in 'target'
    	}
    	return(0);
    }
     
    void weightedSample(gsl_rng* r, double* liste, double* weights, double* target, int sizeOfListe, int nTrials){
    	int i = 0;
    	int* n = NULL;
    	int* sampledListe = NULL;
    	n = malloc(sizeOfListe * sizeof(double));	// will contain the number of succes after K nTrials for each of the sizeOfListe elements of liste
    	sampledListe = malloc(nTrials * sizeof(int));	// if n={0, 3, 1, 1}, sampledListe={1, 1, 1, 4, 5}
     
    	gsl_ran_multinomial(r, sizeOfListe, nTrials, weights, n);	// return in 'n' the number of success for the sizeOfListe elements of liste
     
    	int nValues = 0;
    	int tmp = 0;
    	int tmp2 = 0;
    	for(i=0; i<sizeOfListe; i++){ // loop along the list called 'n' resulting from gsl_ran_multinomial
    		nValues = n[i];
    		if(nValues != 0){
    			tmp2 = 0;
    			do{
    				sampledListe[tmp] = i;
    				tmp++;
    				tmp2++;
    			}while(tmp2 < nValues);
    		}
    	}
     
    	// shuffle values of the sampledListe
    	gsl_permutation* p = gsl_permutation_alloc (nTrials);
    	gsl_permutation_init (p);
    	gsl_ran_shuffle(r, p -> data, nTrials, sizeof(size_t));
     
    	tmp = 0;	
    	for(i=0; i<nTrials; i++){
    		tmp=gsl_permutation_get(p, i);
    		target[i]=liste[sampledListe[tmp]];
    	}
    	gsl_permutation_free(p);
    	free(n);
    	free(sampledListe);
    }
    P.S. vais tenter demain matin l'algo de Leternel, commence à saturer pour aujourd'hui

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Python 2.X] Obtention de Graphe en 3D à partir de deux tableaux Nx1 et d'un tableau NxN
    Par cosupoka dans le forum Programmation multimédia/Jeux
    Réponses: 4
    Dernier message: 07/11/2014, 19h38
  2. Tableau associatif à partir de deux tableaux
    Par faitor1 dans le forum Langage
    Réponses: 2
    Dernier message: 05/09/2014, 14h44
  3. Création indicatrice à partir de deux tableaux
    Par Ineedi2 dans le forum SAS Base
    Réponses: 3
    Dernier message: 23/05/2014, 11h02
  4. Remplir un hash à partir de deux tableaux
    Par USMC666 dans le forum Langage
    Réponses: 11
    Dernier message: 21/10/2013, 09h36
  5. Réponses: 54
    Dernier message: 16/03/2006, 11h42

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