क्या इस एकीकरण कोड को अनुकूलित करना संभव है ताकि यह तेजी से चले?


9
double trap(double func(double), double b, double a, double N) {
  double j;
  double s;
  double h = (b-a)/(N-1.0); //Width of trapezia

  double func1 = func(a);
  double func2;

  for (s=0,j=a;j<b;j+=h){
    func2 = func(j+h);
    s = s + 0.5*(func1+func2)*h;
    func1 = func2;
  }

  return s;
}

ऊपर की func()सीमाओं के बीच 1 डी संख्यात्मक एकीकरण (विस्तारित ट्रेपेज़ियम नियम का उपयोग करके) के लिए मेरा सी ++ कोड है[a,b] का उपयोग करते हुए N1 trapezia।

मैं वास्तव में एक 3D एकीकरण कर रहा हूं, जहां इस कोड को पुनरावर्ती कहा जाता है। मैं साथ काम करता हूं जिससे मुझे अच्छे परिणाम मिले।N=50

और कम करने के अलावा, क्या कोई सुझाव दे सकता है कि ऊपर दिए गए कोड को कैसे अनुकूलित किया जाए ताकि यह तेजी से चले? या, यहां तक ​​कि, एक तेज एकीकरण विधि का सुझाव दे सकता है?N


5
यह वास्तव में प्रश्न के लिए प्रासंगिक नहीं है, लेकिन मैं बेहतर चर नाम चुनने का सुझाव दूंगा। के trapezoidal_integrationबजाय trap, sumया के running_totalबजाय s(और भी के +=बजाय का उपयोग करें s = s +), trapezoid_widthया के dxबजाय h(या नहीं, trapezoidal नियम के लिए अपने पसंदीदा अंकन के आधार पर), और परिवर्तन func1और func2इस तथ्य को प्रतिबिंबित करने के लिए कि वे मूल्य हैं, कार्य नहीं। जैसे func1-> previous_valueऔर func2-> current_value, या ऐसा ही कुछ।
डेविड जेड

जवाबों:


5

गणितीय रूप से, आपकी अभिव्यक्ति इसके बराबर है:

I=h(12f1+f2+f3+...+fn1+12fn)+O((ba)3fn2)

इसलिए आप इसे लागू कर सकते हैं। जैसा कि कहा गया था, समय संभवतः फ़ंक्शन मूल्यांकन पर हावी है, इसलिए समान सटीकता प्राप्त करने के लिए, आप एक बेहतर एकीकरण विधि का उपयोग कर सकते हैं जिसके लिए कम फ़ंक्शन मूल्यांकन की आवश्यकता होती है।

गॉसियन क्वाड्रचर आधुनिक दिनों में, एक खिलौने से थोड़ा अधिक है; केवल उपयोगी है अगर आपको बहुत कम मूल्यांकन की आवश्यकता होती है । यदि आप कुछ आसान लागू करना चाहते हैं, तो आप सिम्पसन के नियम का उपयोग कर सकते हैं, लेकिन मैं एक अच्छे कारण के बिना आदेश से आगे नहीं जा सकता ।1/N3

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


दूर जाने और समस्या पर वापस आने के बाद, मैंने सिम्पसन के शासन को लागू करने का फैसला किया है। लेकिन क्या मैं यह जांच सकता हूं कि वास्तव में समग्र सिम्पसन के नियम में त्रुटि 1 / (N ^ 4) के समानुपाती है (1 / (N ^ 3) नहीं है जैसा कि आप अपने उत्तर में करते हैं)?
user2970116

1
आपके पास सूत्र हैं 1/N3 साथ ही साथ 1/N4। पहले वाला गुणांक का उपयोग करता है5/12,13/12,1,1...1,1,13/12,15/12 और दूसरा 1/3,4/3,2/3,4/3...
डेविड जूल

9

संभावना है कि कार्यों का मूल्यांकन इस गणना का सबसे अधिक समय लेने वाला हिस्सा है। यदि ऐसा है, तो आपको एकीकरण दिनचर्या को तेज करने की कोशिश करने के बजाय फंक () की गति में सुधार करने पर ध्यान केंद्रित करना चाहिए।

फ़ंक () के गुणों के आधार पर, यह भी संभावना है कि आप अधिक परिष्कृत एकीकरण सूत्र का उपयोग करके कम फ़ंक्शन मूल्यांकन के साथ इंटीग्रल का अधिक सटीक मूल्यांकन प्राप्त कर सकते हैं।


1
वास्तव में। यदि आपका कार्य सुचारू है, तो आप आमतौर पर अपने 50 से कम कार्य मूल्यांकन के साथ दूर हो सकते हैं यदि आप उपयोग करते हैं, तो कहते हैं, केवल 5 अंतराल पर एक गाऊस -4 क्वाडरेचर नियम।
वोल्फगैंग बैंगर्थ

7

मुमकिन? हाँ। उपयोगी? नहीं, मैं यहां जिन आशाओं को सूचीबद्ध करने जा रहा हूं, वे रनटाइम में प्रतिशत अंतर के एक छोटे से अंश से अधिक होने की संभावना नहीं हैं। एक अच्छा संकलक पहले से ही आपके लिए ये कर सकता है।

