3
votes

Trouver la position de la première valeur non nulle dans un vecteur à partir d'une position donnée

Si j'ai un vecteur binaire x:

for ii = p:length(x)
    if x(ii)~=0
        ind = ii 
        break
    end
end

et une position (index linéaire) p :

XXX

Je veux trouver la position de la première valeur non nulle dans ce vecteur x à partir de la position p (et en se déplaçant en positif direction):

x = [1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0];
                   ↑                               ↑
           %starting position (7)         %position to find (24)   

Cela peut être fait avec une boucle for:

p = 7;

Mais existe-t-il un moyen plus intelligent / plus moyen d'atteindre le même résultat?


7 commentaires

Ho ok -_-, je viens de remarquer que nous pouvons utiliser: find (x (p + 1: end), 1) + p (direction positive) et find (x ( p: -1: 1), 1) (sens négatif)


correction: p-find (x (p: -1: 1), 1) (direction négative)


Pour les très gros x , la boucle sera l'implémentation la plus efficace!


@CrisLuengo Est-ce vrai? MATLAB crée-t-il réellement un nouveau vecteur ou passe-t-il simplement le sous-tableau par référence (copie lors de l'écriture)? find (x, 1) semble s'arrêter lorsque le premier élément différent de zéro est trouvé plutôt que de rechercher le tableau entier, au moins dans Octave.


@beaker: y = x (p: end) copie les données, les tableaux MATLAB ne font jamais référence aux sous-tableaux, pour autant que je sache. En principe, il pourrait y avoir une optimisation qui ignore la copie pour find (x (p: end)) , mais je ne pense pas que cela soit implémenté. Dans tous les cas, c'est facile à tester ... :)


@CrisLuengo Oui, attribuer une valeur à une nouvelle variable la copie, mais ce n'est pas ce que nous faisons ici. Nous passons une variable à une fonction, qui ne déclenche normalement pas de copie à moins que vous n'écriviez dans la variable. Et il est facile de tester si vous avez MATLAB, ce que je n'ai pas. ;)


@beaker: J'ai ajouté un test. Je pense qu'il est clair que le tableau est copié.


3 Réponses :


3
votes

Vous pouvez choisir le tableau de p et utiliser find.

x = [1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 1 0];
p = 7 ; 

iwant = find(x(p:end)>0,1)


5 commentaires

Je viens de remarquer que: p, il faut encore ajouter p-1 à la fin pour obtenir le bon index


Voici le document de la fonction de recherche: mathworks.com/help/matlab/ref/ find.html


Ou iwant = find (x & (1: numel (x))> = p, 1)


@Siva J'accepterai cette réponse si vous tenez compte de mon commentaire précédent.


Il n'est pas nécessaire de faire > 0 (cela le ralentit), et votre index renvoyé est l'index dans le tableau réduit, pas le tableau d'origine. Vous devez ajouter p-1 comme indiqué par @obchardon.



2
votes

Vous pouvez également utiliser la fonction bwconncomp () . Ces fonctions vous donneront la position des uns dans le tableau donné. Les informations relatives aux postes seront conservées dans la Islands.PixelIdxList . Ainsi, la position à trouver (après p bien sûr) est le premier élément du tableau ptf :

islands=bwconncomp(x);
positions=[cell2mat(islands.PixelIdxList(:))];
ptf=positions(positions(:)>p);


3 commentaires

Solution sympa et créative!


Pourquoi pas simplement positions = find (x); au lieu des deux premières lignes?


@LuisMendo cela pourrait aussi fonctionner. Je propose juste une solution alternative très utile aux situations d'images binaires!



3
votes

Un test de synchronisation rapide:

  • method1 est la boucle dans l'OP.

  • method2 est la version fixe de cette réponse , en utilisant trouver et copier le tableau.

  • (Je n'ai pas ajouté la réponse bwconncomp car elle est nettement plus lente).

Les résultats des tests dépendent, bien sûr, du nombre attendu d'éléments à visiter avant de trouver un élément différent de zéro, ainsi que de la longueur du tableau n . q est l'emplacement du premier élément non nul. Je prends toujours p = 10 . Temps en secondes:

N = 1e6;
p = 10;
x = zeros(1,N);
%x(p+5) = 1;
x(end-5) = 1;
timeit(@()method1(x,p))
timeit(@()method2(x,p))


function ind = method1(x,p)
for ii = p:length(x)
    if x(ii)~=0
        ind = ii;
        break
    end
end
end

function ind = method2(x,p)
ind = find(x(p:end),1) + p-1;
end

Ainsi, la méthode find a un temps d'exécution dicté par la surcharge de copie du tableau et d'appel de find , alors que le temps de la méthode de boucle est dicté par le nombre d'éléments de tableau à visiter pour trouver le premier élément non nul.


Code de test:

n       q       method1     method2
------  ------  ----------  ----------
1e3     p+5     5.9714e-07  2.8644e-06
1e3     end-5   3.9806e-06  3.3714e-06
1e6     p+5     6.4526e-07  0.0027
1e6     end-5   0.0029      0.0033


1 commentaires

Je dois dire que je suis surpris, mais pas choqué;)