วิธีที่เร็วที่สุดในการรับค่าของπคืออะไร?


322

ฉันกำลังมองหาวิธีที่เร็วที่สุดที่จะได้รับคุณค่าของπซึ่งเป็นความท้าทายส่วนบุคคล โดยเฉพาะอย่างยิ่งฉันใช้วิธีการที่ไม่เกี่ยวข้องกับการใช้#defineค่าคงที่เช่นM_PIหรือเข้ารหัสตัวเลขอย่างหนัก

โปรแกรมด้านล่างทดสอบวิธีต่างๆที่ฉันรู้ ในทางทฤษฎีแล้วแอสเซมบลีไลน์อินเวอร์ชั่นคือตัวเลือกที่เร็วที่สุดแม้ว่าจะไม่สามารถพกพาได้อย่างชัดเจน ฉันได้รวมมันเป็นพื้นฐานในการเปรียบเทียบกับรุ่นอื่น ในการทดสอบของฉันด้วยบิวด์อิน4 * atan(1)รุ่นนั้นเร็วที่สุดใน GCC 4.2 เพราะมันจะพับatan(1)ให้เป็นค่าคงที่โดยอัตโนมัติ หาก-fno-builtinระบุไว้atan2(0, -1)เวอร์ชันจะเร็วที่สุด

นี่คือโปรแกรมทดสอบหลัก ( pitimes.c):

#include <math.h>
#include <stdio.h>
#include <time.h>

#define ITERS 10000000
#define TESTWITH(x) {                                                       \
    diff = 0.0;                                                             \
    time1 = clock();                                                        \
    for (i = 0; i < ITERS; ++i)                                             \
        diff += (x) - M_PI;                                                 \
    time2 = clock();                                                        \
    printf("%s\t=> %e, time => %f\n", #x, diff, diffclock(time2, time1));   \
}

static inline double
diffclock(clock_t time1, clock_t time0)
{
    return (double) (time1 - time0) / CLOCKS_PER_SEC;
}

int
main()
{
    int i;
    clock_t time1, time2;
    double diff;

    /* Warmup. The atan2 case catches GCC's atan folding (which would
     * optimise the ``4 * atan(1) - M_PI'' to a no-op), if -fno-builtin
     * is not used. */
    TESTWITH(4 * atan(1))
    TESTWITH(4 * atan2(1, 1))

#if defined(__GNUC__) && (defined(__i386__) || defined(__amd64__))
    extern double fldpi();
    TESTWITH(fldpi())
#endif

    /* Actual tests start here. */
    TESTWITH(atan2(0, -1))
    TESTWITH(acos(-1))
    TESTWITH(2 * asin(1))
    TESTWITH(4 * atan2(1, 1))
    TESTWITH(4 * atan(1))

    return 0;
}

และชุดประกอบแบบอินไลน์ ( fldpi.c) ที่จะทำงานได้เฉพาะกับระบบ x86 และ x64:

double
fldpi()
{
    double pi;
    asm("fldpi" : "=t" (pi));
    return pi;
}

และสคริปต์สร้างที่สร้างการกำหนดค่าทั้งหมดที่ฉันกำลังทดสอบ ( build.sh):

#!/bin/sh
gcc -O3 -Wall -c           -m32 -o fldpi-32.o fldpi.c
gcc -O3 -Wall -c           -m64 -o fldpi-64.o fldpi.c

gcc -O3 -Wall -ffast-math  -m32 -o pitimes1-32 pitimes.c fldpi-32.o
gcc -O3 -Wall              -m32 -o pitimes2-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -fno-builtin -m32 -o pitimes3-32 pitimes.c fldpi-32.o -lm
gcc -O3 -Wall -ffast-math  -m64 -o pitimes1-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall              -m64 -o pitimes2-64 pitimes.c fldpi-64.o -lm
gcc -O3 -Wall -fno-builtin -m64 -o pitimes3-64 pitimes.c fldpi-64.o -lm

นอกเหนือจากการทดสอบระหว่างคอมไพเลอร์แฟล็กต่างๆ (ฉันได้เปรียบเทียบ 32- บิตกับ 64- บิตเช่นกันเนื่องจากการปรับให้เหมาะสมแตกต่างกัน) ฉันได้ลองสลับลำดับการทดสอบรอบ ๆ แต่ถึงกระนั้นatan2(0, -1)รุ่นยังคงออกมาด้านบนทุกครั้ง


38
มีวิธีที่จะทำใน C ++ metaprogramming เวลาทำงานจะดีมาก แต่เวลาในการรวบรวมจะไม่เป็นเช่นนั้น
David Thornley

1
ทำไมคุณถึงพิจารณาใช้ atan (1) แตกต่างจากการใช้ M_PI ฉันเข้าใจว่าทำไมคุณถึงต้องการทำเช่นนี้ถ้าคุณใช้การคำนวณทางคณิตศาสตร์เท่านั้น แต่ด้วย atan ฉันไม่เห็นประเด็น
erikkallen

9
คำถามคือทำไมคุณไม่ต้องการใช้ค่าคงที่ เช่นกำหนดโดยห้องสมุดหรือด้วยตัวเอง? การคำนวณ Pi เป็นการสิ้นเปลืองวงจร CPU เนื่องจากปัญหานี้ได้รับการแก้ไขซ้ำแล้วซ้ำอีกเป็นจำนวนตัวเลขที่มีนัยสำคัญมากกว่าที่จำเป็นสำหรับการคำนวณรายวัน
Tilo

2
@ HopelessN00b ในภาษาอังกฤษฉันพูดว่า "การเพิ่มประสิทธิภาพ" สะกดด้วย "s" ไม่ใช่ "z" (ซึ่งออกเสียงว่า "zed", BTW ไม่ใช่ "zee" ;-)) (นี่ไม่ใช่ครั้งแรกที่ฉันต้องย้อนกลับการแก้ไขประเภทนี้เช่นกันถ้าคุณดูที่ประวัติการตรวจสอบ)
Chris Jester-Young

