Recherche de la maxima locale sur une plage dynamic

En travaillant en C #, je dois trouver tous les sumts locaux dans une liste de doublons et les renvoyer à mesure qu’une autre liste double. Cela semble assez simple si j’ai un nombre défini de valeurs que je compare dans une “fenêtre” de valeurs donnée, mais je dois être capable de passer réellement cette taille de fenêtre à la fonction elle-même. Cela peut être déroutant, mais au fond, j’ai besoin de quelque chose comme ceci:

public List FindPeaks(List values, double rangeOfPeaks) 

où si «rangeOfPeaks» était 5, la valeur «actuelle» serait comparée à 2 valeurs de chaque côté pour déterminer s’il s’agissait ou non d’un pic. Si ‘rangeOfPeaks’ était 11, la valeur actuelle serait comparée à 5 valeurs de chaque côté. Je penserais que c’était un algorithme assez basique, cependant, je n’ai pas réussi à trouver de bonnes méthodes pour détecter un pic comme celui-ci. Quelqu’un a-t-il déjà fait cela auparavant? Toute aide serait appréciée. Merci d’avance!

Il existe probablement des moyens plus efficaces, mais LINQ rend cela assez simple.

  static IList FindPeaks(IList values, int rangeOfPeaks) { List peaks = new List(); int checksOnEachSide = rangeOfPeaks / 2; for (int i = 0; i < values.Count; i++) { double current = values[i]; IEnumerable range = values; if( i > checksOnEachSide ) range = range.Skip(i - checksOnEachSide); range = range.Take(rangeOfPeaks); if (current == range.Max()) peaks.Add(current); } return peaks; } 

Je suggère quelques changements au post de Levy …

1) Le code de Levy a lancé une exception lorsque les valeurs spécifiées IList étaient une ligne presque droite.

2) Je pense que l’indice des pics du tableau correspond au résultat souhaité. Considérons par exemple ce qui se passerait si nous avions deux pics avec des doubles identiques? Ops. Changé pour retourner l’index des pics dans IList spécifié.

  public static IList FindPeaks(IList values, int rangeOfPeaks) { List peaks = new List(); double current; IEnumerable range; int checksOnEachSide = rangeOfPeaks / 2; for (int i = 0; i < values.Count; i++) { current = values[i]; range = values; if (i > checksOnEachSide) { range = range.Skip(i - checksOnEachSide); } range = range.Take(rangeOfPeaks); if ((range.Count() > 0) && (current == range.Max())) { peaks.Add(i); } } return peaks; } 

Vieille question qui a déjà une réponse acceptée, mais je voulais quelque chose de mieux que O (n ^ 2). Cette fonction est O (n * m), où m est la taille de la fenêtre et présente l’avantage de fonctionner réellement. La méthode retourne des nuplets d’indices de maxima locaux et leur valeur associée.

Les appels à Enumerable.Repeat() garantissent la Enumerable.Repeat() maxima au tout début et à la fin de l’ensemble.

La comparaison avec la queue after utilise >= afin qu’un maximum local soit trouvé au début d’un plateau de valeurs. Un effet secondaire est que la valeur d’indice 0 est renvoyée si toutes les valeurs de l’ensemble sont égales, ce qui peut être souhaitable ou non.

 public static IEnumerable> LocalMaxima( IEnumerable source, int windowSize ) { // Round up to nearest odd value windowSize = windowSize - windowSize % 2 + 1; int halfWindow = windowSize / 2; int index = 0; var before = new Queue( Enumerable.Repeat( double.NegativeInfinity, halfWindow ) ); var after = new Queue( source.Take( halfWindow + 1 ) ); foreach( double d in source.Skip( halfWindow + 1 ).Concat( Enumerable.Repeat( double.NegativeInfinity, halfWindow + 1 ) ) ) { double curVal = after.Dequeue(); if( before.All( x => curVal > x ) && after.All( x => curVal >= x ) ) { yield return Tuple.Create( index, curVal ); } before.Dequeue(); before.Enqueue( curVal ); after.Enqueue( d ); index++; } } 

Cette fonction est O (n). Il produit les résultats au fur et à mesure, de sorte qu’il aura également une surcharge de mémoire très faible.

  public static IEnumerable FindPeaks(IEnumerable values, int rangeOfPeaks) { double peak = 0; int decay = 0; foreach (var value in values) { if (value > peak || decay > rangeOfPeaks / 2) { peak = value; decay = 0; } else { decay++; } if (decay == rangeOfPeaks / 2) yield return peak; } } 

En utilisant le package Interactive Extensions de l’équipe Rx, vous pouvez résoudre ce problème parfaitement. Le paquet a beaucoup de fonctions à faire avec différents scénarios de mise en mémoire tampon / fenêtrage.

 IEnumerable FindPeaks(IEnumerable numbers, int windowSize) { // Pad numbers to the left of  so that the first window of  is centred on the first item in  // Eg if numbers = { 1, 2, 3, 4 }, windowSize = 3, the first window should be { MinValue, 1, 2 }, not { 1, 2, 3 } var paddedNumbers = Enumerable.Repeat(double.MinValue, windowSize / 2) .Concat(numbers); // Take buffers of size , stepping forward by one element each time var peaks = paddedNumbers.Buffer(windowSize, 1) .Select(range => range.Max()) .DistinctUntilChanged(); return peaks; }