Calcule pi a 5 decimales


15

Esto viene de http://programmers.blogoverflow.com/2012/08/20-controversial-programming-opinions/

"Dado que Pi se puede estimar usando la función 4 * (1 - 1/3 + 1/5 - 1/7 + ...) con más términos que dan mayor precisión, escriba una función que calcule Pi con una precisión de 5 decimales. "

  • Tenga en cuenta que la estimación debe hacerse calculando la secuencia dada anteriormente.

8
Probablemente deberías agregar algunas reglas más, de lo contrario obtendrás respuestas como (python)p=lambda:3.14159
Matt

1
¿Has visto codegolf.stackexchange.com/questions/506/… , que es muy similar? Como mínimo, las funciones trigonométricas deben prohibirse para este problema porque permiten soluciones triviales como este programa QBASIC:? INT (4E5 * ATN (1)) / 1E5
favor

Creo que debería requerir que el algoritmo sea de aproximación sucesiva: cuanto más tiempo calcule, más se acercará a pi.
DavidC

@DavidCarraher, aunque eso es matemáticamente inevitable al usar esta serie, desde un punto de vista analítico numérico es muy dudoso. Una serie alterna que converge lentamente es un cartel para la pérdida de importancia.
Peter Taylor

2
Dupe, pero es tan viejo que no está aquí: stackoverflow.com/q/407518/12274
JB

Respuestas:


10

JavaScript, 46 58 56 45 bytes

Actualización de ES6 : Resulta que hay más funciones disponibles para nosotros ahora que han pasado cinco años.

let f=(i=0,a=0)=>i>1e6?a:f(i+4,a+8/-~i/(i+3))

Esta versión ( 45 bytes; sí, letse requiere) funciona en modo estricto ES6 en teoría . En la práctica, puede ejecutarlo en V8 (por ejemplo, con nodo) con --use-strict --harmony-tailcalls; La función de llamadas de cola adecuadas aún no está ampliamente implementada, por desgracia. Sin embargo, es un comportamiento específico, por lo que debería estar bien.

Si queremos apegarnos a lo que está ampliamente implementado, y no requiere el modo estricto, simplemente podemos usar la sintaxis de flecha gruesa ES6 para las funciones, pero de lo contrario conservamos la misma implementación que antes (sugerido por Brian H) por un costo de 48 bytes.

a=>{for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}

La elección del nombre para el parámetro único realmente no importa, pero podríamos elegir uno de los nombres que usamos para minimizar la contaminación de alcance global.


function(){for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}

Esta versión es una expresión de función; agregue dos caracteres (por ejemplo, " f") si desea que se nombre. Esta versión golpea a los globales ay i; esto podría evitarse si agregamos " a,i" a la lista de parámetros.

Utiliza una versión reformulada del algoritmo para evitar la necesidad de restar.

 1/1 - 1/3  +   1/5 - 1/7   +    1/9 - 1/11  + ...
(3/3 - 1/3) + (7/35 - 5/35) + (11/99 - 9/99) + ...
    2/3     +      2/35     +       2/99     + ...
  2/(1*3)   +    2/(5*7)    +     2/(9*11)   + ...

Aquí hay una versión "simple" sin este ajuste:

function(){for(a=0,i=1;i<1e6;i+=2)a+=[,4,,-4][i%4]/i;return a}

que registra 64 62 caracteres.

Gracias a @ardnew por la sugerencia de deshacerse de 4*antes return.


Historia

function(){for(a=i=0;i<1e6;a+=8/++i/~-(i+=3));return a}     // got rid of `i+=4`; restructured
// Old versions below.
function(){for(a=0,i=1;i<1e6;i+=4)a+=8/i/-~-~i;return a}    // got rid of `4*`
function(){for(a=0,i=1;i<1e6;i+=4)a+=2/i/-~-~i;return 4*a}

oO muy buen trabajo, factorizando la resta.
acólito

1
gran trabajo, pero necesita ser escrito como una función adecuada
nuevo