2
@Pacerier ดูen.wiktionary.org/wiki/boggleและen.wiktionary.org/wiki/mindboggling
Chris Jester-Young

คำตอบ:


205

วิธีการมอนติคาร์โลดังที่กล่าวมาใช้แนวคิดที่ดีบางอย่าง แต่มันก็ชัดเจนไม่ใช่วิธีที่เร็วที่สุดไม่ใช่ด้วยการยิงยาวไม่ใช่ด้วยวิธีการที่สมเหตุสมผล นอกจากนี้ทุกอย่างขึ้นอยู่กับชนิดของความถูกต้องที่คุณกำลังมองหา fastest ที่ฉันรู้เร็วที่สุดคือรหัสที่มีตัวเลขกำกับยาก มองไปที่PiและPi [PDF]มีสูตรมากมาย

นี่คือวิธีการที่มาบรรจบกันอย่างรวดเร็ว - ประมาณ 14 หลักต่อการวนซ้ำ PiFastแอปพลิเคชั่นที่เร็วที่สุดในปัจจุบันใช้สูตรนี้กับ FFT ฉันจะเขียนสูตรเนื่องจากรหัสตรงไปตรงมา สูตรนี้เกือบถูกพบโดยRamanujan และค้นพบโดย Chudnovsky จริงๆแล้วมันเป็นวิธีที่เขาคำนวณตัวเลขหลายพันล้านหลัก - มันไม่ใช่วิธีที่จะมองข้าม สูตรจะล้นอย่างรวดเร็วและเนื่องจากเราแบ่งแฟคทอเรียลจึงเป็นประโยชน์ในการชะลอการคำนวณดังกล่าวเพื่อลบคำศัพท์

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ที่ไหน

ป้อนคำอธิบายรูปภาพที่นี่

ด้านล่างนี้คือขั้นตอนวิธีการ Brent-Salamin วิกิพีเดียกล่าวว่าเมื่อaและbนั้น "ใกล้พอ" จากนั้น(a + b) ² / 4tจะเป็นการประมาณπ ฉันไม่แน่ใจว่าคำว่า "ใกล้พอ" หมายความว่าอย่างไรจากการทดสอบของฉันการวนซ้ำหนึ่งครั้งมี 2 หลักสองได้ 7 และสามมี 15 แน่นอนนี้เป็นคู่กับดังนั้นมันอาจมีข้อผิดพลาดขึ้นอยู่กับการเป็นตัวแทนและ การคำนวณที่แท้จริงอาจแม่นยำยิ่งขึ้น

let pi_2 iters =
    let rec loop_ a b t p i =
        if i = 0 then a,b,t,p
        else
            let a_n = (a +. b) /. 2.0 
            and b_n = sqrt (a*.b)
            and p_n = 2.0 *. p in
            let t_n = t -. (p *. (a -. a_n) *. (a -. a_n)) in
            loop_ a_n b_n t_n p_n (i - 1)
    in 
    let a,b,t,p = loop_ (1.0) (1.0 /. (sqrt 2.0)) (1.0/.4.0) (1.0) iters in
    (a +. b) *. (a +. b) /. (4.0 *. t)

สุดท้ายนี้มี pi golf ประมาณ 800 หลักไหม? 160 ตัวอักษร!

int a=10000,b,c=2800,d,e,f[2801],g;main(){for(;b-c;)f[b++]=a/5;for(;d=0,g=c*2;c-=14,printf("%.4d",e+d/a),e=d%a)for(b=c;d+=f[b]*a,f[b]=d%--g,d/=g--,--b;d*=b);}

1
สมมติว่าคุณกำลังพยายามใช้สิ่งแรกด้วยตัวเอง sqr (k3) จะไม่เป็นปัญหาหรือไม่ ฉันค่อนข้างแน่ใจว่ามันจะจบลงด้วยจำนวนอตรรกยะที่คุณจะต้องประมาณ (IIRC, รากทั้งหมดที่ไม่ใช่จำนวนเต็มเป็นจำนวนอตรรกยะ) ทุกสิ่งทุกอย่างดูตรงไปตรงมาถ้าคุณใช้เลขคณิตความแม่นยำไม่สิ้นสุด แต่รากที่สองนั้นเป็นตัวแบ่งข้อตกลง คนที่สองรวมถึง sqrt เช่นกัน
Bill K

2
จากประสบการณ์ของฉัน 'ใกล้พอ' มักจะหมายถึงมีการประมาณซีรี่ส์เทย์เลอร์ที่เกี่ยวข้อง
สตีเฟ่น

117

ฉันชอบโปรแกรมนี้มากเพราะมันใกล้เคียงกับπโดยดูที่พื้นที่ของมัน

IOCCC 1988: westley.c

#define _ -F<00||--F-OO--;
int F=00,OO=00;main(){F_OO();printf("%1.3f\n",4.*-F/OO/OO);}F_OO()
{
            _-_-_-_
       _-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
_-_-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
 _-_-_-_-_-_-_-_-_-_-_-_-_-_-_
  _-_-_-_-_-_-_-_-_-_-_-_-_-_
    _-_-_-_-_-_-_-_-_-_-_-_
        _-_-_-_-_-_-_-_
            _-_-_-_
}

1
หากคุณแทนที่ _ ด้วย -F <00 || --F-OO - ควรทำตามง่ายกว่า :-)
Pat

1
หรือหากคุณแทนที่ _ ด้วย "if (อักขระก่อนหน้าคือ '-') {OO--;} F--;"
FryGuy