वैसे भी, अपने भीतर के पाश को देखते हुए:

    for (s=0,j=a;j<b;j+=h){
        func2 = func(j+h);
        s = s + 0.5*(func1+func2)*h;
        func1 = func2;
    }

प्रत्येक लूप पुनरावृत्ति पर आप तीन गणित संचालन करते हैं जिन्हें बाहर लाया जा सकता है: जोड़ना j + h, गुणा करना 0.5और गुणा करना h। सबसे पहले आप अपने पुनरावृत्त चर को शुरू करके ठीक कर सकते हैं a + h, और दूसरों को गुणन करके बाहर निकाल सकते हैं:

    for (s=0, j=a+h; j<=b; j+=h){
        func2 = func(j);
        s += func1+func2;
        func1 = func2;
    }
    s *= 0.5 * h;

हालाँकि, मैं यह बताऊंगा कि फ्लोटिंग पॉइंट राउंडऑफ़ त्रुटि के कारण लूप के अंतिम पुनरावृत्ति को याद करना संभव है। (यह आपके मूल कार्यान्वयन में भी एक समस्या थी।) उस के आसपास जाने के लिए, एक unsigned intया size_tकाउंटर का उपयोग करें :

    size_t n;
    for (s=0, n=0, j=a+h; n<N; n++, j+=h){
        func2 = func(j);
        s += func1+func2;
        func1 = func2;
    }
    s *= 0.5 * h;

जैसा कि ब्रायन का उत्तर कहता है, आपका समय फ़ंक्शन के मूल्यांकन को बेहतर बनाने में बेहतर है func। यदि इस पद्धति की सटीकता पर्याप्त है, तो मुझे संदेह है कि आप उसी के लिए कुछ भी तेजी से पाएंगे N। (यद्यपि आप यह देखने के लिए कुछ परीक्षण चला सकते हैं कि क्या रन-कुट्टा आपको Nपर्याप्त कम देता है कि समग्र एकीकरण सटीकता का त्याग किए बिना कम समय लेता है।)


4

गणना में सुधार के लिए मैं कई बदलावों की सिफारिश करूंगा:

  • प्रदर्शन और सटीकता के लिए, उपयोग करें std::fma(), जो एक जुड़े हुए गुणा-जोड़ को निष्पादित करता है ।
  • प्रदर्शन के लिए, प्रत्येक ट्रेपोज़ॉइड के क्षेत्र को 0.5 से गुणा करना - आप इसे अंत में एक बार कर सकते हैं।
  • बार-बार जोड़ने से बचें h, जो राउंड-ऑफ त्रुटियों को जमा कर सकता है।

इसके अलावा, मैं स्पष्टता के लिए कई बदलाव करूंगा:

  • फ़ंक्शन को अधिक वर्णनात्मक नाम दें।
  • फ़ंक्शन हस्ताक्षर में aऔर के आदेश को स्वैप करें b
  • नाम बदलें Nn, hdx, jx2, saccumulator
  • बदले nएक करने के लिए int
  • एक तंग दायरे में परिवर्तन की घोषणा।
#include <cmath>

double trapezoidal_integration(double func(double), double a, double b, int n) {
    double dx = (b - a) / (n - 1);   // Width of trapezoids

    double func_x1 = func(a);
    double accumulator = 0;

    for (int i = 1; i <= n; i++) {
        double x2 = a + i * dx;      // Avoid repeated floating-point addition
        double func_x2 = func(x2);
        accumulator = std::fma(func_x1 + func_x2, dx, accumulator); // Fused multiply-add
        func_x1 = func_x2;
    }

    return 0.5 * accumulator;
}

3