@ardnew: Gracias, debo haber perdido ese detalle cuando leí la descripción del problema. Lo actualicé y ahora es una expresión de función invocable (lambda); no estoy seguro si esto está permitido o si se le debe dar un nombre. Si ese es el caso, son solo dos caracteres adicionales de todos modos.
FireFly

1
@FireFly también puedes eliminar 2 caracteres cambiando a+=2/i/-~-~i;return 4*aaa+=8/i/-~-~i;return a
ardnew

@ardnew: oh, genial; No pensé en eso. : D
FireFly

8

Python 59 bytes

print reduce(lambda x,p:p/2*x/p+2*10**999,range(6637,1,-2))

Esto imprime 1000 dígitos; un poco más de lo requerido 5. En lugar de usar la iteración prescrita, usa esto:

pi = 2 + 1/3*(2 + 2/5*(2 + 3/7*(2 + 4/9*(2 + 5/11*(2 + ...)))))

los 6637 (el denominador más interno) se puede formular como:

dígitos * 2 * log 2 (10)

Esto implica una convergencia lineal. Cada iteración más profunda producirá un bit binario más de pi .

Si , sin embargo, insiste en usar el bronceado -1 identidad, una convergencia similar puede conseguirse, si no te importa ir sobre el problema de forma ligeramente diferente. Echando un vistazo a las sumas parciales:

4.0, 2.66667, 3.46667, 2.89524, 3.33968, 2.97605, 3.28374, ...

es evidente que cada término salta de un lado a otro a cada lado del punto de convergencia; La serie tiene convergencia alterna. Además, cada término está más cerca del punto de convergencia que el término anterior; Es absolutamente monótono con respecto a su punto de convergencia. La combinación de estas dos propiedades implica que la media aritmética de cualquiera de los dos términos vecinos está más cerca del punto de convergencia que cualquiera de los términos mismos. Para darle una mejor idea de lo que quiero decir, considere la siguiente imagen:

Sumas parciales

La serie externa es la original, y la serie interna se encuentra tomando el promedio de cada uno de los términos vecinos. Una notable diferencia. Pero lo que es verdaderamente notable es que esta nueva serie también tiene convergencia alterna y es absolutamente monótona con respecto a su punto de convergencia. Eso significa que este proceso se puede aplicar una y otra vez, hasta la saciedad.

Okay. ¿Pero cómo?

Algunas definiciones formales. Deje P 1 (n) ser el n º término de la primera secuencia, P 2 (n) ser el n º término de la segunda secuencia, y de manera similar P k (n) el n º plazo de la k ésimo secuencia como se define anteriormente .

P 1 = [P 1 (1), P 1 (2), P 1 (3), P 1 (4), P 1 (5), ...]

P 2 = [(P 1 (1) + P 1 (2)) / 2, (P 1 (2) + P 1 (3)) / 2, (P 1 (3) + P 1 (4)) / 2, (P 1 (4) + P 1 (5)) / 2, ...]

P 3 = [(P 1 (1) + 2P 1 (2) + P 1 (3)) / 4, (P 1 (2) + 2P 1 (3) + P 1 (4)) / 4, (P 1 (3) + 2P 1 (4) + P 1 (5)) / 4, ...]

P 4 = [(P 1 (1) + 3P 1 (2) + 3P 1 (3) + P 1 (4)) / 8, (P 1 (2) + 3P 1 (3) + 3P 1 (4) + P 1 (5)) / 8, ...]

No es sorprendente que estos coeficientes sigan exactamente los coeficientes binomiales, y pueden expresarse como una sola fila del Triángulo de Pascal. Desde una fila arbitraria de triángulo de Pascal es trivial calcular, una arbitrariamente 'profundo' serie se pueden encontrar, simplemente tomando los primeros n parciales sumas, multiplicar cada por el término correspondiente en el k ésimo fila del triángulo de Pascal, y dividiendo por 2 k-1 .

De esta manera, se puede lograr una precisión total de coma flotante de 32 bits (~ 14 lugares decimales) con solo 36 iteraciones, en cuyo punto las sumas parciales ni siquiera han convergido en el segundo lugar decimal. Esto obviamente no es golf:

