Pi ยังคงผิดปกติ [ปิด]


27

พี่ผิด

วิธีการทั่วไปในการคำนวณ pi คือการโยน "ปาเป้า" ลงในกล่อง 1x1 และดูว่าที่ดินใดในวงกลมหน่วยเทียบกับโยนทั้งหมด:

loop
   x = rand()
   y = rand()
   if(sqrt(x*x + y*y) <= 1) n++
   t++
pi = 4.0*(n/t)

เขียนโปรแกรมที่ดูเหมือนว่าควรคำนวณ pi อย่างถูกต้อง (โดยใช้วิธีนี้หรือวิธีทั่วไปอื่น ๆ ในการคำนวณ pi) แต่คำนวณtau (tau = 2 * pi = 6.283185307179586 ... ) แทน รหัสของคุณจะต้องผลิตทศนิยมอย่างน้อย 6 ตำแหน่งแรก: 6.283185

ผู้ชนะจะครองตำแหน่งที่ 6 มิถุนายน (หนึ่งสัปดาห์นับจากวันนี้)


43
ทำไมผู้ชนะไม่ได้ครองตำแหน่งที่ 28 มิถุนายน
corsiKa

9
ฉันไม่แน่ใจว่าทำไมผู้ชนะต้องได้รับการสวมมงกุฎในการประกวดความนิยม
Tim S.

1
ฉันไม่เข้าใจ เช่นนี้ขอฟังก์ชั่นที่ปรากฏขึ้นจะกลับมาแต่ผลตอบแทน1 2พวกเราหลอกใครกันนะ?
ja72

3
@ ja72 ผู้อ่านของรหัส :)
tomsmeding

8
ทุกคนรู้ว่าโปเป็นหนึ่งที่ถูกต้อง : P
Justin Krejcha

คำตอบ:


57

JavaScript

alert(Math.atan2(0, -0) - Math.atan2(-0, -0) + Math.atan2(0, 0))

ช่วยฉันถูกขังอยู่ในโรงงานแห่งจักรวาลและฉันไม่แน่ใจว่าฉันกำลังทำอะไรอยู่ Math.atan2ควรจะคืนไพด้วยค่าที่ดีใช่ไหม Math.atan2(0, -0)ส่งคืน pi ดังนั้นหากฉันลบมันและเพิ่มมันฉันควรจะมี pi


14
ฉันคิดว่าฉันจะไปนอนและร้องไห้ Goddamnit, JavaScript
Jack M

3
กรุณาอธิบาย :)
Jaa-c

2
มุมทวนเข็มนาฬิกาเป็นเรเดียนระหว่างแกน x และจุด (Y, X) เครื่องหมายของจุด Y กำหนดว่านี่เป็นมุมบวกหรือลบและนี่จะกลายเป็นπ - (-π)

8
0_o >>> 0 === -0 ;true ;>>> Math.atan2(0, 0) ;0 ;>>> Math.atan2(0, -0) ;3.141592653589793
Izkata

5
@ JackM คำสั่งนั้นเหมาะสมเสมอที่จะพูด :) แม้ว่าในกรณีนี้มันเป็นเพราะมาตรฐาน IEEE และหลายภาษา (ไม่ใช่แค่ JS) มีปัญหากับศูนย์และศูนย์ลบ
พอลเดรเปอร์

40

ขั้นพื้นฐาน

(โดยเฉพาะอย่างยิ่งพื้นฐาน Chipmunk )

สิ่งนี้ใช้ชุดไม่สิ้นสุดที่ค้นพบโดย Nilakantha Somayaji ในศตวรรษที่ 15:

' Calculate pi using the Nilakantha series:
'               4       4       4       4
'  pi  =  3 + ----- - ----- + ----- - ------ + ...
'             2x3x4   4x5x6   6x7x8   8x9x10
i = pi = 0
numerator = -4
while i<10000
  i = i + 2
  numerator = -numerator
  pi = pi + numerator / (i * (i+1) * (i+2))
wend
pi = pi + 3
print using "#.##########";pi

เอาท์พุต

6.2831853072

หากคุณไม่สามารถทราบได้ว่าเกิดอะไรขึ้นต่อไปนี้เป็นคำแนะนำบางประการ

ใน Chipmunk Basic ตัวแปรpiจะถูกตั้งค่าเป็น value เมื่อโปรแกรมเริ่มทำงาน

และ

ใน BASIC เครื่องหมายเท่ากับถูกใช้ทั้งในการกำหนดตัวแปรและสำหรับการทดสอบความเท่าเทียมกัน ดังนั้นA = B = Cถูกตีความว่าเป็นA = (ข == ค)


รอฉันไม่ได้รับมันดังนั้นiเท่ากับfalse? แล้วคุณ2จะเพิ่มมัน? และมันใช้งานได้ ???
Dunno

