Pi vẫn sai [đóng]


27

Pi là sai

Một phương pháp phổ biến của máy tính pi là ném "phi tiêu" vào hộp 1x1 và xem vùng đất nào trong vòng tròn đơn vị so với tổng số ném:

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

Viết một chương trình trông giống như nó sẽ tính toán chính xác pi (sử dụng phương pháp tính toán này hoặc các phương pháp phổ biến khác) nhưng thay vào đó hãy tính tau (tau = 2 * pi = 6.283185307179586 ...). Mã của bạn phải tạo ra ít nhất 6 chữ số thập phân đầu tiên: 6.283185

Người chiến thắng sẽ đăng quang vào ngày 6 tháng 6 (một tuần kể từ hôm nay).


43
Tại sao người chiến thắng không đăng quang vào ngày 28 tháng 6 ??
corsiKa

9
Tôi không chắc tại sao một người chiến thắng cần phải đăng quang trong một cuộc thi nổi tiếng.
Tim S.

1
Tôi không hiểu Điều này giống như yêu cầu một chức năng xuất hiện để trả về 1nhưng trả về 2. Chúng ta đang lừa ai đây?
ja72

3
@ ja72 Người đọc mã :)
tomsmeding

8
Mọi người đều biết rằng pau là một trong những chính xác . : P
Justin Krejcha

Câu trả lời:


57

JavaScript

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

Giúp tôi với , tôi bị mắc kẹt trong một nhà máy vũ trụ và tôi không chắc mình đang làm gì. Math.atan2được cho là trả lại pi với giá trị tốt, phải không? Math.atan2(0, -0)trả về pi, vì vậy nếu tôi trừ nó và thêm nó, tôi vẫn nên có pi.


14
Tôi nghĩ rằng tôi sẽ nằm xuống và khóc. Chết tiệt, JavaScript.
Jack M

3
Xin giải thích? :)
Jaa-c

2
Góc ngược chiều tính theo radian giữa trục x và điểm (Y, X). Dấu hiệu của điểm Y xác định xem đây là góc dương hay âm và điều này trở thànhπ - (-π)

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

5
@JackM, câu nói đó luôn luôn phù hợp để nói :) Mặc dù trong trường hợp này, đó là do tiêu chuẩn của IEEE và nhiều ngôn ngữ (không chỉ là JS) có vấn đề 0 so với âm 0.
Paul Draper

40

CĂN BẢN

(Cụ thể hơn, Chipmunk Basic )

Điều này sử dụng một loạt vô tận được phát hiện bởi Nilakantha Somayaji trong thế kỷ 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

Đầu ra

6.2831853072

Nếu bạn không thể hiểu chuyện gì đang xảy ra, đây là một vài gợi ý:

Trong Chipmunk Basic, biến pi được đặt trước giá trị của π khi chương trình bắt đầu chạy.

Trong BASIC, dấu bằng được sử dụng cả để gán biến và kiểm tra đẳng thức. Vậy a = b = c được hiểu là a = (b == c) .


Đợi tôi không hiểu, vì vậy ibằng false? Và sau đó bạn thêm 2vào nó? Và nó hoạt động???
Dunno

2
@Dunno: Chắc chắn, các vòng lặp bắt đầu i == falsetương tự như vậy i == 0. Vấn đề là giá trị ban đầu của bộ tích lũy pikhông phải là 0
Bergi

1
@Bergi yeah Tôi chỉ không thể quấn đầu xung quanh sự thật rằng false + 2 == 2: D
Dunno

@Dunno Gõ động, v.v .: false được chuyển đổi hoàn toàn thành 0 khi thực hiện số học. Bạn cũng có hành vi rõ ràng tương tự trong C thiếu một boolloại, và sử dụng 0và khác không để biểu diễn falsetruelặp lại. Không phải là nó thanh lịch, nhưng hey, đó là cách nó hoạt động.
Suzanne Dupéron

15

C - Chiều dài của nửa vòng tròn đơn vị

Một cách để tính toán π chỉ đơn giản là để đo khoảng cách điểm (1, 0)di chuyển khi xoay quanh nguồn gốc để (-1, 0)vì nó sẽ được một nửa chu vi của một vòng tròn đơn vị (đó là ).

nhập mô tả hình ảnh ở đây

Tuy nhiên, không sin(x)hoặc cos(x)là cần thiết vì điều này có thể được thực hiện bằng cách bước tất cả các cách xung quanh nguồn gốc thêm khoảng cách mà điểm di chuyển cho mỗi bước . Kích thước nhỏ hơn cho mỗi bước bạn sẽ nhận được số π chính xác hơn .