# used for pascal's triangle
t = 36; v = 1.0/(1<<t-1); e = 1
# used for the partial sums of pi
p = 4; d = 3; s = -4.0

x = 0
while t:
  t -= 1
  p += s/d; d += 2; s *= -1
  x += p*v
  v = v*t/e; e += 1

print "%.14f"%x

Si quería precisión arbitraria, esto se puede lograr con una pequeña modificación. Aquí una vez más calculando 1000 dígitos:

# used for pascal's triangle
f = t = 3318; v = 1; e = 1
# used for the partial sums of pi
p = 4096*10**999; d = 3; s = -p

x = 0
while t:
  t -= 1
  p += s/d; d += 2; s *= -1
  x += p*v
  v = v*t/e; e += 1

print x>>f+9

El valor inicial de p comienza 2 10 más grande, para contrarrestar los efectos de división entera de s / d a medida que d se hace más grande, haciendo que los últimos dígitos no converjan. Note aquí nuevamente que3318 también es:

dígitos * log 2 (10)

El mismo número de iteraciones que el primer algoritmo (reducido a la mitad porque t disminuye en 1 en lugar de 2 en cada iteración). Una vez más, esto indica una convergencia lineal: un bit binario de pi por iteración. En ambos casos, se requieren 3318 iteraciones para calcular 1000 dígitos de pi , como una cuota ligeramente mejor que 1 millón de iteraciones para calcular 5.


Eso es mucho mejor que mi solución:4 * sum(1/(1+i*2) if not i%2 else -1/(1+i*2) for i in xrange(places*10**(places)))
Aaron Hall

1
Esto es muy similar a mi enfoque , que resulta ser una forma diferente de la suya. En la mía, como k → ∞, se f(-1,k)acerca a su suma Euler.
Simplemente hermoso arte

1
Muy genial; Increíble análisis y explicación, gracias.
jeremy radcliff

Solo una pequeña cosa. ¿No quiso decir después de P_1 = ..., P_2 = ..., P_3 = ..., P_4 = ..., "... multiplique cada uno por el término correspondiente en la kthfila del Triángulo de Pascal, y dividiendo por 2^{k-1}.", En lugar de la nthfila y 2^{n-1}?.
jeremy radcliff

@jeremyradcliff lo hice, sí. Gracias por la corrección.
primo

5

Mathematica 42 39 34 33 31 26 32

Enfoque de Arquímedes 26 caracteres

N@#*Sin[180 Degree/#]&

Esto alcanza el criterio cuando la entrada es 822.

Pregunta: ¿Alguien sabe cómo calculó el pecado de 180 grados? Yo no.


Enfoque de Leibniz (serie de Gregory) 32 caracteres

Esta es la misma función que el presentador del problema le dio como ejemplo. Alcanza el criterio en aproximadamente medio millón de iteraciones.

N@4Sum[(-1)^k/(2k+1),{k,0,10^6}]

Enfoque Madhava-Leibniz 37 caracteres

¡Esta variación utiliza algunos caracteres más pero converge al criterio en solo 9 iteraciones!

N@Sqrt@12 Sum[(-1/3)^k/(2k+1),{k,0,9}]

¿todos lo calculan mediante el algoritmo dado en la definición del problema?
acólito

El enfoque de @acolyte Leibniz (ahora el primero en la lista) es el mencionado en la descripción del problema. Es muy lento para converger. Una ligera variación (Madhava-Leibniz) converge muy rápidamente.
DavidC

El seno de 180 ° es bastante fácil. Es 180 ° / N que puede ser complicado fuera de los sospechosos habituales para N.
JB

Por favor explique, @JB ¿Difícil de medir?
DavidC

Esta entrada debe indicar "32" porque solo el enfoque de Leibniz cumple los requisitos (contando los caracteres en el código como se indica, obtengo 34, pero ambos espacios pueden eliminarse de forma segura, lo que da una longitud de 32).
celtschk


4

Java (67 caracteres)

float r(){float p=0,s=4,i=1E6f;while(--i>0)p+=(s=-s)/i--;return p;}

