ฉันได้เขียนโค้ดมาระยะหนึ่งแล้วซึ่งพยายามคำนวณโดยไม่ต้องใช้ฟังก์ชั่นห้องสมุด เมื่อวานนี้ฉันกำลังตรวจสอบรหัสเดิมและฉันพยายามทำให้เร็วที่สุดเท่าที่จะทำได้ (และถูกต้อง) นี่คือความพยายามของฉัน:
const double ee = exp(1);
double series_ln_taylor(double n){ /* n = e^a * b, where a is an non-negative integer */
double lgVal = 0, term, now;
int i, flag = 1;
if ( n <= 0 ) return 1e-300;
if ( n * ee < 1 )
n = 1.0 / n, flag = -1; /* for extremely small n, use e^-x = 1/n */
for ( term = 1; term < n ; term *= ee, lgVal++ );
n /= term;
/* log(1 - x) = -x - x**2/2 - x**3/3... */
n = 1 - n;
now = term = n;
for ( i = 1 ; ; ){
lgVal -= now;
term *= n;
now = term / ++i;
if ( now < 1e-17 ) break;
}
if ( flag == -1 ) lgVal = -lgVal;
return lgVal;
}
นี่ฉันกำลังพยายามที่จะหาเพื่อให้อีเป็นเพียงกว่า n และจากนั้นผมเพิ่มค่าลอการิทึมของnซึ่งน้อยกว่า 1 ณ จุดนี้การขยายตัวของเทย์เลอร์Loกรัม(1-x)สามารถนำมาใช้โดยไม่ต้องกังวล
เมื่อไม่นานมานี้ฉันมีความสนใจในการวิเคราะห์เชิงตัวเลขและนั่นเป็นสาเหตุที่ฉันไม่สามารถช่วยถามคำถามนี้ได้เลยว่าส่วนของรหัสนี้จะทำงานได้เร็วขึ้นเท่าใดในขณะที่ถูกต้องเพียงพอ ฉันจำเป็นต้องเปลี่ยนไปใช้วิธีอื่นเช่นใช้เศษส่วนต่อเนื่องเช่นนี้หรือไม่
ฟังก์ชันมาพร้อมกับไลบรารีมาตรฐาน C นั้นเร็วกว่าการติดตั้งนี้เกือบ 5.1 เท่า
อัปเดต 1 : การใช้ซีรีย์ไฮเปอร์โบลิกอาร์แทนที่กล่าวถึงในวิกิพีเดียการคำนวณดูเหมือนว่าจะช้ากว่า 2.2 เท่ากว่าฟังก์ชั่นบันทึกในไลบรารีมาตรฐาน C แม้ว่าฉันจะไม่ได้ตรวจสอบประสิทธิภาพอย่างกว้างขวางและสำหรับจำนวนที่มากขึ้นการใช้งานในปัจจุบันของฉันดูเหมือนจะช้ามากจริงๆ ฉันต้องการตรวจสอบการติดตั้งทั้งข้อผิดพลาดและเวลาเฉลี่ยสำหรับหมายเลขที่หลากหลายถ้าฉันสามารถจัดการได้ นี่คือความพยายามครั้งที่สองของฉัน
double series_ln_arctanh(double n){ /* n = e^a * b, where a is an non-negative integer */
double lgVal = 0, term, now, sm;
int i, flag = 1;
if ( n <= 0 ) return 1e-300;
if ( n * ee < 1 ) n = 1.0 / n, flag = -1; /* for extremely small n, use e^-x = 1/n */
for ( term = 1; term < n ; term *= ee, lgVal++ );
n /= term;
/* log(x) = 2 arctanh((x-1)/(x+1)) */
n = (1 - n)/(n + 1);
now = term = n;
n *= n;
sm = 0;
for ( i = 3 ; ; i += 2 ){
sm += now;
term *= n;
now = term / i;
if ( now < 1e-17 ) break;
}
lgVal -= 2*sm;
if ( flag == -1 ) lgVal = -lgVal;
return lgVal;
}
ข้อเสนอแนะหรือคำวิจารณ์ใด ๆ ที่ชื่นชม
อัปเดต 2:ตามคำแนะนำที่ทำไว้ด้านล่างฉันได้เพิ่มการเปลี่ยนแปลงที่เพิ่มขึ้นที่นี่ซึ่งช้ากว่าการใช้ไลบรารีมาตรฐานประมาณ 2.5 เท่า อย่างไรก็ตามฉันได้ทดสอบเฉพาะจำนวนเต็มในครั้งนี้สำหรับจำนวนที่มากขึ้นรันไทม์จะเพิ่มขึ้น สำหรับตอนนี้. ฉันยังไม่รู้เทคนิคในการสร้างตัวเลขสองตัวแบบสุ่ม≤ 1 e 308ดังนั้นจึงยังไม่ได้ทำการทดสอบอย่างสมบูรณ์ เพื่อให้รหัสมีเสถียรภาพมากขึ้นฉันได้เพิ่มการแก้ไขสำหรับตัวพิมพ์มุม ข้อผิดพลาดเฉลี่ยสำหรับการทดสอบที่ผมทำอยู่ที่ประมาณ4 E - 15
double series_ln_better(double n){ /* n = e^a * b, where a is an non-negative integer */
double lgVal = 0, term, now, sm;
int i, flag = 1;
if ( n == 0 ) return -1./0.; /* -inf */
if ( n < 0 ) return 0./0.; /* NaN*/
if ( n < 1 ) n = 1.0 / n, flag = -1; /* for extremely small n, use e^-x = 1/n */
/* the cutoff iteration is 650, as over e**650, term multiplication would
overflow. For larger numbers, the loop dominates the arctanh approximation
loop (with having 13-15 iterations on average for tested numbers so far */
for ( term = 1; term < n && lgVal < 650 ; term *= ee, lgVal++ );
if ( lgVal == 650 ){
n /= term;
for ( term = 1 ; term < n ; term *= ee, lgVal++ );
}
n /= term;
/* log(x) = 2 arctanh((x-1)/(x+1)) */
n = (1 - n)/(n + 1);
now = term = n;
n *= n;
sm = 0;
/* limiting the iteration for worst case scenario, maximum 24 iteration */
for ( i = 3 ; i < 50 ; i += 2 ){
sm += now;
term *= n;
now = term / i;
if ( now < 1e-17 ) break;
}
lgVal -= 2*sm;
if ( flag == -1 ) lgVal = -lgVal;
return lgVal;
}