6
มันพิมพ์ 0.25 ที่นี่ -.
โยฮันเนส Schaub - litb

8
โปรแกรมนี้เป็นโปรแกรมที่ยอดเยี่ยมในปี 1998 แต่ถูกทำลายเนื่องจากตัวประมวลผลล่วงหน้าที่ทันสมัยมีความอิสระมากกว่าด้วยการแทรกช่องว่างรอบการขยายแมโครเพื่อป้องกันสิ่งเช่นนี้จากการทำงาน มันเป็นของที่ระลึกน่าเสียดาย
คริสลัทซ์

38
ผ่าน--traditional-cppไปยังcppเพื่อรับพฤติกรรมที่ต้องการ
Nietzche-jou

78

นี่คือคำอธิบายทั่วไปของเทคนิคสำหรับการคำนวณ pi ที่ฉันเรียนในโรงเรียนมัธยม

ฉันแชร์สิ่งนี้เพียงเพราะฉันคิดว่ามันง่ายพอที่ใคร ๆ ก็สามารถจดจำได้อย่างไม่มีกำหนดรวมถึงสอนแนวคิดของวิธีการ "Monte-Carlo" - ซึ่งเป็นวิธีการทางสถิติของการมาถึงคำตอบที่ไม่ปรากฏขึ้นทันที deducible ผ่านกระบวนการสุ่ม

วาดสี่เหลี่ยมจัตุรัสและจารึกจตุภาค (หนึ่งในสี่ของวงกลมครึ่งวงกลม) ภายในสี่เหลี่ยมนั้น (จตุภาคที่มีรัศมีเท่ากับด้านข้างของสี่เหลี่ยมจัตุรัสดังนั้นจึงเติมเต็มสี่เหลี่ยมมากที่สุดเท่าที่จะทำได้)

ตอนนี้โยนลูกดอกที่จัตุรัสแล้วบันทึกว่ามันอยู่ตรงไหน - นั่นคือเลือกจุดสุ่มที่ใดก็ได้ภายในจัตุรัส แน่นอนมันลงจอดภายในจัตุรัส แต่อยู่ในวงกลมครึ่งหรือไม่? บันทึกข้อเท็จจริงนี้

ทำซ้ำขั้นตอนนี้หลายครั้ง - และคุณจะพบว่ามีอัตราส่วนของจำนวนคะแนนภายในครึ่งวงกลมเมื่อเทียบกับจำนวนทั้งหมดที่ถูกโยนเรียกอัตราส่วนนี้ x

เนื่องจากพื้นที่ของสแควร์คือ r คูณ r คุณสามารถอนุมานได้ว่าพื้นที่ของวงกลมครึ่งคือ x คูณ r คูณ r (นั่นคือ x คูณ r กำลังสอง) ดังนั้น x คูณ 4 จะให้ไพ

นี่ไม่ใช่วิธีการใช้งานที่รวดเร็ว แต่มันเป็นตัวอย่างที่ดีของวิธีการ Monte Carlo และถ้าคุณมองไปรอบ ๆ คุณอาจพบว่าปัญหาหลายอย่างอื่นนอกทักษะการคำนวณของคุณสามารถแก้ไขได้ด้วยวิธีการดังกล่าว


2
นี่เป็นวิธีที่เราใช้ในการคำนวณ Pi ในโครงการ java ในโรงเรียน เพิ่งใช้เครื่องสุ่มที่จะมาพร้อมกับ x, y พิกัดและยิ่ง 'ปาเป้า' เรายิ่งเข้าใกล้ Pi เรามากขึ้น
Jeff Keslinke

55

ในความสนใจของความสมบูรณ์แบบเทมเพลต C ++ ซึ่งสำหรับการสร้างที่ปรับปรุงแล้วจะคำนวณการประมาณค่า PI ณ เวลารวบรวมและจะอินไลน์เป็นค่าเดียว

#include <iostream>

template<int I>
struct sign
{
    enum {value = (I % 2) == 0 ? 1 : -1};
};

template<int I, int J>
struct pi_calc
{
    inline static double value ()
    {
        return (pi_calc<I-1, J>::value () + pi_calc<I-1, J+1>::value ()) / 2.0;
    }
};

template<int J>
struct pi_calc<0, J>
{
    inline static double value ()
    {
        return (sign<J>::value * 4.0) / (2.0 * J + 1.0) + pi_calc<0, J-1>::value ();
    }
};


template<>
struct pi_calc<0, 0>
{
    inline static double value ()
    {
        return 4.0;
    }
};

template<int I>
struct pi
{
    inline static double value ()
    {
        return pi_calc<I, I>::value ();
    }
};

int main ()
{
    std::cout.precision (12);

    const double pi_value = pi<10>::value ();

    std::cout << "pi ~ " << pi_value << std::endl;

    return 0;
}

หมายเหตุสำหรับ I> 10 บิลด์ที่ปรับให้เหมาะสมอาจช้าเช่นเดียวกันกับการรันที่ไม่ปรับให้เหมาะสม สำหรับการวนซ้ำ 12 ครั้งฉันเชื่อว่ามีการโทรหาค่า 80k (ในกรณีที่ไม่มีการบันทึก)


ฉันเรียกใช้งานและรับ "pi ~ 3.14159265383"
maxwellb

5
นั่นถูกต้องกับ 9dp คุณกำลังคัดค้านบางสิ่งหรือเพียงแค่ทำการสังเกตการณ์?
jon-hanson

อัลกอริธึมใช้ชื่ออะไรในการคำนวณ PI
Sebastião Miranda

1
สูตรของ @ sebastião-miranda Leibnizพร้อมการเร่งความเร็วเฉลี่ยปรับปรุงการบรรจบกัน pi_calc<0, J>คำนวณแต่ละคำที่ต่อเนื่องจากสูตรและค่าที่ไม่เฉพาะเจาะจงจะpi_calc<I, J>คำนวณค่าเฉลี่ย
jon-hanson

