Pi masih salah [ditutup]

27

Pi salah

Metode komputasi pi yang umum adalah dengan melemparkan "anak panah" ke dalam kotak 1x1 dan melihat tanah mana dalam satuan lingkaran dibandingkan dengan total yang dilemparkan:

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

Tulislah sebuah program yang kelihatannya harus menghitung pi dengan benar (menggunakan pi ini atau metode umum lainnya dalam menghitung pi) tetapi hitung tau (tau = 2 * pi = 6.283185307179586 ...) sebagai gantinya. Kode Anda harus menghasilkan setidaknya 6 desimal pertama: 6.283185

Pemenang dimahkotai 6 Juni (satu minggu dari hari ini).

Kyle Kanos
sumber
43
Mengapa pemenangnya tidak dinobatkan 28 Juni ??
corsiKa
9
Saya tidak yakin mengapa seorang pemenang perlu dimahkotai dalam kontes popularitas.
Tim S.
1
Saya tidak mengerti. Ini seperti meminta fungsi yang tampaknya mengembalikan 1tetapi mengembalikan 2. Siapa yang kita bodohkan di sini?
ja72
3
@ ja72 Pembaca kode :)
tomsmeding
8
Semua orang tahu bahwa pau adalah yang benar . : P
Justin Krejcha

Jawaban:

57

JavaScript

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

Tolong, saya terjebak di pabrik alam semesta , dan saya tidak yakin apa yang saya lakukan. Math.atan2seharusnya mengembalikan pi dengan nilai bagus, kan? Math.atan2(0, -0)mengembalikan pi, jadi jika saya kurangi, dan tambahkan, saya harus tetap memiliki pi.

Konrad Borowski
sumber
14
Saya pikir saya hanya akan berbaring dan menangis. Sialan, JavaScript.
Jack M
3
tolong jelaskan? :)
Jaa-c
2
Sudut berlawanan arah dalam radian antara sumbu x dan titik (Y, X). Tanda titik Y menentukan apakah ini adalah sudut positif atau negatif, dan ini menjadiπ - (-π)
8
0_o >>> 0 === -0 ;true ;>>> Math.atan2(0, 0) ;0 ;>>> Math.atan2(0, -0) ;3.141592653589793
Izkata
5
@ JackM, pernyataan itu selalu tepat untuk mengatakan :) Meskipun dalam kasus ini, itu karena standar IEEE, dan banyak bahasa (bukan hanya JS) memiliki masalah nol vs negatif nol.
Paul Draper
40

DASAR

(Lebih khusus, Chipmunk Basic )

Ini menggunakan seri tak terbatas yang ditemukan oleh Nilakantha Somayaji di abad ke-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

Keluaran

6.2831853072

Jika Anda tidak tahu apa yang sedang terjadi, berikut adalah beberapa petunjuk:

Dalam Chipmunk Basic, pi variabel diatur ke nilai π saat program mulai berjalan.

dan

Dalam BASIC, tanda sama dengan digunakan untuk menentukan variabel dan untuk menguji kesetaraan. Jadi a = b = c ditafsirkan sebagai a = (b == c) .

osifrque melengking
sumber
Tunggu Aku tidak mengerti, jadi isama false? Dan kemudian Anda menambahkannya 2? Dan itu berhasil ???
Entah
2
@ Tidak Tahu: Tentu, loop dimulai pada i == falseyang mirip dengan i == 0. Intinya adalah bahwa nilai awal untuk akumulator pibukanlah 0 ...
Bergi
1
@Bergi ya saya hanya tidak bisa membungkus kepala saya di sekitar fakta bahwa false + 2 == 2: D
Entah tanggal
@ Tidak Tahu Pengetikan dinamis dll .: false secara implisit dikonversi ke 0 saat melakukan aritmatika. Anda juga memiliki perilaku nyata yang sama dalam C yang tidak memiliki booltipe, dan menggunakan 0dan tidak nol untuk mewakili falsedan secara trueberulang. Bukannya elegan, tapi hei, begitulah cara kerjanya.
Suzanne Dupéron
15

C - Panjang setengah lingkaran unit

Salah satu cara untuk menghitung π adalah hanya untuk mengukur jarak titik (1, 0)perjalanan ketika berputar di sekitar asal untuk (-1, 0)karena akan setengah keliling lingkaran satuan (yang ).

masukkan deskripsi gambar di sini

