मेरे पास बहुत सीमित संसाधन हैं क्योंकि मैं एक माइक्रोकंट्रोलर के साथ काम कर रहा हूं। क्या एक टेलर-श्रृंखला विस्तार, सामान्य लुकअप टेबल या पुनरावर्ती दृष्टिकोण है?
मैं math.h के sqrt () का उपयोग किए बिना कुछ करना पसंद करूंगा
मेरे पास बहुत सीमित संसाधन हैं क्योंकि मैं एक माइक्रोकंट्रोलर के साथ काम कर रहा हूं। क्या एक टेलर-श्रृंखला विस्तार, सामान्य लुकअप टेबल या पुनरावर्ती दृष्टिकोण है?
मैं math.h के sqrt () का उपयोग किए बिना कुछ करना पसंद करूंगा
जवाबों:
यदि आप एक सस्ते और गंदे अनुकूलित पावर-सीरीज़ का विस्तार चाहते हैं (टेलर श्रृंखला के लिए गुणांक धीरे-धीरे अभिसरण करते हैं) sqrt()
और अन्य ट्रान्सेंडेंटल का एक गुच्छा, मेरे पास बहुत पहले से कुछ कोड है। मैं इस कोड को बेचता था, लेकिन लगभग एक दशक तक मुझे किसी ने इसके लिए भुगतान नहीं किया। इसलिए मुझे लगता है कि मैं इसे सार्वजनिक उपभोग के लिए जारी करूंगा। यह विशेष फ़ाइल एक ऐसे अनुप्रयोग के लिए थी जहाँ प्रोसेसर में फ्लोटिंग पॉइंट (IEEE-754 सिंगल प्रिसिजन) था और उनके पास C कंपाइलर और देव सिस्टम था, लेकिन उन्होंने नहीं किया(या वे लिंक नहीं करना चाहते थे) स्टैडलिब में जो मानक गणित कार्य होता है। उन्हें सही सटीकता की आवश्यकता नहीं थी, लेकिन वे चाहते थे कि चीजें जल्दी हो। आप बहुत आसानी से इंजीनियर को कोड को रिवर्स कर सकते हैं यह देखने के लिए कि बिजली श्रृंखला गुणांक क्या है और अपना कोड लिखें। इस कोड IEEE-754 मानता है और mantissa और प्रतिपादक के लिए बिट बंद नकाबपोश।
ऐसा प्रतीत होता है कि एसई के पास "कोड मार्कअप" है जो कोण वर्णों (आप जानते हैं ">" या "<") के साथ है, इसलिए आपको शायद यह सब देखने के लिए "संपादन" करना होगा।
//
// FILE: __functions.h
//
// fast and approximate transcendental functions
//
// copyright (c) 2004 Robert Bristow-Johnson
//
// rbj@audioimagination.com
//
#ifndef __FUNCTIONS_H
#define __FUNCTIONS_H
#define TINY 1.0e-8
#define HUGE 1.0e8
#define PI (3.1415926535897932384626433832795028841972) /* pi */
#define ONE_OVER_PI (0.3183098861837906661338147750939)
#define TWOPI (6.2831853071795864769252867665590057683943) /* 2*pi */
#define ONE_OVER_TWOPI (0.15915494309189535682609381638)
#define PI_2 (1.5707963267948966192313216916397514420986) /* pi/2 */
#define TWO_OVER_PI (0.636619772367581332267629550188)
#define LN2 (0.6931471805599453094172321214581765680755) /* ln(2) */
#define ONE_OVER_LN2 (1.44269504088896333066907387547)
#define LN10 (2.3025850929940456840179914546843642076011) /* ln(10) */
#define ONE_OVER_LN10 (0.43429448190325177635683940025)
#define ROOT2 (1.4142135623730950488016887242096980785697) /* sqrt(2) */
#define ONE_OVER_ROOT2 (0.707106781186547438494264988549)
#define DB_LOG2_ENERGY (3.01029995663981154631945610163) /* dB = DB_LOG2_ENERGY*__log2(energy) */
#define DB_LOG2_AMPL (6.02059991327962309263891220326) /* dB = DB_LOG2_AMPL*__log2(amplitude) */
#define ONE_OVER_DB_LOG2_AMPL (0.16609640474436811218256075335) /* amplitude = __exp2(ONE_OVER_DB_LOG2_AMPL*dB) */
#define LONG_OFFSET 4096L
#define FLOAT_OFFSET 4096.0
float __sqrt(float x);
float __log2(float x);
float __exp2(float x);
float __log(float x);
float __exp(float x);
float __pow(float x, float y);
float __sin_pi(float x);
float __cos_pi(float x);
float __sin(float x);
float __cos(float x);
float __tan(float x);
float __atan(float x);
float __asin(float x);
float __acos(float x);
float __arg(float Imag, float Real);
float __poly(float *a, int order, float x);
float __map(float *f, float scaler, float x);
float __discreteMap(float *f, float scaler, float x);
unsigned long __random();
#endif
//
// FILE: __functions.c
//
// fast and approximate transcendental functions
//
// copyright (c) 2004 Robert Bristow-Johnson
//
// rbj@audioimagination.com
//
#define STD_MATH_LIB 0
#include "__functions.h"
#if STD_MATH_LIB
#include "math.h" // angle brackets don't work with SE markup
#endif
float __sqrt(register float x)
{
#if STD_MATH_LIB
return (float) sqrt((double)x);
#else
if (x > 5.877471754e-39)
{
register float accumulator, xPower;
register long intPart;
register union {float f; long i;} xBits;
xBits.f = x;
intPart = ((xBits.i)>>23); /* get biased exponent */
intPart -= 127; /* unbias it */
x = (float)(xBits.i & 0x007FFFFF); /* mask off exponent leaving 0x800000*(mantissa - 1) */
x *= 1.192092895507812e-07; /* divide by 0x800000 */
accumulator = 1.0 + 0.49959804148061*x;
xPower = x*x;
accumulator += -0.12047308243453*xPower;
xPower *= x;
accumulator += 0.04585425015501*xPower;
xPower *= x;
accumulator += -0.01076564682800*xPower;
if (intPart & 0x00000001)
{
accumulator *= ROOT2; /* an odd input exponent means an extra sqrt(2) in the output */
}
xBits.i = intPart >> 1; /* divide exponent by 2, lose LSB */
xBits.i += 127; /* rebias exponent */
xBits.i <<= 23; /* move biased exponent into exponent bits */
return accumulator * xBits.f;
}
else
{
return 0.0;
}
#endif
}
float __log2(register float x)
{
#if STD_MATH_LIB
return (float) (ONE_OVER_LN2*log((double)x));
#else
if (x > 5.877471754e-39)
{
register float accumulator, xPower;
register long intPart;
register union {float f; long i;} xBits;
xBits.f = x;
intPart = ((xBits.i)>>23); /* get biased exponent */
intPart -= 127; /* unbias it */
x = (float)(xBits.i & 0x007FFFFF); /* mask off exponent leaving 0x800000*(mantissa - 1) */
x *= 1.192092895507812e-07; /* divide by 0x800000 */
accumulator = 1.44254494359510*x;
xPower = x*x;
accumulator += -0.71814525675041*xPower;
xPower *= x;
accumulator += 0.45754919692582*xPower;
xPower *= x;
accumulator += -0.27790534462866*xPower;
xPower *= x;
accumulator += 0.12179791068782*xPower;
xPower *= x;
accumulator += -0.02584144982967*xPower;
return accumulator + (float)intPart;
}
else
{
return -HUGE;
}
#endif
}
float __exp2(register float x)
{
#if STD_MATH_LIB
return (float) exp(LN2*(double)x);
#else
if (x >= -127.0)
{
register float accumulator, xPower;
register union {float f; long i;} xBits;
xBits.i = (long)(x + FLOAT_OFFSET) - LONG_OFFSET; /* integer part */
x -= (float)(xBits.i); /* fractional part */
accumulator = 1.0 + 0.69303212081966*x;
xPower = x*x;
accumulator += 0.24137976293709*xPower;
xPower *= x;
accumulator += 0.05203236900844*xPower;
xPower *= x;
accumulator += 0.01355574723481*xPower;
xBits.i += 127; /* bias integer part */
xBits.i <<= 23; /* move biased int part into exponent bits */
return accumulator * xBits.f;
}
else
{
return 0.0;
}
#endif
}
float __log(register float x)
{
#if STD_MATH_LIB
return (float) log((double)x);
#else
return LN2*__log2(x);
#endif
}
float __exp(register float x)
{
#if STD_MATH_LIB
return (float) exp((double)x);
#else
return __exp2(ONE_OVER_LN2*x);
#endif
}
float __pow(float x, float y)
{
#if STD_MATH_LIB
return (float) pow((double)x, (double)y);
#else
return __exp2(y*__log2(x));
#endif
}
float __sin_pi(register float x)
{
#if STD_MATH_LIB
return (float) sin(PI*(double)x);
#else
register float accumulator, xPower, xSquared;
register long evenIntPart = ((long)(0.5*x + 1024.5) - 1024)<<1;
x -= (float)evenIntPart;
xSquared = x*x;
accumulator = 3.14159265358979*x;
xPower = xSquared*x;
accumulator += -5.16731953364340*xPower;
xPower *= xSquared;
accumulator += 2.54620566822659*xPower;
xPower *= xSquared;
accumulator += -0.586027023087261*xPower;
xPower *= xSquared;
accumulator += 0.06554823491427*xPower;
return accumulator;
#endif
}
float __cos_pi(register float x)
{
#if STD_MATH_LIB
return (float) cos(PI*(double)x);
#else
register float accumulator, xPower, xSquared;
register long evenIntPart = ((long)(0.5*x + 1024.5) - 1024)<<1;
x -= (float)evenIntPart;
xSquared = x*x;
accumulator = 1.57079632679490*x; /* series for sin(PI/2*x) */
xPower = xSquared*x;
accumulator += -0.64596406188166*xPower;
xPower *= xSquared;
accumulator += 0.07969158490912*xPower;
xPower *= xSquared;
accumulator += -0.00467687997706*xPower;
xPower *= xSquared;
accumulator += 0.00015303015470*xPower;
return 1.0 - 2.0*accumulator*accumulator; /* cos(w) = 1 - 2*(sin(w/2))^2 */
#endif
}
float __sin(register float x)
{
#if STD_MATH_LIB
return (float) sin((double)x);
#else
x *= ONE_OVER_PI;
return __sin_pi(x);
#endif
}
float __cos(register float x)
{
#if STD_MATH_LIB
return (float) cos((double)x);
#else
x *= ONE_OVER_PI;
return __cos_pi(x);
#endif
}
float __tan(register float x)
{
#if STD_MATH_LIB
return (float) tan((double)x);
#else
x *= ONE_OVER_PI;
return __sin_pi(x)/__cos_pi(x);
#endif
}
float __atan(register float x)
{
#if STD_MATH_LIB
return (float) atan((double)x);
#else
register float accumulator, xPower, xSquared, offset;
offset = 0.0;
if (x < -1.0)
{
offset = -PI_2;
x = -1.0/x;
}
else if (x > 1.0)
{
offset = PI_2;
x = -1.0/x;
}
xSquared = x*x;
accumulator = 1.0;
xPower = xSquared;
accumulator += 0.33288950512027*xPower;
xPower *= xSquared;
accumulator += -0.08467922817644*xPower;
xPower *= xSquared;
accumulator += 0.03252232640125*xPower;
xPower *= xSquared;
accumulator += -0.00749305860992*xPower;
return offset + x/accumulator;
#endif
}
float __asin(register float x)
{
#if STD_MATH_LIB
return (float) asin((double)x);
#else
return __atan(x/__sqrt(1.0 - x*x));
#endif
}
float __acos(register float x)
{
#if STD_MATH_LIB
return (float) acos((double)x);
#else
return __atan(__sqrt(1.0 - x*x)/x);
#endif
}
float __arg(float Imag, float Real)
{
#if STD_MATH_LIB
return (float) atan2((double)Imag, (double)Real);
#else
register float accumulator, xPower, xSquared, offset, x;
if (Imag > 0.0)
{
if (Imag <= -Real)
{
offset = PI;
x = Imag/Real;
}
else if (Imag > Real)
{
offset = PI_2;
x = -Real/Imag;
}
else
{
offset = 0.0;
x = Imag/Real;
}
}
else
{
if (Imag >= Real)
{
offset = -PI;
x = Imag/Real;
}
else if (Imag < -Real)
{
offset = -PI_2;
x = -Real/Imag;
}
else
{
offset = 0.0;
x = Imag/Real;
}
}
xSquared = x*x;
accumulator = 1.0;
xPower = xSquared;
accumulator += 0.33288950512027*xPower;
xPower *= xSquared;
accumulator += -0.08467922817644*xPower;
xPower *= xSquared;
accumulator += 0.03252232640125*xPower;
xPower *= xSquared;
accumulator += -0.00749305860992*xPower;
return offset + x/accumulator;
#endif
}
float __poly(float *a, int order, float x)
{
register float accumulator = 0.0, xPower;
register int n;
accumulator = a[0];
xPower = x;
for (n=1; n<=order; n++)
{
accumulator += a[n]*xPower;
xPower *= x;
}
return accumulator;
}
float __map(float *f, float scaler, float x)
{
register long i;
x *= scaler;
i = (long)(x + FLOAT_OFFSET) - LONG_OFFSET; /* round down without floor() */
return f[i] + (f[i+1] - f[i])*(x - (float)i); /* linear interpolate between points */
}
float __discreteMap(float *f, float scaler, float x)
{
register long i;
x *= scaler;
i = (long)(x + (FLOAT_OFFSET+0.5)) - LONG_OFFSET; /* round to nearest */
return f[i];
}
unsigned long __random()
{
static unsigned long seed0 = 0x5B7A2775, seed1 = 0x80C7169F;
seed0 += seed1;
seed1 += seed0;
return seed1;
}
stdlib
।
यदि आपने इसे नहीं देखा है, तो "क्वेक स्क्वायर रूट" बस रहस्यमय है। यह आपको एक बहुत अच्छा प्रथम स्तर देने के लिए कुछ बिट-स्तरीय जादू का उपयोग करता है, और फिर संशोधित करने के लिए न्यूटन के एक दौर या दो का उपयोग करता है। यदि आप सीमित संसाधनों के साथ काम कर रहे हैं तो यह आपकी मदद कर सकता है।
https://en.wikipedia.org/wiki/Fast_inverse_square_root
http://betterexplained.com/articles/understanding-quakes-fast-inverse-square-root/
आप न्यूटन की विधि का उपयोग करके वर्गमूल फ़ंक्शन को भी अनुमानित कर सकते हैं । न्यूटन की विधि अनुमान लगाने का एक तरीका है जहां एक फ़ंक्शन की जड़ें होती हैं। यह एक पुनरावृत्त विधि भी है जहां पिछले पुनरावृत्ति से परिणाम अभिसरण तक अगले पुनरावृत्ति में उपयोग किया जाता है। न्यूटन की विधि का अनुमान लगाने के लिए समीकरण जहां रूट एक फ़ंक्शन है, जिसे प्रारंभिक अनुमान है को निम्न के रूप में परिभाषित किया गया है:
पहला अनुमान है कि रूट कहां स्थित है। हम समीकरण को पुनर्चक्रित करते रहते हैं और पिछले पुनरावृत्तियों से परिणाम का उपयोग करते हैं जब तक कि उत्तर नहीं बदलता है। सामान्य तौर पर, पुनरावृत्ति पर मूल के अनुमान को निर्धारित करने के लिए , पुनरावृति पर अनुमान को परिभाषित किया गया है:
वर्गमूल के लिए लगभग न्यूटन की विधि का उपयोग करने के लिए, मान लें कि हमें एक संख्या दी गई । इस तरह, वर्गमूल की गणना करने के लिए, हमें गणना करने की आवश्यकता है। जैसे, हम एक उत्तर खोजना चाहते हैं जैसे । दोनों पक्षों वर्ग, और आगे बढ़ समीकरण पैदावार के दूसरी तरफ । इस प्रकार, इस समीकरण का उत्तर और इस प्रकार यह फ़ंक्शन का मूल है। जैसे, ऐसा समीकरण है जिसकी जड़ हम खोजना चाहते हैं। न्यूटन की विधि में इसे प्रतिस्थापित करके, , और इसलिए:
इसलिए, वर्गमूल की गणना करने के लिए , हमें बस न्यूटन की विधि की गणना करने की आवश्यकता है जब तक हम अभिसरण नहीं करते। हालाँकि, जैसा कि @ robertbristow-johnson द्वारा बताया गया है, विभाजन एक बहुत महंगा ऑपरेशन है - विशेष रूप से सीमित संसाधनों के साथ माइक्रोकंट्रोलर / डीएसपी के लिए। इसके अलावा, एक ऐसा मामला हो सकता है जहां एक अनुमान 0 हो सकता है, जिसके परिणामस्वरूप विभाजन ऑपरेशन के कारण 0 त्रुटि से विभाजन होगा। जैसे, हम क्या कर सकते हैं न्यूटन की विधि का उपयोग करें और इसके बजाय पारस्परिक फ़ंक्शन के लिए हल करें , अर्थात । यह भी किसी भी विभाजन से बचा जाता है, जैसा कि हम बाद में देखेंगे। दोनों पक्षों को चुकाना, और बाएं हाथ की ओर चलना इस प्रकार । इसलिए, इसका समाधान होगा । से गुणा करके , हम अपने इच्छित परिणाम मिलेगा। फिर, न्यूटन की विधि का उपयोग करते हुए, हमारे पास इस प्रकार है:
हालांकि, एक चेतावनी है कि हमें उपरोक्त समीकरण को देखते समय विचार करना चाहिए। वर्गमूल के लिए, समाधान सकारात्मक होना चाहिए और इसलिए पुनरावृत्तियों (और परिणाम) के लिए सकारात्मक होने के लिए, निम्नलिखित स्थिति को संतुष्ट करना होगा:
3 एक्स एन > ( एक्स एन ) 3 एक ( एक्स एन ) 2 एक < 3
इसलिए:
इसलिए, जिस संख्या को हम वर्गमूल की गणना करना चाहते हैं, उसे देखते हुए, प्रारंभिक अनुमान को उपरोक्त स्थिति को पूरा करना चाहिए । जैसा कि यह अंततः एक माइक्रोकंट्रोलर पर रखा जा रहा है, हम किसी भी मूल्य के साथ शुरू कर सकते हैं (1 कहते हैं), फिर लूपिंग रखें और के मूल्य को कम करें जब तक कि उपरोक्त स्थिति संतुष्ट न हो। ध्यान दें कि मैंने के मान को सीधे गणना करने के लिए विभाजन करने सेx 0 x 0 10 - 6होना चाहिए क्योंकि विभाजन एक महंगा ऑपरेशन है। एक बार जब हम अपना प्रारंभिक अनुमान लगा लेते हैं, तो न्यूटन की विधि के माध्यम से पुनरावृति करते हैं। ध्यान दें कि प्रारंभिक अनुमान के आधार पर, इसे रूपांतरित होने में कम या अधिक समय लग सकता है। यह सब इस बात पर निर्भर करता है कि आप वास्तविक उत्तर के कितने करीब हैं। आप या तो पुनरावृत्तियों की संख्या को कैप कर सकते हैं, या तब तक प्रतीक्षा कर सकते हैं जब तक कि दोनों जड़ों के बीच का अंतर कुछ सीमा से कम न हो (जैसे या तो)।
जैसा कि आपका टैग एक एल्गोरिथ्म की तलाश में है C
, आइए एक बहुत जल्दी लिखें:
#include <stdio.h> // For printf
#include <math.h> // For fabs
void main()
{
float a = 5.0; // Number we want to take the square root of
float x = 1.0; // Initial guess
float xprev; // Root for previous iteration
int count; // Counter for iterations
// Find a better initial guess
// Half at each step until condition is satisfied
while (x*x*a >= 3.0)
x *= 0.5;
printf("Initial guess: %f\n", x);
count = 1;
do {
xprev = x; // Save for previous iteration
printf("Iteration #%d: %f\n", count++, x);
x = 0.5*(3*xprev - (xprev*xprev*xprev)*a); // Find square root of the reciprocal
} while (fabs(x - xprev) > 1e-6);
x *= a; // Actual answer - Multiply by a
printf("Square root is: %f\n", x);
printf("Done!");
}
यह न्यूटन की विधि का एक बहुत ही बुनियादी कार्यान्वयन है। ध्यान दें कि मैं प्रारंभिक अनुमान को आधे तक कम कर रहा हूं जब तक कि जिस स्थिति के बारे में हमने पहले बात की थी वह संतुष्ट है। मैं भी 5. का वर्गमूल खोजने की कोशिश कर रहा हूं। हम जानते हैं कि यह लगभग 2.236 या तो के बराबर है। उपरोक्त कोड का उपयोग निम्नलिखित आउटपुट देता है:
Initial guess: 0.500000
Iteration #1: 0.500000
Iteration #2: 0.437500
Iteration #3: 0.446899
Iteration #4: 0.447213
Square root is: 2.236068
Done!
ध्यान दें कि न्यूटन की विधि पारस्परिक समाधान के समाधान से गुणा खोजने और हम है अंत में हमारे अंतिम जवाब मिलता है। इसके अलावा, ध्यान दें कि प्रारंभिक अनुमान को यह सुनिश्चित करने के लिए बदल दिया गया था कि मैंने ऊपर जिन मानदंडों के बारे में बात की थी, वे संतुष्ट हैं। बस मनोरंजन के लिए, आइए 9876 का वर्गमूल ज्ञात करें।
Initial guess: 0.015625
Iteration #1: 0.015625
Iteration #2: 0.004601
Iteration #3: 0.006420
Iteration #4: 0.008323
Iteration #5: 0.009638
Iteration #6: 0.010036
Iteration #7: 0.010062
Square root is: 99.378067
Done!
जैसा कि आप देख सकते हैं, केवल एक चीज अलग है कि वर्गमूल की गणना करने के लिए कितने पुनरावृत्तियों की आवश्यकता है। जितना अधिक आप गणना करना चाहते हैं, उतनी ही अधिक पुनरावृत्ति होगी।
मुझे पता है कि यह विधि पहले ही पोस्ट में सुझाई जा चुकी है, लेकिन मुझे लगा कि मैं इस विधि को प्राप्त करूँगा और कुछ कोड भी प्रदान करूँगा!
हाँ, एक पॉवर सीरीज़ जल्दी और कुशलता से वर्गाकार रूट फंक्शन और केवल एक सीमित डोमेन पर अनुमानित कर सकती है । डोमेन को व्यापक करें, त्रुटि को पर्याप्त रूप से कम रखने के लिए आपको अपनी शक्ति श्रृंखला में और अधिक शर्तों की आवश्यकता होगी।
कहाँ पे
अगर यह फ्लोटिंग पॉइंट है, तो आपको एक्सप्रेशन और मंटिसा को अलग करने की आवश्यकता है जैसे मेरा सी कोड दूसरे उत्तर में करता है।
वास्तव में यह न्यूटन विधि का उपयोग करके एक द्विघात समीकरण को हल करके किया जाता है:
http://en.wikipedia.org/wiki/Methods_of_computing_square_roots
एक से अधिक संख्या के लिए आप निम्नलिखित टेलर विस्तार का उपयोग कर सकते हैं:
4% परिशुद्धता के भीतर, अगर मुझे अच्छी तरह से याद है। लॉगरिदमिक शासकों और कैलकुलेटर से पहले इसका इस्तेमाल इंजीनियरों द्वारा किया जाता था। मैंने इसे नोट्स एट फॉर्मुल्स डे लिंगेन्युर, डे लाहर्पे , 1923 में सीखा