Pi a toujours tort [fermé]


27

Pi a tort

Une méthode courante de calcul de pi consiste à lancer des "fléchettes" dans une boîte 1x1 et à voir quel terrain dans le cercle unitaire par rapport au total lancé:

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

Écrivez un programme qui semble devoir calculer correctement pi (en utilisant cette méthode ou d'autres méthodes courantes de calcul de pi) mais calcule tau (tau = 2 * pi = 6.283185307179586 ...) à la place. Votre code doit produire au moins les 6 premières décimales: 6.283185

Le gagnant est couronné le 6 juin (une semaine à partir d'aujourd'hui).


43
Pourquoi le vainqueur n'est-il pas couronné le 28 juin ??
corsiKa

9
Je ne sais pas pourquoi un gagnant doit être couronné dans un concours de popularité.
Tim S.

1
Je ne comprends pas. Cela 1revient à demander une fonction qui semble retourner mais qui revient 2. Qui trompons-nous ici?
ja72

3
@ ja72 Le lecteur du code :)
tomsmeding

8
Tout le monde sait que pau est le bon . : P
Justin Krejcha

Réponses:


57

Javascript

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

Au secours, je suis pris au piège dans une fabrique d'univers et je ne sais pas trop ce que je fais. Math.atan2est censé retourner pi avec de bonnes valeurs, non? Math.atan2(0, -0)renvoie pi, donc si je le soustrais et l'ajoute, je devrais toujours avoir pi.


14
Je pense que je vais juste m'allonger et pleurer. Goddamnit, JavaScript.
Jack M

3
explication s'il vous plait? :)
Jaa-c

2
Angle dans le sens antihoraire en radians entre l'axe x et le point (Y, X). Le signe du point Y détermine s'il s'agit d'un angle positif ou négatif, et cela devientπ - (-π)

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

5
@JackM, cette déclaration est toujours appropriée pour dire :) Bien que dans ce cas, cela soit dû à la norme IEEE, et de nombreuses langues (pas seulement JS) ont le problème zéro vs zéro négatif.
Paul Draper

40

DE BASE

(Plus précisément, Chipmunk Basic )

Celui-ci utilise une série infinie découverte par Nilakantha Somayaji au XVe siècle:

' 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

Sortie

6.2831853072

Si vous ne pouvez pas comprendre ce qui se passe, voici quelques conseils:

Dans Chipmunk Basic, la variable pi est prédéfinie à la valeur de π lorsque le programme démarre.

et

En BASIC, le signe égal est utilisé à la fois pour affecter des variables et pour tester l'égalité. Donc a = b = c est interprété comme a = (b == c) .


Attendez, je ne comprends pas, alors c'est iégal false? Et puis vous y ajoutez 2? Et il fonctionne???
Dunno

2
@Dunno: Bien sûr, les boucles commencent à i == falsece qui est similaire à i == 0. Le fait est que la valeur initiale de l'accumulateur pin'est pas 0…
Bergi

1
@Bergi ouais je ne peux pas comprendre ce que false + 2 == 2: D
Dunno

@Dunno Typage dynamique, etc.: false est implicitement converti en 0 lors de l'exécution de l'arithmétique. Vous avez également le même comportement apparent en C qui n'a pas de booltype, et utilise 0et non nul pour représenter falseet respectivement true. Non pas que ce soit élégant, mais bon, c'est comme ça que ça marche.
Suzanne Dupéron

15

C - La longueur d'un demi-cercle d'unité

Une façon de calculer π est simplement de mesurer la distance au point se (1, 0)déplace lors de la rotation autour de l' origine à (-1, 0)puisqu'il sera la moitié de la circonférence d'un cercle unitaire (qui est ).

entrez la description de l'image ici

Cependant, pas sin(x)ou cos(x)est nécessaire , car cela peut être fait par l' intensification tout autour de l' origine et en ajoutant la distance au point se déplace pour chaque étape . La plus petite taille pour chaque étape la plus précise de π vous obtiendrez.

Remarque: le pas se termine lorsque y est inférieur à zéro (ce qui est juste au moment où il passe (-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;
}

Il donne la sortie suivante:

Value of pi is 6.283185

3
Semble légitime… Certainement.
bjb568

1
Votre lengthmacro manque un sqrt. Est-ce prévu? xet ysont également échangés entre la définition et l'appel (sans effet)
Ben Voigt

@BenVoigt Shhh! Ne gâchez pas l'astuce, mais oui. sqrta été accidentellement omis pour que la valeur de pi soit imprimée comme 6,28 ... Aussi +1 pour l'avoir remarqué xet yce que je n'ai pas fait!
Thism2

1
oh, maintenant je vois que vous tracez non pas un cercle d'unité, mais un rayon de 2. Ouaip, ça marche très bien.
Ben Voigt

7
Je dois avouer qu'avant de comprendre comment cela fonctionne, j'ai perdu quelques minutes en
n'ignorant

10

C

(Cela a fini par être plus long que prévu, mais je le publierai quand même ...)

Au 17ème siècle, Wallis publie une série infinie pour Pi:

entrez la description de l'image ici

(Voir Nouveaux produits infinis de type wallis et catalan pour π, e et √ (2 + √2) pour plus d'informations)

Maintenant, pour calculer Pi, nous devons d'abord multiplier par deux pour factoriser le dénominateur:

entrez la description de l'image ici

Ma solution calcule ensuite la série infinie pour Pi / 2 et deux, puis multiplie les deux valeurs ensemble. Notez que les produits infinis sont incroyablement lents à converger lors du calcul des valeurs finales.

sortie:

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

L'exposant dans la double conversion ne peut pas être ignoré. Si c'est le seul changement (laissez la division par 2, la multiplication par 4, la multiplication entière), tout fonctionne étonnamment.


8

Java - Nilakantha Series

La série Nilakantha est donnée comme:

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

Ainsi, pour chaque terme, le dénominateur est construit en multipliant des entiers consécutifs, le début augmentant de 2 chaque terme. Notez que vous ajoutez / soustrayez des termes alternatifs.

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

Après cinq cents termes, nous obtenons une estimation raisonnable de pi:

6.283185311179568

4

C ++: Madhava de Sangamagrama

Cette série infinie est maintenant connue sous le nom de Madhava-Leibniz :

Séries

Commencez par la racine carrée de 48 et multipliez-la par le résultat de la somme de (-3) -k / (2k + 1). Très simple et simple à mettre en œuvre:

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

Sortie:

my pi: 6.283185307179588

3

Python - Une alternative à la série Nilakantha

Il s'agit d'une autre série infinie pour calculer pi qui est assez facile à comprendre.

entrez la description de l'image ici

Pour cette formule, prenez 6 et commencez à alterner entre l'addition et la soustraction de fractions avec des numérateurs de 2 et des dénominateurs qui sont le produit de deux entiers consécutifs et leur somme. Chaque fraction suivante commence son ensemble d'entiers en augmentant de 1. Effectuez cela même quelques fois et les résultats se rapprochent assez de 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)

ce qui donne 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

Sortie: 6.28

#include "Math.h" n'est pas la même chose que #include, mais juste en regardant le fichier principal, presque personne ne penserait à vérifier. Évident peut-être, mais un problème similaire est apparu dans un projet sur lequel je travaillais et est resté longtemps non détecté.


Une solution intelligente néanmoins.
BobTheAwesome
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.