2
@Dunno: แน่นอนว่าลูปเริ่มต้นที่ซึ่งคล้ายกับi == false i == 0ประเด็นก็คือค่าเริ่มต้นสำหรับตัวสะสมpiไม่ใช่ 0 …
Bergi

1
@Bergi ใช่ฉันไม่สามารถห่อหัวของฉันรอบความจริงที่ว่าfalse + 2 == 2: D
Dunno

@Dunno Dynamic การพิมพ์และอื่น ๆ : false ถูกแปลงเป็น 0 โดยปริยายเมื่อทำคณิตศาสตร์ นอกจากนี้คุณยังมีพฤติกรรมที่ชัดเจนเหมือนกันใน C ซึ่งขาดboolประเภทและการใช้งาน0และไม่เป็นศูนย์เพื่อเป็นตัวแทนfalseและtruerepsectively ไม่ว่ามันจะสง่างาม แต่เฮ้นั่นคือวิธีการทำงาน
Suzanne Dupéron

15

C - ความยาวครึ่งวงกลมหน่วย

วิธีหนึ่งในการคำนวณπนั้นเพียงเพื่อวัดระยะทางที่จุด(1, 0)เดินทางเมื่อหมุนรอบจุดกำเนิดไป(-1, 0)เนื่องจากจะเป็นครึ่งหนึ่งของเส้นรอบวงของวงกลมหน่วย (ซึ่งคือ )

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

อย่างไรก็ตามยังไม่มีการsin(x)หรือcos(x)เป็นสิ่งจำเป็นตั้งแต่นี้สามารถทำได้โดยการก้าวทุกทางรอบต้นกำเนิดและการเพิ่มระยะทางที่จุดเดินทางสำหรับแต่ละขั้นตอน ขนาดที่มีขนาดเล็กสำหรับแต่ละขั้นตอนถูกต้องมากขึ้นπคุณจะได้รับ

หมายเหตุ:การก้าวจะสิ้นสุดเมื่อ y ต่ำกว่าศูนย์ (ซึ่งก็คือเมื่อมันผ่านไป(-1, 0))

#include <stdio.h>                          // for printf
#define length(y, x) ((x * x) + (y * y))
int main()
{
    double x, y;
    double pi, tau, step;
    // start at (2, 0) which actually calculates tau
    x  = 2;
    y  = 0;
    // the step needs to be very low for high accuracy
    step = 0.00000001;  
    tau = 0;
    while (y >= 0)
    {   // the derivate of (x, y) is itself rotated 90 degrees
        double dx = -y;
        double dy = x;

        tau += length(dx, dy) * step; // add the distance for each step to tau
        // add the distance to the point (make a tiny rotation)
        x += dx * step;
        y += dy * step;
    }
    pi = tau / 2;   // divide tau with 2 to get pi

    /* ignore this line *\                      pi *= 2;    /* secret multiply ^-^ */

    // print the value of pi
    printf("Value of pi is %f", pi); getchar(); 
    return 0;
}

มันให้ผลลัพธ์ต่อไปนี้:

Value of pi is 6.283185

3
ดูเหมือนว่าจะถูกต้อง ...
bjb568

1
lengthมาโครของคุณไม่มี sqrt นั่นตั้งใจหรือไม่ xและyสลับไปมาระหว่างคำจำกัดความและการโทร (โดยไม่มีผลกระทบ)
Ben Voigt

@BenVoigt Shhh! อย่าเสียเล่ห์เหลี่ยม แต่ใช่ sqrtถูกละเว้นโดยไม่ตั้งใจเพื่อให้ค่าของ pi ถูกพิมพ์เป็น 6,28 ... นอกจากนี้ยังมี +1 สำหรับการสังเกตxและyที่ฉันไม่ได้!
Thism2

1
โอ้ตอนนี้ฉันเห็นว่าคุณไม่ได้ติดตามวงกลมหน่วย แต่อย่างใดอย่างหนึ่งที่มีรัศมี 2 ใช่ที่ทำงานได้ดี
Ben Voigt

7
ฉันต้องสารภาพว่าก่อนที่จะทำความเข้าใจวิธีการทำงานผมเสียไม่กี่นาทีโดยไม่ละเว้นบรรทัดที่ ...
loreb

10

C

(เรื่องนี้จบลงด้วยการนานกว่าที่ตั้งใจไว้ แต่ฉันจะโพสต์มันต่อไป ... )

ในศตวรรษที่ 17 วาลลิสได้ตีพิมพ์ซีรี่ส์ที่ไม่มีที่สิ้นสุดสำหรับ Pi:

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