Lưu ý: Bước sẽ kết thúc khi y ở dưới 0 (giống như khi nó đi qua (-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;
}

Nó cho đầu ra sau:

Value of pi is 6.283185

3
Có vẻ hợp pháp.
bjb568

1
lengthMacro của bạn đang thiếu một sqrt. Đó có phải là dự định? xycũng được hoán đổi giữa định nghĩa và cuộc gọi (không có hiệu lực)
Ben Voigt

@BenVoigt Suỵt! Đừng làm hỏng mánh khóe, nhưng đúng vậy. sqrtđã vô tình bị bỏ qua để giá trị của pi được in là 6,28 ... Ngoài ra +1 để nhận thấy xyđiều đó tôi đã không làm!
Thism2

1
ồ, bây giờ tôi thấy bạn đang theo dõi không phải là một vòng tròn đơn vị, mà là một vòng tròn có bán kính 2. Yup, nó hoạt động rất tốt.
Ben Voigt

7
Tôi phải thú nhận rằng trước khi hiểu cách thức hoạt động của nó, tôi đã lãng phí một vài phút bằng cách không bỏ qua dòng đó ...
truyền thuyết

10

C

(Điều này cuối cùng đã dài hơn dự định, nhưng dù sao tôi cũng sẽ đăng nó ...)

Vào thế kỷ 17, Wallis đã xuất bản một bộ truyện vô tận cho Pi:

nhập mô tả hình ảnh ở đây

(Xem Sản phẩm vô hạn mới của Wallis- và Catalan cho các số π, e và (2 + 2) để biết thêm thông tin)

Bây giờ, để tính Pi, trước tiên chúng ta phải nhân hai để nhân ra mẫu số:

nhập mô tả hình ảnh ở đây

Giải pháp của tôi sau đó tính toán chuỗi vô hạn cho Pi / 2 và hai và sau đó nhân hai giá trị với nhau. Lưu ý rằng các sản phẩm vô hạn rất chậm hội tụ khi tính toán các giá trị cuối cùng.

đầu ra:

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;
}

Số mũ trong chuyển đổi kép thực sự không thể bỏ qua. Nếu đó là thay đổi duy nhất (để lại số chia cho 2, nhân với 4, phép nhân số nguyên) thì mọi thứ sẽ hoạt động một cách đáng ngạc nhiên.


8

Dòng Java - Nilakantha

Sê-ri Nilakantha được đưa ra là:

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

Vì vậy, đối với mỗi thuật ngữ, mẫu số được xây dựng bằng cách nhân các số nguyên liên tiếp, với số bắt đầu tăng thêm 2 mỗi số hạng. Lưu ý rằng bạn thêm / bớt các thuật ngữ xen kẽ.

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);
    }
}

Sau năm trăm điều khoản, chúng tôi có được ước tính hợp lý của pi:

6.283185311179568

4

C ++: Madhava của Sangamagrama

Loạt vô tận này hiện được gọi là Madhava-Leibniz :

Loạt

Bắt đầu với căn bậc hai của 48 và nhân nó với kết quả của tổng (-3) -k / (2k + 1). Rất đơn giản và dễ thực hiện:

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;
}

Đầu ra:

my pi: 6.283185307179588

3

Python - Một sự thay thế cho loạt Nilakantha

Đây là một chuỗi vô hạn khác để tính pi khá dễ hiểu.

nhập mô tả hình ảnh ở đây

Đối với công thức này, lấy 6 và bắt đầu xen kẽ giữa cộng và trừ các phân số với tử số 2 và mẫu số là tích của hai số nguyên liên tiếp và tổng của chúng. Mỗi phân số tiếp theo bắt đầu tập hợp các số nguyên tăng thêm 1. Thực hiện điều này thậm chí một vài lần và kết quả khá gần với 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)

cung cấp cho 6.283185.


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

Toán.h:

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

Đầu ra: 6.28

#include "Math.h" không giống với #include, nhưng chỉ cần nhìn vào tệp chính, hầu như không ai nghĩ sẽ kiểm tra. Rõ ràng có lẽ, nhưng một vấn đề tương tự đã xuất hiện trong một dự án mà tôi đang thực hiện và đã không bị phát hiện trong một thời gian dài.


Một giải pháp thông minh dù sao.
BobTheAwgie
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.