8
votes

Compter les entiers répétés dans un tableau

Si j'ai ce vecteur:

[~,ind] = sort(y);
x_relative_sort = x(ind);
% x_relative_sort = 1   2   3   4   6   1   2   4   6   1   2   6   1   6   1

Je voudrais obtenir la position de chaque numéro unique en fonction de lui-même.

y = sum(triu(x==x.')) % MATLAB 2016b and above

En ce moment j'utilise:

y = [1 2 3 4 5 1 2 3 1 1 2 1 2 3 4]

C'est compact mais évidemment pas efficace en mémoire.

Pour la pure beauté de la programmation MATLAB, j'éviterais d'utiliser un boucle. Avez-vous une meilleure implémentation simple?

Contexte:

Mon objectif final est de trier le vecteur x mais avec la contrainte qu'un nombre qui apparaît N fois a la priorité sur un autre numéro apparu plus de N fois:

x = [1 1 1 1 1 2 2 2 3 4 4 6 6 6 6]


3 commentaires

x = [1 1 1 1 1 2 2 2 3 4 4 6 6 6 6 1 1 1] est-il une entrée possible? Les hommes, un nombre peut-il apparaître en deux passages séparés?


Cela pourrait être l'entrée brute, mais pour faciliter les choses, j'ai déjà trié le tableau. Bien sûr, si vous pouvez trouver une solution qui fonctionne dans les deux cas sans utiliser la fonction de tri, c'est encore mieux!


C'est drôle que cela ait été demandé sur PPCG . Peut-être que certaines des solutions vous donneront des idées


5 Réponses :



5
votes

Si vous avez des entiers positifs, vous pouvez utiliser une matrice creuse:

[x_relative_sort ,~] = find(sort(sparse(x ,1:numel(x),true), 2, 'descend'));

De même, x_relative_sort peut être directement calculé:

[y ,~] = find(sort(sparse(1:numel(x), x, true), 1, 'descend'));


0 commentaires

4
votes

Juste pour la variété, voici une solution basée sur accumarray code> . Cela fonctionne pour x triés et contenant des entiers positifs, comme dans la question:

y = cell2mat(accumarray(x(:), x(:), [], @(t){1:numel(t)}).');


0 commentaires

4
votes

Vous pouvez être plus efficace en mémoire en comparant uniquement à unique (x) , vous n'avez donc pas une grande matrice N * N mais plutôt N * M , où N = numel (x), M = numel (unique (x)) .

J'ai utilisé une syntaxe de fonction anonyme pour éviter de déclarer une variable de matrice intermédiaire , nécessaire car il est utilisé deux fois - cela peut probablement être amélioré.

f = @(X) sum(cumsum(X,2).*X); y = f(unique(x).'==x);


0 commentaires

4
votes

Voici ma solution qui ne nécessite pas de tri:

% Turn each group new into a unique number:
t1 = cumsum(logical([1 diff(x)]));
%  x = [1 1 1 1 1 2 2 2 3 4 4 6 6 6 6 1 1 1];
% t1 = [1 1 1 1 1 2 2 2 3 4 4 5 5 5 5 6 6 6];

% Apply cumsum separately to each group:
t2 = cell2mat( splitapply(@(v){cumsum(v)},x,t1) );
% t1 = [1 1 1 1 1 2 2 2 3 4 4 5  5  5  5 6 6 6];
% t2 = [1 2 3 4 5 2 4 6 3 4 8 6 12 18 24 1 2 3];

% Finally, divide by x to get the increasing values:
y = t2 ./ x;
%  x = [1 1 1 1 1 2 2 2 3 4 4 6  6  6  6 1 1 1];
% t2 = [1 2 3 4 5 2 4 6 3 4 8 6 12 18 24 1 2 3];

Explication:

x = [1 1 1 1 1 2 2 2 3 4 4 6 6 6 6 1 1 1];
y = cell2mat( splitapply(@(v){cumsum(v)},x,cumsum(logical([1 diff(x)]))) ) ./ x;


0 commentaires