Namun, tidak ada sin(x)atau cos(x)diperlukan karena ini dapat dilakukan dengan melangkah di sekitar titik asal dan menambahkan jarak yang ditempuh titik untuk setiap langkah . Semakin kecil ukuran untuk setiap langkah, semakin akurat π yang akan Anda dapatkan.

Catatan: Langkah ini akan berakhir ketika y di bawah nol (yang sama seperti saat melewati (-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;
}

Ini memberikan output sebagai berikut:

Value of pi is 6.283185
Ini m2
sumber
3
Tampak sah ... Pasti.
bjb568
1
lengthMakro Anda tidak memiliki sqrt. Apakah itu dimaksudkan? xdan yjuga bertukar antara definisi dan panggilan (tanpa efek)
Ben Voigt
@BenVoigt Shhh! Jangan merusak triknya, tapi ya. sqrtitu sengaja dihilangkan sehingga nilai dari pi dicetak sebagai 6,28 ... Juga 1 untuk memerhatikan xdan yyang saya tidak!
Ini 2
1
oh, sekarang saya melihat Anda melacak bukan unit lingkaran, tetapi satu dengan jari-jari 2. Ya, itu bekerja dengan baik.
Ben Voigt
7
Saya harus mengakui bahwa sebelum memahami cara kerjanya, saya menyia-nyiakan beberapa menit dengan tidak mengabaikan kalimat itu ...
loreb
10

C

(Ini akhirnya lebih panjang dari yang dimaksudkan, tapi aku akan mempostingnya ...)

Pada abad ke-17, Wallis menerbitkan seri tak terbatas untuk Pi:

masukkan deskripsi gambar di sini

(Lihat Produk Infinite Jenis Wallis dan Catalan Baru untuk π, e, dan √ (2 + √2) untuk informasi lebih lanjut)

Sekarang, untuk menghitung Pi, pertama-tama kita harus mengalikan dua dengan faktor faktor penyebut:

masukkan deskripsi gambar di sini

Solusi saya kemudian menghitung deret tak terhingga untuk Pi / 2 dan dua dan kemudian mengalikan kedua nilai tersebut bersama-sama. Perhatikan bahwa produk tak terbatas sangat lambat untuk berkumpul ketika menghitung nilai akhir.

keluaran:

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

Eksponen dalam konversi ganda sebenarnya tidak dapat diabaikan. Jika itu satu-satunya perubahan (biarkan membagi dengan 2, yang dikalikan dengan 4, perkalian bilangan bulat) semuanya secara mengejutkan berfungsi.


sumber
8

Seri Java - Nilakantha

Seri Nilakantha diberikan sebagai:

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

Jadi untuk setiap istilah, penyebut dikonstruksikan dengan mengalikan bilangan bulat berurutan, dengan awal naik 2 setiap istilah. Perhatikan bahwa Anda menambahkan / mengurangi istilah bergantian.

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

Setelah lima ratus istilah, kami mendapatkan estimasi pi yang masuk akal:

6.283185311179568
Geobit
sumber
4

C ++: Madhava of Sangamagrama

Seri tak terbatas ini sekarang dikenal sebagai Madhava-Leibniz :

Seri

Mulailah dengan akar kuadrat dari 48 dan kalikan dengan hasil dari jumlah (-3) -k / (2k +1). Sangat mudah dan sederhana untuk diimplementasikan:

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

Keluaran:

my pi: 6.283185307179588
Bruno Ferreira
sumber
3

Python - Sebuah Alternatif untuk seri Nilakantha

Ini adalah seri tak hingga untuk menghitung pi yang cukup mudah dimengerti.

masukkan deskripsi gambar di sini

Untuk rumus ini, ambil 6 dan mulai berganti-ganti antara menambah dan mengurangi pecahan dengan pembilang 2 dan penyebut yang merupakan produk dari dua bilangan bulat berturut-turut dan jumlah mereka. Setiap fraksi berikutnya mulai set bilangan bulatnya naik sebesar 1. Lakukan ini bahkan beberapa kali dan hasilnya mendekati 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)

yang memberi 6,283185.

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

Math.h:

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

Output: 6.28

#include "Math.h" tidak sama dengan #include, tetapi hanya dengan melihat file utama, hampir tidak ada yang berpikir untuk memeriksanya. Jelas mungkin, tetapi masalah serupa muncul dalam proyek yang sedang saya kerjakan, dan tidak terdeteksi untuk waktu yang lama.

Lucas
sumber
Meskipun demikian, solusi yang cerdas.
BobTheAwesome