43

มีหนังสือทั้งหมดเล่มหนึ่งที่อุทิศให้กับวิธีการอย่างรวดเร็วสำหรับการคำนวณ \ pi: 'Pi และ AGM' โดย Jonathan และ Peter Borwein ( มีใน Amazon )

ฉันศึกษา AGM และอัลกอริธึมที่เกี่ยวข้องสักหน่อยมันค่อนข้างน่าสนใจ (แม้ว่าบางครั้งก็ไม่สำคัญ)

โปรดทราบว่าในการใช้อัลกอริทึมที่ทันสมัยที่สุดในการคำนวณ \ pi คุณจะต้องใช้ไลบรารีทางคณิตศาสตร์แบบหลายครั้ง ( GMPเป็นตัวเลือกที่ดีแม้ว่าจะเป็นเวลานานแล้วที่ฉันใช้มันครั้งล่าสุด)

เวลาความซับซ้อนของอัลกอริธึมที่ดีที่สุดคือใน O (M (n) log (n)) โดยที่ M (n) เป็นเวลาที่ซับซ้อนสำหรับการคูณจำนวนเต็มสองจำนวน n-bit (M (n) = O (n) log (n) log (log (n))) โดยใช้อัลกอริธึมที่ใช้ FFT ซึ่งโดยทั่วไปจำเป็นต้องใช้เมื่อคำนวณตัวเลขของ \ pi และอัลกอริทึมดังกล่าวถูกนำไปใช้ใน GMP)

โปรดทราบว่าแม้ว่าคณิตศาสตร์ที่อยู่เบื้องหลังอัลกอริทึมอาจไม่น่าสนใจ แต่อัลกอริธึมเองก็มักจะเป็นโค้ดหลอกสองสามบรรทัดและการใช้งานมักจะตรงไปตรงมามาก (ถ้าคุณเลือกที่จะไม่เขียนเลขทศนิยมของคุณเอง


42

คำตอบต่อไปนี้ได้อย่างแม่นยำวิธีการที่จะทำเช่นนี้ในทางที่เร็วที่สุด - มีความพยายามในการคำนวณน้อย แม้ว่าคุณจะไม่ชอบคำตอบก็ตามคุณต้องยอมรับว่ามันเป็นวิธีที่เร็วที่สุดในการรับค่า PI

เร็วที่สุดวิธีที่จะได้รับค่าของพี่คือ

1) เลือกภาษาการเขียนโปรแกรมที่คุณชื่นชอบ 2) โหลดไลบรารีคณิตศาสตร์ 3) และพบว่า Pi มีการกำหนดไว้แล้ว - พร้อมใช้งาน!

ในกรณีที่คุณไม่มีห้องสมุดคณิตศาสตร์อยู่ในมือ ..

เร็ว SECONDวิธี (สารละลายสากลมากขึ้น) เป็น:

ค้นหา Pi บนอินเทอร์เน็ตเช่นที่นี่:

http://www.eveandersson.com/pi/digits/1000000 (1 ล้านหลัก .. ความแม่นยำของทศนิยมของคุณคืออะไร)

หรือที่นี่:

http://3.141592653589793238462643383279502884197169399375105820974944592.com/

หรือที่นี่:

http://en.wikipedia.org/wiki/Pi

มันเร็วมากที่จะหาตัวเลขที่คุณต้องการสำหรับความแม่นยำทางคณิตศาสตร์ที่คุณต้องการใช้และการกำหนดค่าคงที่คุณสามารถมั่นใจได้ว่าคุณจะไม่เสียเวลาอันมีค่าของ CPU

นี่ไม่เพียง แต่เป็นคำตอบที่ตลกขบขันเพียงบางส่วนเท่านั้น แต่ในความเป็นจริงแล้วถ้าใครจะไปข้างหน้าและคำนวณคุณค่าของ Pi ในแอปพลิเคชั่นจริง .. นั่นจะเป็นการเสียเวลาของ CPU ที่ค่อนข้างใหญ่ใช่ไหม อย่างน้อยฉันก็ไม่เห็นแอปพลิเคชั่นตัวจริงสำหรับพยายามคำนวณสิ่งนี้อีก

เรียนผู้ดูแล: โปรดทราบว่า OP ได้ถามว่า: "วิธีที่เร็วที่สุดในการรับมูลค่าของ PI"


เรียน Tilo: โปรดทราบว่า OP กล่าวว่า: "ฉันกำลังมองหาวิธีที่เร็วที่สุดในการรับค่าของ as ซึ่งเป็นความท้าทายส่วนบุคคลโดยเฉพาะอย่างยิ่งฉันใช้วิธีที่ไม่เกี่ยวข้องกับการใช้ #define ค่าคงที่เช่น M_PI หรือเข้ารหัสตัวเลขได้ยาก
สูงสุด

เรียน @Max: หมายเหตุโปรดที่สหกรณ์แก้ไขคำถามเดิมของพวกเขาหลังจากที่ผมตอบมัน - ที่แทบจะไม่ได้ความผิดของฉัน;) โซลูชั่นของฉันยังคงเป็นวิธีที่เร็วที่สุดและแก้ปัญหาด้วยการที่ต้องการความแม่นยำจุดใดลอยและไม่มีรอบการทำงานอย่างหรูหรา :)
Tilo

โอ้ขอโทษฉันไม่ได้ตระหนัก แค่ความคิดค่าคงที่รหัสตายตัวจะมีความแม่นยำน้อยกว่าการคำนวณไพหรือไม่ ผมคิดว่ามันขึ้นอยู่กับสิ่งที่มันเป็นภาษาและวิธีการสร้างความเต็มใจที่จะใส่ทั้งหมดของตัวเลขใน :-)
แม็กซ์