Tenga en cuenta que esto evita la pérdida de importancia al sumar los números en el orden correcto.


este también es un código C totalmente compatible. si se publica como C, se puede cambiar while(--i>0)a while(i--)y guardar 2 caracteres
ardnew

1
@ardnew, cierto, pero con C hay trucos mucho más interesantes para jugar ...
Peter Taylor

4

Haskell, 32

foldr(\k->(4/(2*k+1)-))0[0..8^7]

GHCi> foldr (\ k -> (4 / (2 * k + 1) -)) 0 [0..8 ^ 7]
3.141593130426724

Contando un nombre de función es

34

π=foldr(\k->(4/(2*k+1)-))0[0..8^7]


3

C (GCC) (44 caracteres)

float p(i){return i<1E6?4./++i-p(++i):0;}

Eso es 41 caracteres, pero también debe compilarse -O2para que el optimizador elimine la recursividad de la cola. Esto también se basa en un comportamiento indefinido con respecto al orden en que ++se ejecutan; Gracias a Ugoren por señalar esto. He probado con gcc 4.4.3 en Linux de 64 bits.

Tenga en cuenta que a menos que el optimizador también reordene la suma, agregará desde el número más pequeño, por lo que evita la pérdida de importancia.

Llamar como p().


Tu llamada recursiva es q(), no p(). Y no creo -O2que deba contarse (pero si lo cuenta, son 4 caracteres debido al espacio requerido).
ugoren

Además: 1. gcc 4.1.1 no optimiza la recursividad (y no veo cómo podría), por lo que la pila se desborda. 2. debería llamarse como p(0). 3. Guardar un char por return++i.... 4. Dos ++ihace un comportamiento indefinido.
ugoren

@ugoren, gracias por tus comentarios. En orden: qeso me enseñará a verificar dos veces después de cambiar el nombre. Creo que sigo la práctica normal de contar -O2como 3 caracteres, pero podemos abrirlo en meta si lo desea; meta.codegolf.stackexchange.com/questions/19 es la única discusión relevante que puedo encontrar. He agregado la versión de gcc que estoy usando, y que me permite llamarla como p(). Guardar el carácter detiene el optimizador y da segfault. Aclararé que estoy usando un comportamiento indefinido, según meta.codegolf.stackexchange.com/questions/21
Peter Taylor

Agregué una respuesta a la meta pregunta sobre banderas. Acerca de p(): ¿está seguro de que llamar p()desde cualquier contexto funcionaría? ¿O es solo lo que sucedió en la pila en su prueba?
ugoren

@ugoren, tal vez tuve suerte constantemente. Incluso si lo llamo dos veces seguidas, el segundo aún devuelve el valor correcto. gcc parece producir un código ligeramente diferente para p()vs p(0), pero no sé qué comportamiento documenta y no soy realmente un programador en C.
Peter Taylor

3

J, 26 caracteres

+ / + / _ 2 ((4 _4) &%)>: +: i.100

Se movió de 100 elementos de secuencia a 1e6 elementos. Además, ahora es un código etiquetado y podría copiarse desde el navegador a la consola sin errores.

+/+/_2((4 _4)&%)\>:+:i.1e6