यदि आपका फ़ंक्शन एक बहुपद है, तो संभवतः किसी फ़ंक्शन (जैसे एक गॉसियन) द्वारा भारित किया जाता है, तो आप सीधे 3 डी में एक घन सूत्र के साथ सटीक एकीकरण कर सकते हैं (जैसे http://people.sc.fsu.edu/~jkarkardt/c_src/ stroud / stroud.html ) या विरल ग्रिड के साथ (जैसे http://tasmanian.ornl.gov/ )। ये विधियां केवल फ़ंक्शन मान को गुणा करने के लिए अंकों और भार का एक सेट निर्दिष्ट करती हैं, इसलिए वे बहुत तेज़ हैं। यदि आपका कार्य बहुपद द्वारा अनुमानित रूप से सुचारू है, तो ये तरीके अभी भी बहुत अच्छा जवाब दे सकते हैं। सूत्र आपके द्वारा एकीकृत किए जा रहे फ़ंक्शन के प्रकार के लिए विशिष्ट होते हैं, इसलिए सही खोजने के लिए कुछ खुदाई हो सकती है।


3

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

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


3

एकीकरण करने के बहुत सारे तरीके हैं, जिनमें से सबसे सरल नियम ट्रैपोज़ाइडल नियम है।

यदि आप वास्तविक कार्य के बारे में कुछ भी जानते हैं जिसे आप एकीकृत कर रहे हैं, तो आप इसका फायदा उठा सकते हैं। त्रुटि के स्वीकार्य स्तर के भीतर ग्रिड बिंदुओं की संख्या को कम करने के लिए विचार है।

उदाहरण के लिए, ट्रेपोज़ाइडल लगातार बिंदुओं के लिए एक रैखिक फिट बना रहा है। आप एक द्विघात फिट कर सकते हैं, जो यदि वक्र चिकनी है तो बेहतर फिट होगा, जो आपको एक मोटे ग्रिड का उपयोग करने की अनुमति दे सकता है।

ऑर्बिटल सिमुलेशन कभी-कभी शंकुओं का उपयोग करके किया जाता है, क्योंकि ऑर्बिट को शंकु वर्गों की तरह बहुत पसंद है।

मेरे काम में, हम आकृतियों को एकीकृत कर रहे हैं जो लगभग घंटी के आकार का घटता है, इसलिए उन्हें उस के रूप में मॉडल करना प्रभावी है ( इस काम में अनुकूली गॉसियन क्वाडरेचर को "सोने का मानक" माना जाता है)।


1

इसलिए, जैसा कि अन्य उत्तरों में बताया गया है, यह इस बात पर निर्भर करता है कि आपका कार्य कितना महंगा है। यदि वास्तव में यह आपकी अड़चन है, तो ट्रैप कोड का अनुकूलन करना ही इसके लायक है। यदि यह पूरी तरह से स्पष्ट नहीं है, तो आपको अपने कोड को प्रोफाइल करके जांचना चाहिए (Intels V-tune, Valgrind या Visual Studio जैसे उपकरण ऐसा कर सकते हैं)।

मैं फिर भी एक पूरी तरह से अलग दृष्टिकोण का सुझाव दूंगा: मोंटे कार्लो एकीकरण । यहां आप परिणामों को जोड़ते हुए यादृच्छिक बिंदुओं पर अपने फ़ंक्शन का नमूना करके केवल अभिन्न को अनुमानित करते हैं। देखें इस जानकारी के लिए विकि पृष्ठ के अलावा पीडीएफ।

यह उच्च आयामी डेटा के लिए बहुत अच्छी तरह से काम करता है, आमतौर पर 1-डी एकीकरण में उपयोग किए जाने वाले द्विघात तरीकों की तुलना में बहुत बेहतर है।

साधारण मामला लागू करने के लिए बहुत आसान है (पीडीएफ देखें), बस सावधान रहें कि मानक ++ फ़ंक्शन 98 सी में काफी खराब है प्रदर्शन और गुणवत्ता दोनों। C ++ 11 में, आप Mersenne Twister का उपयोग कर सकते हैं।

यदि आपके कार्य में कुछ क्षेत्रों में बहुत अधिक भिन्नता है और दूसरों में कम है, तो स्तरीकृत नमूने का उपयोग करने पर विचार करें। मैं अपने स्वयं के लेखन के बजाय GNU वैज्ञानिक पुस्तकालय का उपयोग करने की सलाह दूंगा


1
मैं वास्तव में एक 3D एकीकरण कर रहा हूं, जहां इस कोड को पुनरावर्ती कहा जाता है।

"पुनरावर्ती" कुंजी है। आप या तो एक बड़े डेटा सेट से गुजर रहे हैं और बहुत से डेटा पर एक से अधिक बार विचार कर रहे हैं, या आप वास्तव में अपना डेटा सेट कर रहे हैं (टुकड़ा-टुकड़ा?) फ़ंक्शन।

पुनरावर्ती रूप से मूल्यांकन किए गए एकीकरण हास्यास्पद रूप से महंगे होंगे, और पुनरावृत्ति में शक्तियां बढ़ने के कारण हास्यास्पद रूप से अभेद्य होंगे।

अपने डेटा सेट को प्रक्षेपित करने के लिए एक मॉडल बनाएं और एक टुकड़े-टुकड़े प्रतीकात्मक एकीकरण करें। चूंकि बहुत सारे डेटा तब बेस फ़ंक्शंस के गुणांक में ढह रहे हैं, इसलिए गहन पुनर्संरचना के लिए जटिलता तेजी से बढ़ने के बजाय बहुपद (और आमतौर पर कम शक्तियां) बढ़ती है। और आपको "सटीक" परिणाम मिलते हैं (आपको अभी भी उचित संख्यात्मक प्रदर्शन प्राप्त करने के लिए अच्छी मूल्यांकन योजनाओं का पता लगाने की आवश्यकता है, लेकिन यह अभी भी बल्कि ट्रैपोज़ाइडल एकीकरण से बेहतर प्राप्त करने के लिए संभव है)।

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

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


1

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

    double trap(double func(double), double b, double a, double N){
double j, s;
double h = (b-a)/(N-1.0); //Width of trapezia

double s = 0;
j = a;
for(i=1; i<N-1; i++){
  j += h;
  s += func(j);
}
s += (func(a)+func(b))/2;

return s*h;
}

1
कृपया अपने परिवर्तनों और कोड के लिए तर्क दें। अधिकांश लोगों के लिए कोड का एक ब्लॉक काफी बेकार है।
गोड्रिक सेर

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