चलती औसत की गणना करने के लिए कुशल एल्गोरिथ्म / डेटा संरचना


9

वर्तमान में मैं हीट पंप सिस्टम में तापमान, प्रवाह, वोल्टेज, शक्ति और ऊर्जा प्रदर्शित करने के लिए एक ग्राफिक एलसीडी सिस्टम विकसित कर रहा हूं। एक ग्राफिक एलसीडी के उपयोग का मतलब है कि मेरे SRAM का आधा और ~ 75% मेरे फ्लैश का उपयोग स्क्रीन बफर और स्ट्रिंग्स द्वारा किया गया है।

मैं वर्तमान में ऊर्जा के लिए न्यूनतम / अधिकतम / औसत आंकड़े प्रदर्शित कर रहा हूं जब आधी रात को दैनिक आंकड़ा रीसेट होता है, तो सिस्टम यह जांचता है कि दिन के लिए खपत पिछले न्यूनतम या अधिकतम से ऊपर है या नहीं, और मूल्य संग्रहीत करता है। औसत की गणना संचयी ऊर्जा खपत को दिनों की संख्या से विभाजित करके की जाती है।

मैं पिछले सप्ताह और महीने में दैनिक औसत (सादगी के लिए 4 सप्ताह) यानी एक रोलिंग औसत प्रदर्शित करना चाहूंगा। वर्तमान में इसमें पिछले 28 दिनों के मूल्यों की एक सरणी बनाए रखना और साप्ताहिक के लिए मासिक और पिछले 7 दिनों के लिए पूरे सरणी पर औसत की गणना करना शामिल है।

प्रारंभ में मैं यह फ़्लोट्स की एक सरणी का उपयोग करके कर रहा था (जैसा कि ऊर्जा "12.12kWh" के रूप में है), लेकिन यह 28 * 4 बाइट्स = 112 बाइट्स (SRAM का 5.4%) का उपयोग कर रहा था। मुझे संकल्प के केवल एक दशमलव बिंदु होने से कोई आपत्ति नहीं है, इसलिए मैं uint16_t का उपयोग करने और आंकड़ा को 100 से गुणा करने के लिए बदल गया। इसका मतलब है कि 12.12 को 1212 के रूप में दर्शाया गया है, और मैं प्रदर्शन उद्देश्यों के लिए 100 से विभाजित करता हूं।

सरणी का आकार अब 56 बाइट्स के नीचे है (बहुत बेहतर!)।

नीचे दिए गए चित्र को कम करने के लिए कोई तुच्छ तरीका नहीं है जिसे मैं देख सकता हूं। मैं एक दशमलव स्थान ("12.1kWh" के बजाय "12.12kWh") के नुकसान को बर्दाश्त कर सकता था, लेकिन खपत अक्सर 25.5kWh से अधिक होती है (255 एक 8-बिट अहस्ताक्षरित पूर्णांक द्वारा प्रतिनिधित्व किया जाने वाला उच्चतम मूल्य)। उपभोग कभी भी 10.0kWh से नीचे या 35.0kWh से ऊपर नहीं रहा है, इसलिए अनुमान है कि मैं संग्रहीत आंकड़ों से 10 घटा सकता हूं, लेकिन मुझे पता है कि एक दिन हम इन सीमाओं को पार कर जाएंगे।

फिर मैंने एक सरणी में 9-बिट मान को पैक करने के लिए कोड का परीक्षण किया। यह 0-51.2kWh की रेंज देता है और कुल 32 बाइट्स का उपयोग करता है। हालाँकि, इस तरह एक सरणी तक पहुँचना बहुत धीमी है, खासकर जब आपको एक औसत की गणना करने के लिए सभी मूल्यों पर पुनरावृत्त करना पड़ता है।

तो मेरा सवाल है - क्या तीन खिड़कियों के साथ चलती औसत की गणना करने का एक अधिक कुशल तरीका है - जीवनकाल, 28 दिन और 7 दिन? दक्षता का अर्थ SRAM उपयोग के संदर्भ में छोटा है, लेकिन विशाल कोड के दंड के बिना। क्या मैं सभी मूल्यों को संग्रहीत करने से बच सकता हूं?


क्या आप विशिष्ट खिड़कियों पर एक चलती औसत की गणना करना चाहते हैं या औसत का अनुमान / अनुमान लगा सकते हैं?
आशिष्र

मैं 7 दिन और 28 दिन की खिड़की पर एक चलती औसत चाहता हूं।
साइबरबर्गन