1
ประณามฉันลืมที่จะเพิ่มDear Tilo
สูงสุด


23

acos(-1)แทนการกำหนดปี่เป็นค่าคงที่ผมมักจะใช้


2
cos (-1) หรือ acos (-1)? :-P นั่น (หลัง) เป็นหนึ่งในกรณีทดสอบในรหัสเดิมของฉัน เป็นของฉันที่ต้องการ (พร้อมกับ atan2 (0, -1), ซึ่งเหมือนกับ acos (-1), ยกเว้น acos นั้นมักจะถูกนำมาใช้ในแง่ของ atan2), แต่คอมไพเลอร์บางตัวปรับให้เหมาะสำหรับ 4 * atan (1) !
Chris Jester-Young

21

นี่เป็นวิธี "คลาสสิค" ใช้งานง่ายมาก การติดตั้งในภาษาไพ ธ อน (ไม่ใช่ภาษาที่เร็วที่สุด):

from math import pi
from time import time


precision = 10**6 # higher value -> higher precision
                  # lower  value -> higher speed

t = time()

calc = 0
for k in xrange(0, precision):
    calc += ((-1)**k) / (2*k+1.)
calc *= 4. # this is just a little optimization

t = time()-t

print "Calculated: %.40f" % calc
print "Constant pi: %.40f" % pi
print "Difference: %.40f" % abs(calc-pi)
print "Time elapsed: %s" % repr(t)

ท่านสามารถหาข้อมูลเพิ่มเติมได้ที่นี่

อย่างไรก็ตามวิธีที่เร็วที่สุดในการรับค่า pi ใน python ที่แม่นยำเท่าที่คุณต้องการคือ:

from gmpy import pi
print pi(3000) # the rule is the same as 
               # the precision on the previous code

นี่คือส่วนของแหล่งที่มาสำหรับวิธี gmpy pi ฉันไม่คิดว่าโค้ดมีประโยชน์เท่ากับความคิดเห็นในกรณีนี้:

static char doc_pi[]="\
pi(n): returns pi with n bits of precision in an mpf object\n\
";

/* This function was originally from netlib, package bmp, by
 * Richard P. Brent. Paulo Cesar Pereira de Andrade converted
 * it to C and used it in his LISP interpreter.
 *
 * Original comments:
 * 
 *   sets mp pi = 3.14159... to the available precision.
 *   uses the gauss-legendre algorithm.
 *   this method requires time o(ln(t)m(t)), so it is slower
 *   than mppi if m(t) = o(t**2), but would be faster for
 *   large t if a faster multiplication algorithm were used
 *   (see comments in mpmul).
 *   for a description of the method, see - multiple-precision
 *   zero-finding and the complexity of elementary function
 *   evaluation (by r. p. brent), in analytic computational
 *   complexity (edited by j. f. traub), academic press, 1976, 151-176.
 *   rounding options not implemented, no guard digits used.
*/
static PyObject *
Pygmpy_pi(PyObject *self, PyObject *args)
{
    PympfObject *pi;
    int precision;
    mpf_t r_i2, r_i3, r_i4;
    mpf_t ix;

    ONE_ARG("pi", "i", &precision);
    if(!(pi = Pympf_new(precision))) {
        return NULL;
    }

    mpf_set_si(pi->f, 1);

    mpf_init(ix);
    mpf_set_ui(ix, 1);

    mpf_init2(r_i2, precision);

    mpf_init2(r_i3, precision);
    mpf_set_d(r_i3, 0.25);

    mpf_init2(r_i4, precision);
    mpf_set_d(r_i4, 0.5);
    mpf_sqrt(r_i4, r_i4);

    for (;;) {
        mpf_set(r_i2, pi->f);
        mpf_add(pi->f, pi->f, r_i4);
        mpf_div_ui(pi->f, pi->f, 2);
        mpf_mul(r_i4, r_i2, r_i4);
        mpf_sub(r_i2, pi->f, r_i2);
        mpf_mul(r_i2, r_i2, r_i2);
        mpf_mul(r_i2, r_i2, ix);
        mpf_sub(r_i3, r_i3, r_i2);
        mpf_sqrt(r_i4, r_i4);
        mpf_mul_ui(ix, ix, 2);
        /* Check for convergence */
        if (!(mpf_cmp_si(r_i2, 0) && 
              mpf_get_prec(r_i2) >= (unsigned)precision)) {
            mpf_mul(pi->f, pi->f, r_i4);
            mpf_div(pi->f, pi->f, r_i3);
            break;
        }
    }

    mpf_clear(ix);
    mpf_clear(r_i2);
    mpf_clear(r_i3);
    mpf_clear(r_i4);

    return (PyObject*)pi;
}

แก้ไข:ผมมีปัญหาบางอย่างกับการตัดและวางและเยื้องคุณสามารถหาแหล่งที่มาที่นี่


20

หากเร็วที่สุดคุณหมายถึงการพิมพ์รหัสที่เร็วที่สุดนี่คือคำตอบของgolfscript :

