Thursday 23 February 2017

C ++ Moyenne Mobile

Je sais que cela est réalisable avec boost selon: Mais je voudrais vraiment éviter d'utiliser boost. J'ai googlé et n'a pas trouvé d'exemples appropriés ou lisibles. Essentiellement, je veux suivre la moyenne mobile d'un flux continu d'un flux de nombres à virgule flottante en utilisant les plus récents numéros 1000 comme un échantillon de données. Quel est le moyen le plus simple pour atteindre ce que j'ai expérimenté avec l'aide d'un tableau circulaire, moyenne mobile exponentielle et une moyenne mobile plus simple et a constaté que les résultats de la matrice circulaire convenait mieux à mes besoins. Si vos besoins sont simples, vous pouvez simplement essayer d'utiliser une moyenne mobile exponentielle. Autrement dit, vous créez une variable d'accumulateur, et comme votre code regarde chaque échantillon, le code met à jour l'accumulateur avec la nouvelle valeur. Vous choisissez un alpha constant qui se situe entre 0 et 1, et calculez ceci: Il vous suffit de trouver une valeur de alpha où l'effet d'un échantillon donné ne dure que pour environ 1000 échantillons. Hmm, je ne suis pas sûr que ce soit approprié pour vous, maintenant que Ive mis ici. Le problème est que 1000 est une fenêtre assez longue pour une moyenne mobile exponentielle Im pas sûr il ya un alpha qui serait la propagation de la moyenne sur les 1000 derniers chiffres, sans underflow dans le calcul en virgule flottante. Mais si vous voulez une moyenne plus petite, comme 30 nombres ou ainsi, c'est une manière très facile et rapide de le faire. A répondu 12 juin à 4:44 1 sur votre poste. La moyenne mobile exponentielle peut permettre à l'alpha d'être variable. Ainsi, cela permet de calculer des moyennes de base de temps (par exemple, des octets par seconde). Si le temps écoulé depuis la dernière mise à jour de l'accumulateur est supérieur à 1 seconde, laissez alpha be 1.0. Sinon, vous pouvez laisser alpha be (usecs depuis la dernière mise à jour1000000). Ndash jxh 12 juin à 6:21 Je veux essentiellement suivre la moyenne mobile d'un flux continu d'un flux de nombres à virgule flottante en utilisant les plus récents numéros 1000 comme un échantillon de données. Notez que la mise à jour ci-dessous le total en tant qu'éléments comme addedreplaced, en évitant coûteux O (N) traversal pour calculer la somme - nécessaire pour la moyenne - sur demande. Le total est fait d'un paramètre différent de T à un support, par ex. En utilisant un long long pour un total de 1000 s longs, un int pour char s, ou un flottant double au total. C'est un peu vicié en ce que les numsamples pourraient dépasser INTMAX - si vous vous inquiétez vous pourriez employer un unsigned long long. Ou utiliser un membre de données bool supplémentaire pour enregistrer quand le conteneur est rempli tout en cyclant numsamples autour du tableau (mieux renommé quelque chose d'inoffensif comme pos). Répondue 12 juin à 5:19 on suppose que l'opérateur quotvoid (échantillon T) est effectivement opérateur quotvoid (T échantillon) quot. Ndash oPless Jun 8 14 at 11:52 oPless ahhh. Bien repéré. En fait, je voulais qu'il soit vide opérateur () (T échantillon), mais bien sûr, vous pouvez utiliser n'importe quelle note que vous avez aimé. Correction, merci. Ndash Tony D Jun 8 14 à 14: 27I utilise le code suivant: int GetAnalogValue (Input) retourne une valeur de 12 bits à partir d'une distance de mesure du capteur ultrasonique (0-4095, ANALOGVALUERANGE 4096). La double compensation est un membre de la classe d'encapsulation. ReferenceValue est la valeur de référence initiale de référenceInput (normalement, elle a une faible fluctuation, avec une valeur quelque part au milieu de la plage de 12 bits, mais elle change avec la température). RecordData () est appelé toutes les 2 millisecondes (plus ou moins, s'exécutant sous Windows). Donc, un taux de mise à jour élevé pour la valeur de référence, mais je voulais une compensation progressive au fil du temps. Par conséquent, j'ai utilisé une moyenne exponentielle mobile, le delta est déterminé empiriquement et mai besoin d'être modifié plus tard. GetData () est appelé environ toutes les secondes pour vider les données et utiliser l'enregistrement pour une évaluation ultérieure. Nous avons commencé à tester ce code sur les installations du client, mais nous avons obtenu la rétroaction que les valeurs sont à la dérive après quelques jours. Je pense que c'est un algorithme numérique instable qui accumule les erreurs d'arrondissement. Est-ce que quelqu'un avec plus d'expérience avec les subtilités de l'arithmétique de virgule flottante me répondent: Est-ce que cet algorithme de moyenne mobile exponentielle peut être amélioré pour obtenir une meilleure stabilité Notes pour des améliorations supplémentaires du code sont bien accueillies aussi. J'essaie de calculer la moyenne mobile d'un signal. La valeur du signal (un double) est mise à jour au hasard. Je cherche un moyen efficace de calculer sa moyenne pondérée en fonction du temps sur une fenêtre temporelle, en temps réel. Je pouvais le faire moi-même, mais c'est plus difficile que je pensais. La plupart des ressources que j'ai trouvées sur Internet sont le calcul de la moyenne mobile du signal périodique, mais les mises à jour de la mine au hasard. Est-ce que quelqu'un sait de bonnes ressources pour cela L'astuce est la suivante: Vous obtenez des mises à jour au hasard fois via la mise à jour vide (temps int, valeur flottante). Cependant, vous devez aussi suivre quand une mise à jour tombe de la fenêtre de temps, donc vous définissez une alarme qui a appelé à l'heure N qui supprime la mise à jour précédente d'être jamais considéré à nouveau dans le calcul. Si cela se produit en temps réel, vous pouvez demander au système d'exploitation de faire un appel à une méthode void dropoffoldestupdate (int time) à appeler au moment N Si c'est une simulation, vous ne pouvez pas obtenir d'aide du système d'exploitation et vous devez Le faire manuellement. Dans une simulation, on appellerait des méthodes avec le temps fourni comme argument (qui ne correspond pas au temps réel). Cependant, une hypothèse raisonnable est que les appels sont garantis pour être tels que les arguments de temps sont en augmentation. Dans ce cas, vous devez conserver une liste triée des valeurs d'heure d'alarme et pour chaque mise à jour et appel de lecture vous vérifiez si l'argument de temps est supérieur à la tête de la liste d'alarmes. Bien qu'il soit plus important, vous effectuez le traitement d'alarme (déposez la mise à jour la plus ancienne), retirez la tête et vérifiez à nouveau jusqu'à ce que toutes les alarmes avant le moment donné soient traitées. Ensuite, effectuez l'appel de mise à jour. J'ai jusqu'à présent supposé qu'il est évident ce que vous feriez pour le calcul réel, mais je vais élaborer juste au cas où. Je suppose que vous avez une méthode float read (int time) que vous utilisez pour lire les valeurs. L'objectif est de rendre cet appel aussi efficace que possible. Donc, vous ne calculez pas la moyenne mobile chaque fois que la méthode de lecture est appelée. Au lieu de cela vous précomputer la valeur à partir de la dernière mise à jour ou de la dernière alarme, et tordre cette valeur par un couple d'opérations en virgule flottante pour tenir compte du passage du temps depuis la dernière mise à jour. (C'est-à-dire un nombre constant d'opérations sauf pour le traitement peut-être d'une liste d'alarmes). Espérons que cela est clair - ce devrait être un algorithme assez simple et très efficace. Optimisation supplémentaire. L'un des problèmes restants est si un grand nombre de mises à jour se produisent dans la fenêtre de temps, puis il ya une longue période pour laquelle il n'y a ni lectures ni mises à jour, puis une lecture ou une mise à jour arrive. Dans ce cas, l'algorithme ci-dessus sera inefficace dans la mise à jour incrémentielle de la valeur de chacune des mises à jour qui est en baisse. Cela n'est pas nécessaire car nous ne nous soucions de la dernière mise à jour au-delà de la fenêtre de temps si il ya un moyen de déposer efficacement toutes les mises à jour plus anciennes, il serait utile. Pour ce faire, nous pouvons modifier l'algorithme pour faire une recherche binaire de mises à jour pour trouver la mise à jour la plus récente avant la fenêtre de temps. S'il ya relativement peu de mises à jour qui doivent être supprimées, il est possible de mettre à jour progressivement la valeur de chaque mise à jour abandonnée. Mais s'il ya beaucoup de mises à jour qui doivent être supprimées, alors on peut recalculer la valeur à partir de zéro après avoir abandonné les anciennes mises à jour. Annexe sur le calcul incrémental: Je devrais préciser ce que je veux dire par le calcul incrémental ci-dessus dans la phrase de tordre cette valeur par un couple d'opérations en virgule flottante pour tenir compte du passage du temps depuis la dernière mise à jour. Calcul initial non incrémentiel: itérer ensuite sur les dates d'actualisation en fonction de l'augmentation du temps: movingaverage (sum lastupdate timesincelastupdate) windowlength. Maintenant, si exactement une mise à jour tombe de la fenêtre mais pas de nouvelles mises à jour arriver, ajuster la somme comme: (note c'est priorupdate qui a son horodatage modifié au début de la dernière fenêtre début). Et si exactement une mise à jour entre dans la fenêtre, mais qu'aucune nouvelle mise à jour ne s'arrête, ajustez la somme comme: Comme cela devrait être évident, il s'agit d'un schéma approximatif mais nous espérons qu'il vous montrera comment vous pouvez maintenir la moyenne telle que c'est O (1) Sur une base amortie. Mais remarquez une optimisation supplémentaire dans le paragraphe précédent. Notez également les problèmes de stabilité mentionnés dans une réponse plus ancienne, ce qui signifie que des erreurs de virgule flottante peuvent s'accumuler sur un grand nombre d'opérations incrémentielles telles qu'il ya une divergence par rapport au résultat du calcul complet qui est significatif pour l'application. Si une approximation est OK et theres un temps minimum entre les échantillons, vous pouvez essayer de super-échantillonnage. Avoir un tableau qui représente des intervalles de temps régulièrement espacés qui sont plus courts que le minimum, et à chaque période de temps stocker le dernier échantillon qui a été reçu. Plus l'intervalle est court, plus la moyenne sera proche de la valeur réelle. La période ne doit pas être supérieure à la moitié du minimum ou il est possible de manquer un échantillon. Répondre Dec 15 11 at 18:12 réponse Dec 15 11 at 22:38 Merci pour la réponse. Une amélioration qui serait nécessaire pour quotcachequot la valeur de la moyenne totale donc nous don39t boucle tout le temps. En outre, il peut être un point mineur, mais ne serait-il pas plus efficace d'utiliser un deque ou une liste pour stocker la valeur, puisque nous supposons que la mise à jour viendra dans le bon ordre. L'insertion serait plus rapide que dans la carte. Ndash Arthur Dec 16 11 at 8:55 Oui, vous pouvez mettre en cache la valeur de somme. Soustrayez les valeurs des échantillons que vous effacez, ajoutez les valeurs des échantillons que vous insérez. Aussi, oui, un dequeltpairltSample, Dategtgt pourrait être plus efficace. J'ai choisi la carte pour la lisibilité, et la facilité d'invoquer map :: upperbound. Comme toujours, écrire le code correct en premier, puis profil et mesurer les changements incrémentiels. Ndash Rob Dec 16 11 at 15:00 Note: Apparemment, ce n'est pas la façon d'aborder cela. Laissant ici pour référence sur ce qui ne va pas avec cette approche. Vérifiez les commentaires. MISE À JOUR - basé sur Olis commentaire. Pas sûr de l'instabilité dont il parle cependant. Utilisez une carte triée des heures d'arrivée par rapport aux valeurs. À l'arrivée d'une valeur ajouter l'heure d'arrivée à la carte triée avec sa valeur et mettre à jour la moyenne mobile. Avertissement c'est pseudo-code: Là. Pas complètement étoffé mais vous obtenez l'idée. Choses à noter. Comme je l'ai dit le code ci-dessus est pseudo. Vous aurez besoin de choisir une carte appropriée. Ne supprimez pas les paires pendant l'itération car vous invalidez l'itérateur et vous devrez recommencer. Voir Olis commentaire ci-dessous aussi. Répondre Dec 15 11 at 12:22 Ce doesn39t travail: il doesn39t prendre en compte quelle proportion de la fenêtre de longueur chaque valeur existe pour. En outre, cette approche d'addition et de soustraction est seulement stable pour les types d'entiers, pas les flotteurs. Ndash Oliver Charlesworth Dec 15 11 at 12:29 OliCharlesworth - désolé j'ai manqué quelques points clés dans la description (double et pondéré en temps). Je vais mettre à jour. Merci. Ndash Dennis Dec 15 11 at 12:33 La pondération de temps est encore un autre problème. Mais ce n'est pas ce dont je parle. Je faisais allusion au fait que quand une nouvelle valeur entre d'abord dans la fenêtre de temps, sa contribution à la moyenne est minime. Sa contribution continue d'augmenter jusqu'à l'entrée d'une nouvelle valeur. Ndash Oliver Charlesworth 15 déc à 12:35


No comments:

Post a Comment