(ดูผลิตภัณฑ์ ใหม่ที่ไม่มีที่สิ้นสุดของวอลลิสและคาตาลันสำหรับ e, e และ√ (2 + √2)สำหรับข้อมูลเพิ่มเติม)

ตอนนี้เพื่อคำนวณ Pi เราต้องคูณด้วยสองก่อนเพื่อแยกตัวประกอบ:

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

โซลูชันของฉันจะคำนวณอนุกรมอนันต์สำหรับ Pi / 2 และสองแล้วคูณค่าทั้งสองเข้าด้วยกัน โปรดทราบว่าผลิตภัณฑ์ที่ไม่มีที่สิ้นสุดนั้นจะค่อยๆบรรจบกันอย่างไม่น่าเชื่อเมื่อคำนวณค่าสุดท้าย

เอาท์พุท:

pi: 6.283182
#include "stdio.h"
#include "stdint.h"

#define ITERATIONS 10000000
#define one 1

#define IEEE_MANTISSA_MASK 0xFFFFFFFFFFFFFULL

#define IEEE_EXPONENT_POSITION 52
#define IEEE_EXPONENT_BIAS 1023

// want to get an exact as possible result, so convert
// to integers and do custom 64-bit multiplication.
double multiply(double aa, double bb)
{
    // the input values will be between 1.0 and 2.0
    // so drop these to less than 1.0 so as not to deal 
    // with the double exponents.
    aa /= 2;
    bb /= 2;

    // extract fractional part of double, ignoring exponent and sign
    uint64_t a = *(uint64_t*)&aa & IEEE_MANTISSA_MASK;
    uint64_t b = *(uint64_t*)&bb & IEEE_MANTISSA_MASK;

    uint64_t result = 0x0ULL;

    // multiplying two 64-bit numbers is a little tricky, this is done in two parts,
    // taking the upper 32 bits of each number and multiplying them, then
    // then doing the same for the lower 32 bits.
    uint64_t a_lsb = (a & 0xFFFFFFFFUL);
    uint64_t b_lsb = (b & 0xFFFFFFFFUL);

    uint64_t a_msb = ((a >> 32) & 0xFFFFFFFFUL);
    uint64_t b_msb = ((b >> 32) & 0xFFFFFFFFUL);

    uint64_t lsb_result = 0;
    uint64_t msb_result = 0;

    // very helpful link explaining how to multiply two integers
    // http://stackoverflow.com/questions/4456442/interview-multiplication-of-2-integers-using-bitwise-operators
    while(b_lsb != 0)
    {
        if (b_lsb & 01)
        {
            lsb_result = lsb_result + a_lsb;
        }
        a_lsb <<= 1;
        b_lsb >>= 1;
    }
    while(b_msb != 0)
    {
        if (b_msb & 01)
        {
            msb_result = msb_result + a_msb;
        }
        a_msb <<= 1;
        b_msb >>= 1;
    }

    // find the bit position of the most significant bit in the higher 32-bit product (msb_answer)
    uint64_t x2 = msb_result;
    int bit_pos = 0;
    while (x2 >>= 1)
    {
        bit_pos++;
    }

    // stuff bits from the upper 32-bit product into the result, starting at bit 51 (MSB of mantissa)
    int result_position = IEEE_EXPONENT_POSITION - 1;
    for(;result_position > 0 && bit_pos > 0; result_position--, bit_pos--)
    {
        result |= ((msb_result >> bit_pos) & 0x01) << result_position;
    }

    // find the bit position of the most significant bit in the lower 32-bit product (lsb_answer)
    x2 = lsb_result;
    bit_pos = 0;
    while (x2 >>= 1)
    {
        bit_pos++;
    }

    // stuff bits from the lowre 32-bit product into the result, starting at whatever position
    // left off at from above.
    for(;result_position > 0 && bit_pos > 0; result_position--, bit_pos--)
    {
        result |= ((lsb_result >> bit_pos) & 0x01) << result_position;
    }

    // create hex representation of the answer
    uint64_t r = (uint64_t)(/* exponent */ (uint64_t)IEEE_EXPONENT_BIAS << IEEE_EXPONENT_POSITION) |
            (uint64_t)( /* fraction */ (uint64_t)result & IEEE_MANTISSA_MASK);

    // stuff hex into double
    double d = *(double*)&r;

    // since the two input values were divided by two,
    // need to multiply by four to fix the result.
    d *= 4;

   return d;
}