;''6666,-2%{2+.2/@*\/10.3??2*+}*`1000<~\;

18

หากคุณยินดีที่จะใช้การประมาณ355 / 113จะดีสำหรับตัวเลขทศนิยม 6 หลักและมีข้อได้เปรียบเพิ่มเติมในการใช้งานกับนิพจน์จำนวนเต็ม นั่นไม่ใช่สิ่งสำคัญในวันนี้เช่นเดียวกับ "ผู้ร่วมประมวลผลเลขทศนิยม" หยุดที่จะมีความหมายใด ๆ แต่มันค่อนข้างสำคัญครั้งเดียว


18

ใช้สูตรที่เหมือนแมชชิน

176 * arctan (1/57) + 28 * arctan (1/239) - 48 * arctan (1/682) + 96 * arctan(1/12943) 

[; \left( 176 \arctan \frac{1}{57} + 28 \arctan \frac{1}{239} - 48 \arctan \frac{1}{682} + 96 \arctan \frac{1}{12943}\right) ;], for you TeX the World people.

นำมาใช้ใน Scheme เช่น:

(+ (- (+ (* 176 (atan (/ 1 57))) (* 28 (atan (/ 1 239)))) (* 48 (atan (/ 1 682)))) (* 96 (atan (/ 1 12943))))


16

กับคู่:

4.0 * (4.0 * Math.Atan(0.2) - Math.Atan(1.0 / 239.0))

สิ่งนี้จะมีความแม่นยำถึง 14 ตำแหน่งทศนิยมเพียงพอที่จะเติมสองครั้ง (ความไม่ถูกต้องอาจเป็นเพราะส่วนที่เหลือของทศนิยมในส่วนโค้งแทนเจนต์ถูกตัดทอน)

เซทมันคือ 3.14159265358979323846 3ไม่ใช่ 64


16

Pi เท่ากับ 3! [ศ. Frink (ซิมป์สัน)]

โจ๊ก แต่นี่คือ C # (ต้องใช้. NET-Framework)

using System;
using System.Text;

class Program {
    static void Main(string[] args) {
        int Digits = 100;

        BigNumber x = new BigNumber(Digits);
        BigNumber y = new BigNumber(Digits);
        x.ArcTan(16, 5);
        y.ArcTan(4, 239);
        x.Subtract(y);
        string pi = x.ToString();
        Console.WriteLine(pi);
    }
}

public class BigNumber {
    private UInt32[] number;
    private int size;
    private int maxDigits;

    public BigNumber(int maxDigits) {
        this.maxDigits = maxDigits;
        this.size = (int)Math.Ceiling((float)maxDigits * 0.104) + 2;
        number = new UInt32[size];
    }
    public BigNumber(int maxDigits, UInt32 intPart)
        : this(maxDigits) {
        number[0] = intPart;
        for (int i = 1; i < size; i++) {
            number[i] = 0;
        }
    }
    private void VerifySameSize(BigNumber value) {
        if (Object.ReferenceEquals(this, value))
            throw new Exception("BigNumbers cannot operate on themselves");
        if (value.size != this.size)
            throw new Exception("BigNumbers must have the same size");
    }

    public void Add(BigNumber value) {
        VerifySameSize(value);

        int index = size - 1;
        while (index >= 0 && value.number[index] == 0)
            index--;

        UInt32 carry = 0;
        while (index >= 0) {
            UInt64 result = (UInt64)number[index] +
                            value.number[index] + carry;
            number[index] = (UInt32)result;
            if (result >= 0x100000000U)
                carry = 1;
            else
                carry = 0;
            index--;
        }
    }
    public void Subtract(BigNumber value) {
        VerifySameSize(value);

        int index = size - 1;
        while (index >= 0 && value.number[index] == 0)
            index--;

        UInt32 borrow = 0;
        while (index >= 0) {
            UInt64 result = 0x100000000U + (UInt64)number[index] -
                            value.number[index] - borrow;
            number[index] = (UInt32)result;
            if (result >= 0x100000000U)
                borrow = 0;
            else
                borrow = 1;
            index--;
        }
    }
    public void Multiply(UInt32 value) {
        int index = size - 1;
        while (index >= 0 && number[index] == 0)
            index--;

        UInt32 carry = 0;
        while (index >= 0) {
            UInt64 result = (UInt64)number[index] * value + carry;
            number[index] = (UInt32)result;
            carry = (UInt32)(result >> 32);
            index--;
        }
    }
    public void Divide(UInt32 value) {
        int index = 0;
        while (index < size && number[index] == 0)
            index++;

        UInt32 carry = 0;
        while (index < size) {
            UInt64 result = number[index] + ((UInt64)carry << 32);
            number[index] = (UInt32)(result / (UInt64)value);
            carry = (UInt32)(result % (UInt64)value);
            index++;
        }
    }
    public void Assign(BigNumber value) {
        VerifySameSize(value);
        for (int i = 0; i < size; i++) {
            number[i] = value.number[i];
        }
    }

    public override string ToString() {
        BigNumber temp = new BigNumber(maxDigits);
        temp.Assign(this);

        StringBuilder sb = new StringBuilder();
        sb.Append(temp.number[0]);
        sb.Append(System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator);

        int digitCount = 0;
        while (digitCount < maxDigits) {
            temp.number[0] = 0;
            temp.Multiply(100000);
            sb.AppendFormat("{0:D5}", temp.number[0]);
            digitCount += 5;
        }

        return sb.ToString();
    }
    public bool IsZero() {
        foreach (UInt32 item in number) {
            if (item != 0)
                return false;
        }
        return true;
    }

    public void ArcTan(UInt32 multiplicand, UInt32 reciprocal) {
        BigNumber X = new BigNumber(maxDigits, multiplicand);
        X.Divide(reciprocal);
        reciprocal *= reciprocal;

        this.Assign(X);

        BigNumber term = new BigNumber(maxDigits);
        UInt32 divisor = 1;
        bool subtractTerm = true;
        while (true) {
            X.Divide(reciprocal);
            term.Assign(X);
            divisor += 2;
            term.Divide(divisor);
            if (term.IsZero())
                break;

            if (subtractTerm)
                this.Subtract(term);
            else
                this.Add(term);
            subtractTerm = !subtractTerm;
        }
    }
}

15

คำนวณ PI ณ เวลารวบรวมด้วย D

(คัดลอกมาจากDSource.org )

/** Calculate pi at compile time
 *
 * Compile with dmd -c pi.d
 */
module calcpi;

import meta.math;
import meta.conv;

/** real evaluateSeries!(real x, real metafunction!(real y, int n) term)
 *
 * Evaluate a power series at compile time.
 *
 * Given a metafunction of the form
 *  real term!(real y, int n),
 * which gives the nth term of a convergent series at the point y
 * (where the first term is n==1), and a real number x,
 * this metafunction calculates the infinite sum at the point x
 * by adding terms until the sum doesn't change any more.
 */
template evaluateSeries(real x, alias term, int n=1, real sumsofar=0.0)
{
  static if (n>1 && sumsofar == sumsofar + term!(x, n+1)) {
     const real evaluateSeries = sumsofar;
  } else {
     const real evaluateSeries = evaluateSeries!(x, term, n+1, sumsofar + term!(x, n));
  }
}

/*** Calculate atan(x) at compile time.
 *
 * Uses the Maclaurin formula
 *  atan(z) = z - z^3/3 + Z^5/5 - Z^7/7 + ...
 */
template atan(real z)
{
    const real atan = evaluateSeries!(z, atanTerm);
}

template atanTerm(real x, int n)
{
    const real atanTerm =  (n & 1 ? 1 : -1) * pow!(x, 2*n-1)/(2*n-1);
}

/// Machin's formula for pi
/// pi/4 = 4 atan(1/5) - atan(1/239).
pragma(msg, "PI = " ~ fcvt!(4.0 * (4*atan!(1/5.0) - atan!(1/239.0))) );

2
น่าเสียดายที่แทนเจนต์เป็นอาร์กแทนเจนต์นั้นขึ้นอยู่กับปี่ค่อนข้างทำให้การคำนวณนี้เป็นโมฆะ
Grant Johnson

14

รุ่นนี้ (ใน Delphi) ไม่มีอะไรพิเศษ แต่อย่างน้อยก็เร็วกว่ารุ่น Nick Hodge ที่โพสต์บนบล็อกของเขา :) บนเครื่องของฉันใช้เวลาประมาณ 16 วินาทีในการทำซ้ำพันล้านครั้งให้ค่า3.14159265 25879 (ส่วนที่ถูกต้องเป็นตัวหนา)

program calcpi;

{$APPTYPE CONSOLE}

uses
  SysUtils;

var
  start, finish: TDateTime;

function CalculatePi(iterations: integer): double;
var
  numerator, denominator, i: integer;
  sum: double;
begin
  {
  PI may be approximated with this formula:
  4 * (1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 .......)
  //}
  numerator := 1;
  denominator := 1;
  sum := 0;
  for i := 1 to iterations do begin
    sum := sum + (numerator/denominator);
    denominator := denominator + 2;
    numerator := -numerator;
  end;
  Result := 4 * sum;
end;

begin
  try
    start := Now;
    WriteLn(FloatToStr(CalculatePi(StrToInt(ParamStr(1)))));
    finish := Now;
    WriteLn('Seconds:' + FormatDateTime('hh:mm:ss.zz',finish-start));
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.

13

ย้อนกลับไปในสมัยก่อนด้วยคำขนาดเล็กและการดำเนินการจุดลอยตัวที่ช้าหรือไม่มีอยู่เราเคยทำสิ่งนี้:

/* Return approximation of n * PI; n is integer */
#define pi_times(n) (((n) * 22) / 7)

สำหรับแอปพลิเคชันที่ไม่ต้องการความแม่นยำมาก (ตัวอย่างเช่นวิดีโอเกม) นี่เป็นไปอย่างรวดเร็วและแม่นยำเพียงพอ


11
355 / 113สำหรับการใช้งานมีความถูกต้องมากขึ้น แม่นยำมากสำหรับขนาดของตัวเลขที่เกี่ยวข้อง
David Thornley

เพิ่งออกจากความอยากรู้อยากเห็น: 22/7เป็น3 + 1/7
Agnius Vasiliauskas

13

หากคุณต้องการคำนวณการประมาณค่าของ for (ด้วยเหตุผลบางอย่าง) คุณควรลองอัลกอริทึมการแยกแบบไบนารี การปรับปรุงBBPของBellardให้ค่า PI ใน O (N ^ 2)


หากคุณต้องการได้รับการประมาณค่าของπเพื่อทำการคำนวณแล้ว:

PI = 3.141592654

ได้รับนั่นเป็นเพียงการประมาณค่าและไม่แม่นยำอย่างแท้จริง มันปิดน้อยกว่า 0.00000000004102 (สี่สิบล้านล้านประมาณ4 / 10,000 ล้าน )


ถ้าคุณต้องการทำคณิตศาสตร์ด้วยπคุณสามารถใช้ดินสอและกระดาษหรือแพ็คเกจพีชคณิตของคอมพิวเตอร์และใช้ค่าที่แน่นอนของ,

ถ้าคุณต้องการสูตรจริง ๆ สูตรนี้สนุก:

π = - i ln (-1)


สูตรของคุณขึ้นอยู่กับวิธีที่คุณกำหนด ln ในระนาบเชิงซ้อน มันจะไม่ต่อเนื่องกันไปตามเส้นหนึ่งในระนาบเชิงซ้อนและมันค่อนข้างธรรมดาสำหรับเส้นนั้นที่จะเป็นแกนจริงเชิงลบ
erikkallen

12

วิธีการของเบรนท์โพสต์ข้างต้นโดยคริสนั้นดีมาก โดยทั่วไปแล้วเบรนท์เป็นยักษ์ใหญ่ในสาขาเลขคณิตที่มีความแม่นยำสูง

หากสิ่งที่คุณต้องการคือตัวเลข Nth สูตร BBP ที่มีชื่อเสียง จะมีประโยชน์เป็นเลข ฐานสิบหก


1
ฉันไม่ได้โพสต์วิธีการเบรนต์ มันโพสต์โดย Andrea และฉันเพิ่งจะเป็นคนสุดท้ายที่แก้ไขโพสต์ :-) แต่ฉันเห็นด้วยโพสต์นั้นสมควรได้รับ upvote
Chris Jester-Young

1

กำลังคำนวณπจากพื้นที่วงกลม :-)

<input id="range" type="range" min="10" max="960" value="10" step="50" oninput="calcPi()">
<br>
<div id="cont"></div>

<script>
function generateCircle(width) {
    var c = width/2;
    var delta = 1.0;
    var str = "";
    var xCount = 0;
    for (var x=0; x <= width; x++) {
        for (var y = 0; y <= width; y++) {
            var d = Math.sqrt((x-c)*(x-c) + (y-c)*(y-c));
            if (d > (width-1)/2) {
                str += '.';
            }
            else {
                xCount++;
                str += 'o';
            }
            str += "&nbsp;" 
        }
        str += "\n";
    }
    var pi = (xCount * 4) / (width * width);
    return [str, pi];
}

function calcPi() {
    var e = document.getElementById("cont");
    var width = document.getElementById("range").value;
    e.innerHTML = "<h4>Generating circle...</h4>";
    setTimeout(function() {
        var circ = generateCircle(width);
        e.innerHTML  = "<pre>" + "π = " + circ[1].toFixed(2) + "\n" + circ[0] +"</pre>";
    }, 200);
}
calcPi();
</script>


0

อัลกอริทึมของ Chudnovsky นั้นค่อนข้างเร็วหากคุณไม่สนใจที่จะทำการสแควร์รูทและผู้บุกรุกสองคน มันมาบรรจบกันเพื่อความแม่นยำสองเท่าในเพียง 2 ซ้ำ

/*
    Chudnovsky algorithm for computing PI
*/

#include <iostream>
#include <cmath>
using namespace std;

double calc_PI(int K=2) {

    static const int A = 545140134;
    static const int B = 13591409;
    static const int D = 640320;

    const double ID3 = 1./ (double(D)*double(D)*double(D));

    double sum = 0.;
    double b   = sqrt(ID3);
    long long int p = 1;
    long long int a = B;

    sum += double(p) * double(a)* b;

    // 2 iterations enough for double convergence
    for (int k=1; k<K; ++k) {
        // A*k + B
        a += A;
        // update denominator
        b *= ID3;
        // p = (-1)^k 6k! / 3k! k!^3
        p *= (6*k)*(6*k-1)*(6*k-2)*(6*k-3)*(6*k-4)*(6*k-5);
        p /= (3*k)*(3*k-1)*(3*k-2) * k*k*k;
        p = -p;

        sum += double(p) * double(a)* b;
    }

    return 1./(12*sum);
}

int main() {

    cout.precision(16);
    cout.setf(ios::fixed);

    for (int k=1; k<=5; ++k) cout << "k = " << k << "   PI = " << calc_PI(k) << endl;

    return 0;
}

ผล:

k = 1   PI = 3.1415926535897341
k = 2   PI = 3.1415926535897931
k = 3   PI = 3.1415926535897931
k = 4   PI = 3.1415926535897931
k = 5   PI = 3.1415926535897931

0

แนวทางที่ดีกว่า

เพื่อให้ได้ผลลัพธ์ของค่าคงที่มาตรฐานเช่นpiหรือแนวคิดมาตรฐานอันดับแรกเราควรไปกับวิธีการของบิวด์อินที่มีอยู่ในภาษาที่คุณใช้ มันจะคืนค่าในวิธีที่เร็วและดีที่สุด ฉันใช้ python เพื่อเรียกใช้วิธีที่เร็วที่สุดเพื่อให้ได้ค่าของ pi

  • ตัวแปรปี่ห้องสมุดคณิตศาสตร์ ไลบรารีคณิตศาสตร์เก็บตัวแปร pi เป็นค่าคงที่

math_pi.py

import math
print math.pi

รันสคริปต์ด้วยอรรถประโยชน์เวลาของ linux /usr/bin/time -v python math_pi.py

เอาท์พุท:

Command being timed: "python math_pi.py"
User time (seconds): 0.01
System time (seconds): 0.01
Percent of CPU this job got: 91%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.03
  • ใช้วิธีการอาร์ค cos ของคณิตศาสตร์

acos_pi.py

import math
print math.acos(-1)

รันสคริปต์ด้วยอรรถประโยชน์เวลาของ linux /usr/bin/time -v python acos_pi.py

เอาท์พุท:

Command being timed: "python acos_pi.py"
User time (seconds): 0.02
System time (seconds): 0.01
Percent of CPU this job got: 94%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.03

bbp_pi.py

from decimal import Decimal, getcontext
getcontext().prec=100
print sum(1/Decimal(16)**k * 
          (Decimal(4)/(8*k+1) - 
           Decimal(2)/(8*k+4) - 
           Decimal(1)/(8*k+5) -
           Decimal(1)/(8*k+6)) for k in range(100))

รันสคริปต์ด้วยอรรถประโยชน์เวลาของ linux /usr/bin/time -v python bbp_pi.py

เอาท์พุท:

Command being timed: "python c.py"
User time (seconds): 0.05
System time (seconds): 0.01
Percent of CPU this job got: 98%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.06

ดังนั้นวิธีที่ดีที่สุดคือการใช้วิธีการแบบ builtin ที่จัดทำโดยภาษาเพราะวิธีที่เร็วที่สุดและดีที่สุดในการรับผลลัพธ์ ในหลามใช้ math.pi

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.