आप 0.2kWh के एक रिज़ॉल्यूशन का उपयोग कर सकते हैं (कारक 5 से विभाजित करें और गुणा करें) और आप अभी भी 8 बिट्स में 0-51.2kWh रेंज प्राप्त करते हैं
शाफ़्ट फ्रीक

आप बाहरी रैम या बाहरी फ्लैश में तार और अन्य स्थिरांक लगाने को समाप्त कर सकते हैं - देखें "अगर मैं फ्लैश मेमोरी या एसआरएएम से बाहर निकलता हूं तो मैं क्या कर सकता हूं?"
डेविड कैरी

जवाबों:


2

यदि आपके डेटा में कम मानक विचलन है, तो एक विधि खिड़की पर मानों को योग करने के लिए होगी, और फिर नए मूल्य को जोड़ते समय योग से घटाना रखें।

अगर कोई आउटलेयर नहीं होता है तो यह अच्छा काम करेगा , जिससे समय के साथ-साथ कुल त्रुटि शून्य हो जाएगी।

//Pseudocode

count=0
while new_reading and count<7:
    sum += new_reading        //Calculate the sum of first 7 values
    count++

while new_reading:            //Loop till new readings available
    avg = sum / 7             //Calculate average
    sum -= avg                //Subtract average from sum
    sum += new_reading        //Add next reading to sum
    print avg

2

आप एक अलग विधि का उपयोग कर सकते हैं, आप वर्तमान औसत रखते हैं और फिर करते हैं

average = (weight1*average+weight2*new_value)/(weight1+weight2);

यह एक सही रोलिंग औसत नहीं है और इसमें अलग-अलग शब्दार्थ हैं, लेकिन फिर भी यह आपकी आवश्यकताओं के अनुरूप हो सकता है

अपने 9 बिट्स प्रति वैल्यू सॉल्यूशन के लिए अधिक कुशल गणना पद्धति के लिए आप किसी मान में 8 उच्चतम बिट्स रख सकते हैं और कम से कम महत्वपूर्ण बिट्स को अलग कर सकते हैं:

uint8_t[28] highbits;
uint32_t lowbits;

एक मूल्य निर्धारित करने के लिए आपको इसे विभाजित करने की आवश्यकता है

void getvalue(uint8_t index, uint16_t value){
    highbits[index] = value>>1;
    uint32_t flag = (value & 1)<<index;
    highbits|=flag;
    highbits&=~flag;
}

जिसके परिणामस्वरूप 2 शिफ्ट ए और ए और ए नहीं

औसत की गणना करने के लिए आप इसे तेज करने के लिए विभिन्न बिट ट्रिक्स का उपयोग कर सकते हैं:

uint16_t getAverage(){
    uint16_t sum=0;
    for(uint8_t i=0;i<28;i++){
        sum+=highbits[i];
    }
    sum<<=1;//multiply by 2 after the loop
    sum+=bitcount(lowbits);
    return sum/28;
}

आप एक का उपयोग कर सकते कुशल समानांतर bitcount के लिएbitcount()


1
क्या आप अधिक बता सकते हैं कि इससे मुझे 7 और 28 दिन की औसत गणना करने की अनुमति कैसे मिलेगी?
साइबरबर्गन

मैंने इस दृष्टिकोण का उपयोग शोर के अनुरूप मूल्यों को चौरसाई करने से पहले किया है, और यह निश्चित रूप से काफी प्रभावी था। मुझे बहुत अधिक सटीकता की आवश्यकता नहीं थी, क्योंकि परिणामस्वरूप मूल्यों को एक बहुत मोटे क्वांटिसर के माध्यम से रखा जा रहा था। मुझे ऐतिहासिक औसत की भी जरूरत नहीं थी।
पीटर ब्लूमफील्ड

यह एक विशिष्ट विंडो के लिए औसत की गणना करने की अनुमति नहीं देता है।
आशिष्र

@Cybergibbons आप अलग-अलग वज़न का उपयोग कर सकते हैं खिड़की को अनुमानित करने के लिए इसलिए पुराने मूल्य पहले या बाद में महत्वहीन हो जाते हैं, या 7 दिन की खिड़की के लिए 7 दिन रखते हैं और यह 28 दिनों के औसत के लिए चलती औसत
शाफ़्ट फ्रीक

1