int main()
{
    double pi_over_two = one;
    double two = one;

    double num = one + one;
    double dem = one;

    int i=0;

    i=ITERATIONS;
    while(i--)
    {
        // pi = 2 2 4 4 6 6 8 8 ...
        // 2    1 3 3 5 5 7 7 9
        pi_over_two *= num / dem;

        dem += one + one;

        pi_over_two *= num / dem;

        num += one + one;
    }

    num = one + one;
    dem = one;

    i=ITERATIONS;
    while(i--)
    {
        // 2 = 2 4 4 6   10 12 12 14
        //     1 3 5 7    9 11 13 15
        two *= num / dem;

        dem += one + one;
        num += one + one;

        two *= num / dem;

        dem += one + one;

        two *= num / dem;

        dem += one + one;
        num += one + one;

        two *= num / dem;

        dem += one + one;
        num += one + one + one + one;
    }

    printf("pi: %f\n", multiply(pi_over_two, two));

    return 0;
}

ไม่สามารถละเว้นเลขชี้กำลังในการแปลงสองครั้งได้ หากเป็นการเปลี่ยนแปลงเพียงอย่างเดียว (ปล่อยให้หารด้วย 2 คูณด้วยคูณด้วย 4 คูณด้วยจำนวนเต็ม) ทุกอย่างทำงานได้อย่างน่าประหลาดใจ


8

Java - Nilakantha ซีรีส์

ซีรี่ส์ Nilakantha มอบให้เป็น:

pi = 3 + 4/(2*3*4) - 4/(4*5*6) + 4/(6*7*8) - 4/(8*9*10) ...

ดังนั้นสำหรับแต่ละเทอมตัวหารจึงถูกสร้างขึ้นโดยการคูณจำนวนเต็มต่อเนื่องโดยเริ่มต้นที่ 2 โดยแต่ละเทอม โปรดสังเกตว่าคุณเพิ่ม / ลบคำอื่น ๆ

public class NilakanthaPi {
    public static void main(String[] args) {
        double pi = 0;
        // five hundred terms
        for(int t=1;t<500;t++){
            // each i is 2*term
            int i=t*2;
            double part = 4.0 / ((i*i*t)+(3*i*t)+(2*t));
            // flip sign for alternating terms
            if(t%2==0)
                pi -= part;
            else
                pi += part;
            // add 3 for first term
            if(t<=2)
                pi += 3;
        }
        System.out.println(pi);
    }
}

หลังจากห้าร้อยคำเราจะได้รับการประมาณค่าที่เหมาะสมของ pi:

6.283185311179568

4

C ++: Madhava แห่ง Sangamagrama

ซีรี่ส์ที่ไม่มีที่สิ้นสุดนี้เป็นที่รู้จักกันในชื่อMadhava-Leibniz :

ชุด

เริ่มต้นด้วยรากที่สองของ 48 และคูณด้วยผลรวมของ (-3) -k / (2k + 1) ตรงไปตรงมาและง่ายต่อการใช้งาน:

long double findPi(int iterations)
{
    long double value = 0.0;

    for (int i = 0; i < iterations; i++) {
        value += powl(-3.0, -i) / (2 * i + 1);
    }

    return sqrtl(48.0) * value;
}

int main(int argc, char* argv[])
{
    std::cout << "my pi: " << std::setprecision(16) << findPi(1000) << std::endl;

    return 0;
}

เอาท์พุท:

my pi: 6.283185307179588

3

Python - ทางเลือกสำหรับซีรี่ส์ Nilakantha

นี่เป็นอีกซีรีย์ที่ไม่มีที่สิ้นสุดในการคำนวณ pi ที่ค่อนข้างเข้าใจง่าย

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

สำหรับสูตรนี้ใช้เวลา 6 และเริ่มสลับกันระหว่างการบวกและการลบเศษส่วนด้วยตัวเลข 2 และตัวส่วนที่เป็นผลคูณของจำนวนเต็มสองตัวติดต่อกันและผลรวมของพวกเขา เศษส่วนที่ตามมาแต่ละชุดจะเริ่มต้นด้วยจำนวนเต็มที่เพิ่มขึ้น 1 ทำสิ่งนี้ออกมาสองสามครั้งและผลลัพธ์ก็ใกล้เคียงกับ pi

pi = 6
sign = 1
for t in range(1,500):
i = t+1
   part = 2.0 / (i*t*(i+t))
   pi = pi + sign * part
   sign = - sign # flip sign for alternating terms  
print(pi)

ซึ่งให้ 6.283185


-1
#include "Math.h"
#include <iostream>
int main(){
    std::cout<<PI;
    return 0;
}

math.h:

#include <Math.h>
#undef PI
#define PI 6.28

ผลลัพธ์: 6.28

#include "Math.h" ไม่เหมือนกับ #include แต่เพียงแค่ดูไฟล์หลักเกือบจะไม่มีใครคิดว่าจะตรวจสอบ เห็นได้ชัดว่าอาจมี แต่ปัญหาที่คล้ายกันปรากฏในโครงการที่ฉันทำงานและไปตรวจไม่พบเป็นเวลานาน


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