3
-/4%>:2*i.1e6- 13 caracteres. (Gracias a b_jonas en #jsoftware por darme cuenta de que -/funciona para calcular una suma con signo alternativo. [Esto se debe a que todos los operadores en J tienen la misma precedencia y asociativos a la derecha, entonces -/ 1 2 3 4<=> 1 - (2 - (3 - 4))<=> 1 - 2 + 3 - 4.])
FireFly

eso es ordenado y el doble de asombroso. ¡O incluso 2 ^ 10 más increíbles!
fftw

@FireFly eso es hermoso
Jonah

2

Javascript - 33 caracteres

p=x=>4*(1-(x&2))/x+(x>1?p(x-2):0)

Llame ppasando un número impar positivo xy calculará Pi con (x-1)/2términos.


2

Ruby - 82 caracteres

def f(n,k=n)k>0?(f(n,k-1)+f(n+1,k-1))/2:n<0?0:f(n-1,0)+(-1)**n/(2*n+1.0)end;4*f(9)

Intentalo : https://repl.it/LQ8w

El enfoque usa la serie dada indirectamente usando un enfoque de aceleración numérica. La salida resultante es

pi ≈ 3.14159265161

vs.

pi = 3.14159265359

Empieza con

f(n,0) = 1/1 - 1/3 + 1/5 - ... + ((-1)**n)/(2*n+1)

Y luego, dado que esto se alterna, podemos acelerar la convergencia usando

f(n,1) = (f(n,0) + f(n+1,0))/2

Y aplica esto repetidamente:

f(n,k) = (f(n,k-1) + f(n+1,k-1))/2

Y por simplicidad, f(n) = f(n,n) .


Ruby - 50 caracteres

Si no te importa correr durante mucho tiempo, entonces simplemente puedes usar

def f(n)n<0?0:f(n-1)+(-1)**n/(2*n+1.0)end;4*f(1e7)

o

a=0;for k in 0..1e7 do a+=(-1)**k/(2*k+1.0)end;4*a

1

C, 69 caracteres

float p,b;void main(a){b++<9e6?p+=a/b++,main(-a):printf("%f\n",4*p);}
  • Ejecutar sin parámetros de línea de comando (así a se inicializa a 1).
  • Debe compilarse con optimización.
  • void maines extraño y no estándar, pero hace que las cosas funcionen. Sin ella, la recursión se implementa como una llamada real, lo que lleva a un desbordamiento de pila. Una alternativa es agregar return.
  • Se 4*pueden guardar dos caracteres , si se ejecuta con tres parámetros de línea de comando.

Podría acortar eso int main(a)o incluso main(a), GCC solo da una advertencia. Y dará una advertencia de void maintodos modos, y tal vez incluso porque solo tiene un argumento para hacerlo main.
nyuszika7h

1

Clojure - 79 caracteres

(fn [](* 4(apply +(map #(*(Math/pow -1 %1)(/ 1.0(+ 1 %1 %1)))(range 377000)))))

Esto crea una función sin argumentos que calculará un flotante que se aproxima a pi correctamente a cinco decimales. Tenga en cuenta que esto no vincula la función a un nombre como pi, por lo tanto, este código debe evaluarse en su lugar con o evalcomo (<code>)un nombre, en cuyo caso la solución es

(defn p[](* 4(apply +(map #(*(Math/pow -1 %1)(/ 1.0(+ 1 %1 %1)))(range 377000)))))

por 82 caracteres

Acerca de

(defn nth-term-of-pi [n] (* (Math/pow -1 n) (/ 1.0 (+ 1 n n))))
(defn pi [c] (* 4 (apply + (map nth-term-of-pi (range c)))))
(def  pi-accuracy-constant (loop [c 1000] (if (< (pi c) 3.14159) (recur (inc c)) c)))
; (pi pi-accuracy-constant) is then the value of pi to the accuracy of five decimal places

1

PHP - 56 55 caracteres

<?for($j=$i=-1;1e6>$j;){$p+=($i=-$i)/($j+=2);}echo$p*4;

No sé si puedo hacerlo mucho más pequeño sin romper la regla del algoritmo.


1
¿Qué tal esto para 45? <?for(;1e6>$j;)$p+=($i=-$i|4)/~-$j+=2;echo$p;
primo

Estaba tratando de llegar a eso, pero no pude conseguir que las operaciones bit a bit funcionaran. ¡Gracias por la sugerencia!
TwoScoopsofPig

Puede eliminar el último punto y coma para guardar 1 carácter.
nyuszika7h

1

Perl - 43 39 caracteres

no estoy seguro de las reglas sobre subrutinas anónimas, pero aquí hay otra implementación que usa la construcción de series de @ FireFly

sub{$s+=8/((4*$_+2)**2-1)for 0..1e6;$s}

sub p{$s+=(-1)**$_*4/(2*$_+1)for 0..1e6;$s}


0

Java - 92 84 caracteres

No puedo superar con mucho el resultado de Peter Taylor, pero aquí está el mío:

double d(){float n=0,k=0,x;while(n<9E5){x=1/(1+2*n++);k+=(n%2==0)?-x:x;}return 4*k;}

Versión sin golf:

double d() {
    float n = 0, k = 0, x;
    while (n < 9E5) {
        x = 1 / (1 + 2 * n++);
        k += (n % 2 == 0) ? -x : x;
    }
    return 4 * k;
}

Editar: guardado algunos caracteres con el operador ternario.


0

Python - 56 caracteres

Meh, mi python-fu no es lo suficientemente fuerte. No pude ver más atajos, pero ¿quizás un golfista más experimentado podría encontrar algo para recortar aquí?

t=s=0
k=i=1
while t<1e6:t,s,i,k=t+1,k*4./i+s,i+2,-k

Puede usar Python 3 para guardar un byte para la división flotante ( 4.-> 4). En otras noticias, ¡acabo de encontrar un caso en el que Python 3 realmente supera a Python 2 en el código de golf!
nyuszika7h

0

Ruby - 54 caracteres

def a()p=0;1000000.times{|i|p+=8/(4*i*(4*i+2))};p;end;

Mi primer intento en la consola

def a()i=1;p=0;while i<2**100 do p+=8/(i*(i+2));i+=4;end;p;end;

63 caracteres.


Puede guardar un byte utilizando en def a;lugar de def a().
nyuszika7h

Otro al eliminar el último punto y coma.
nyuszika7h

0

Perl (76 caracteres)

$y=1e4;for$x(0..1e4-1){$y--while sqrt($x**2+$y**2)>1e4;$a+=$y}print 4*$a/1e8

(Resultado: 3.14159052)

No es la solución más corta posible, pero puede ser interesante. Es geométrico. Calculo el área debajo de un círculo.

Tengo otro enfoque divertido, pero es muy lento. Cuenta el número de puntos discretos en un cuadrado que están debajo de un cuarto de círculo y calcula pi a partir de él:

$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2

Espera el número de iteraciones como argumento de línea de comando. Aquí puede ver cómo el tiempo de ejecución se relaciona con la precisión. ;)

$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 100
3.1796
real    0m0.011s
user    0m0.005s
sys 0m0.003s

$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 1000
3.14552
real    0m0.354s
user    0m0.340s
sys 0m0.004s

$ time perl -e '$i=shift;for$x(0..$i){for$y(0..$i){$h++if sqrt($x**2+$y**2)<$i}}print$h*4/$i**2' 10000
3.14199016
real    0m34.941s
user    0m33.757s
sys 0m0.097s

0

k (25 caracteres)

4 * + /% (i # 1 -1) '1 + 2 ! I: 1000000

Ligeramente más corto:

+/(i#4 -4)%1+2*!i:1000000

0

Pitón (49)

print 4*sum((-1)**i/(2*i+1.)for i in range(9**6))
3.14159 453527



0

SQL, 253 bytes

DECLARE @B int=3, @A varchar(max), @C varchar(max)='1'
WHILE @B<100000
BEGIN
SELECT @C=@C+(select case when (@B-1)%4=0 then'+'else'-'end)+
(SELECT cast(cast(1.0/@B as decimal(9,8)) as varchar(max)))
SELECT @B=@B+2
END
EXECUTE('SELECT 4*('+@C+')')

Proporcionaría un Fiddle de SQL, pero esto va demasiados bucles de profundidad al encontrar las fracciones 1/3 1/5 1/7 etc. y da errores lol. Sin embargo, si cambia @B<100000a, 1000entonces se ejecuta (obviamente no con el mismo número de dígitos de precisión).


0

Befunge, 129 bytes

p08p109p^v*86%+55:<$$$<
\$\>:#,_@>+\55+/:#^_"."
v>p"~"/:"~"%08p"~"/00p:2\4%-*"(}"
8^90%"~":+2:+g90*+g80*<
>*:**\/+>"~~"00g:"~"`!|

Pruébalo en línea!

En caso de que alguien se pregunte, es un elefante.

Elefante de dibujos animados

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.