Пі все ще помиляється [закрито]


27

Пі неправильно

Поширений метод обчислення пі - це кидання "дротиків" у вікно 1х1 і бачення, яка земля в одиничному колі порівняно із загальним викинутим:

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
Я не впевнений, чому переможця потрібно одержати на конкурсі популярності.
Тім С.

1
Я не розумію. Це як запит про функцію, яка, здається, повертається, 1але повертається 2. Кого ми тут дуримо?
ja72

3
@ ja72 Читач коду :)
tomsmeding

8
Усі знають, що Пау - правильний . : P
Джастін Крейча

Відповіді:


57

JavaScript

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

Допоможіть, я потрапив у пастку на фабриці Всесвіту , і я не впевнений, що роблю. Math.atan2повинен повернути пі з хорошими значеннями, правда? Math.atan2(0, -0)повертає pi, тож якщо я віднімаю його і додаю, я все одно повинен мати pi.


14
Я думаю, що я просто піду лягати і плакати. Прокляття, JavaScript.
Джек М

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
Ізката

5
@JackM, це твердження завжди доречно сказати :) Хоча в цьому випадку це пов'язано зі стандартом IEEE, і багато мов (не лише JS) мають проблему нуль проти негативного нуля.
Пол Дрейпер

40

ОСНОВНІ

(Більш конкретно, Chipmunk Basic )

Для цього використовується нескінченна серія, виявлена ​​Нілаканта Сомаяджі в 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 задається значенням π при запуску програми.

і

У BASIC знак рівності використовується як для призначення змінних, так і для тестування рівності. Тож a = b = c трактується як a = (b == c) .


Зачекайте, я цього не отримаю, так що iдорівнює false? А потім ви додаєте 2до цього? І це працює ???
Данно

2
@Dunno: Звичайно, петлі, з яких починається i == false, схожі на i == 0. Справа в тому, що початкове значення для акумулятора piне дорівнює 0…
Бергі

1
@Bergi Так, я просто не можу обернути голову навколо того факту, що false + 2 == 2: D
Данно

@Dunno Dynamic typeping тощо: false неявно перетворюється на 0 при виконанні арифметики. Ви також маєте таку ж очевидну поведінку в C, у якої відсутній boolтип, і він використовує 0та не-нульовий для представлення falseта trueреплікації. Не те, щоб це елегантно, але ей, ось так це працює.
Сюзанна Дюперон

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також поміняються між визначенням та дзвінком (без ефекту)
Бен Войгт

@BenVoigt Shhh! Не псуйте хитрість, але так. sqrtбуло випадково пропущено, так що значення pi було надруковано як 6,28 ... Також +1 для помічення, xа yчого я не зробив!
м2

1
о, тепер я бачу, що ви відстежуєте не одиничне коло, а одне з радіусом 2. Так, це чудово працює.
Бен Войгт

7
Я мушу зізнатися, що перш ніж зрозуміти, як це працює, я витратив пару хвилин, не ігноруючи цю лінію ...
loreb

10

С

(Це закінчилося довше, ніж було заплановано, але я все одно опублікую його ...)

У 17 столітті Уолліс опублікував нескінченну серію для Pi:

введіть тут опис зображення

(Докладніші відомості див. У розділі Нові продукти безмежних типів Wallis та Catalan для π, 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 - серія Нілаканта

Серія "Нілаканта" подається як:

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 ++: Мадхава з Сангагамаграми

Цей нескінченний ряд тепер відомий як Мадхава-Лейбніц :

Серія

Почніть з квадратного кореня з 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

Пітон - альтернатива серії Нілаканта

Це ще один нескінченний ряд для обчислення пі, який досить легко зрозуміти.

введіть тут опис зображення

Для цієї формули візьміть 6 і почніть чергувати додавання і віднімання дробів з чисельниками 2 і знаменниками, які є добутком двох послідовних цілих чисел, та їх сумою. Кожен наступний дріб починає свій набір цілих чисел, що збільшується на 1. Виконайте це навіть кілька разів, і результати наблизяться до пі.

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.