कैसे केवल पिछले मूल्य से अंतर को संग्रहीत करने के बारे में? इलेक्ट्रॉनिक्स में डेल्टा सिग्मा कनवर्टर नामक एक समान अवधारणा है, जिसका उपयोग DA / AD कन्वर्टर्स के लिए किया जाता है। यह इस तथ्य पर निर्भर करता है कि पिछले माप वर्तमान के पास यथोचित है।


एक और दिलचस्प विचार। दुर्भाग्य से मुझे यकीन नहीं है कि ऊर्जा की खपत हमेशा इस तरह होगी, क्योंकि यह एक हीट पंप सिस्टम है और एक दिन 30kWh, अगले 10kWh ले सकता है। मुझे वास्तव में डेटा इकट्ठा करने और देखने की जरूरत है।
Cybergibbons

0

जैसे ही आप उन्हें प्राप्त करते हैं आप केवल मूल्यों को एक साथ जोड़ नहीं सकते। तो मेरा मतलब है कि आप दिन 1 के लिए मूल्य प्राप्त करते हैं, आप इसे 1 से विभाजित करते हैं और इसे और 1 को कहीं स्टोर करते हैं। फिर आप मूल्य से 1 गुणा करें और इसे अगले मूल्य में जोड़ें और उन दोनों को 2 से विभाजित करें।

इस विधि को करने से दो या तीन चर के साथ एक रोलिंग औसत बन जाता है जैसा कि मैं सोच सकता हूं। मैं कुछ कोड लिखूंगा लेकिन मैं स्टेक्सएक्सचेंज के लिए नया हूं इसलिए कृपया मेरे साथ रहें।


मुझे समझ में नहीं आता कि यह 7 दिन और 28 दिन की खिड़की से कैसे निपटता है?
Cybergibbons

पिछले और अगले मानों पर नज़र रखें और उन्हें अपने औसत चलने वाले भाग से जोड़ते और घटाते रहें ...
आदित्य सोमानी

1
तो फिर मैं 27 दिनों के इतिहास को याद करने की आवश्यकता की स्थिति में वापस आ गया हूं, निश्चित रूप से?
Cybergibbons

मैं सोच रहा था और तुम सही हो। ताकि तकनीकी रूप से मेरा उत्तर गलत हो जाए। मैं इसमें कुछ और समय और धैर्य का निवेश कर रहा हूं। शायद कुछ आउट ऑफ द बॉक्स। अगर मैं कुछ लेकर आता हूं तो आपको बता दूंगा। हम अपने कार्यस्थल पर बहुत कुछ ऐसा करते हैं। मुझे चारों ओर से पूछने दो। भ्रम के बारे में क्षमा करें।
आदित्य सोमानी

0

28 दिनों और 7 दिनों के साथ चलती औसत की गणना करने का एक अधिक कुशल तरीका है? ... 27 दिन के इतिहास को याद रखने की जरूरत है ...?

आपको 28 मानों की बजाए करीब 11 मान प्राप्त हो सकते हैं, शायद कुछ इस तरह हैं:

// untested code
// static variables
uint16_t daily_energy[7]; // perhaps in units of 0.01 kWh ?
uint16_t weekly_energy[4]; // perhaps in units of 0.1 kWh ?

void print_week_status(){
    Serial.print( F("last week's total energy :") );
    Serial.println( weekly_energy[0] );
    int sum = 0;
    for( int i=0; i<4; i++ ){
        sum += weekly_energy[i];
    };
    Serial.print( F("Total energy over last 4 complete weeks :") );
    Serial.println( sum );
    int average_weekly_energy = sum/4;
    int average_daily_energy = average_weekly_energy/7;
    Serial.print( F("Average daily energy over last 4 weeks :") );
    Serial.println( average_daily_energy );
}
void print_day_status(){
    Serial.print( F("Yesterday's energy :") );
    Serial.println( daily_energy[0] );
    Serial.print( F("average daily energy over the last 7 complete days: ") );
    int sum = 0;
    for( int i=0; i<7; i++ ){
        sum += daily_energy[i];
    };
    int average = sum/7;
    Serial.println( average );
}

दूसरे शब्दों में, पिछले 27 दिनों के लिए हर दिन के प्रत्येक विवरण को संग्रहीत करने के बजाय, (ए) स्टोर 7 या पिछले 7 या इतने दिनों के लिए विस्तृत दैनिक जानकारी के मूल्यों, और यह भी (बी) की दुकान 4 या तो "सारांशित" पिछले 4 या तो हफ्तों में से प्रत्येक के लिए कुल या औसत जानकारी के मूल्य।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.