5 secondi per trovare la torta


11

Pi volte e (o Torta se ti piace la notazione ambigua) con 100 cifre decimali è:

8.5397342226735670654635508695465744950348885357651149618796011301792286111573308075725638697104739439...

( OIES A019609 ) ( argomento per possibile irrazionalità )

Il tuo compito è quello di scrivere un programma che accetta un numero intero positivo N e restituisce Pi * e troncato con N decimali. ad es. se N = 2, allora l'uscita dovrebbe essere 8.53.

Questo è un problema di ottimizzazione, quindi vincerà l'invio che può dare l'output corretto per il valore più alto di N.

Per garantire che tutti gli invii vengano giudicati utilizzando la stessa potenza di calcolo, il codice deve essere eseguito su ideone , utilizzando qualsiasi linguaggio supportato. Secondo l' ideone faq , esiste un limite di tempo di esecuzione di 5 secondi per gli utenti non connessi. Questo limite di 5 secondi è quello che devi usare, non il limite di 15 secondi per gli utenti che hanno effettuato l'accesso. (Vedi faq per altri limiti come memoria, dimensioni del codice, ecc.)

In particolare, chiunque non abbia effettuato l'accesso a ideone dovrebbe essere in grado di eseguire il programma su ideone per tutti i valori di N da 1 a un massimo di Nmax e vedere l'output corretto quasi sempre . senza errori Time limit exceededo Memory limit exceeded, ecc. Vince l'invio con la più grande Nmax.

(Indipendentemente dal fatto che il tempo effettivo impiegato sia un fumo nell'arco di 5 secondi, non importa se ideone non dà errori. " Quasi tutto il tempo " è definito come più del 95% del tempo per ogni particolare N.)

Dettagli

  • Puoi usare qualsiasi metodo matematico che ti piace per calcolare Pi * e, ma potresti non codificare l'output oltre la prima dozzina di cifre di Pi, e o Pi * e .
    • Il tuo programma dovrebbe essere in grado di funzionare per qualsiasi N, date risorse illimitate.
    • Puoi usare le costanti Pi o e incorporate se la tua lingua sembra averle.
  • Non puoi accedere a siti Web o risorse esterne al tuo codice (se ideone lo consente anche).
  • Oltre all'hardcoding e all'accesso a risorse esterne, tutto ciò che ideone consente va quasi sicuramente bene.
  • L'input e l'output devono (ovviamente) funzionare con qualunque ideone fornisca l'I / O (solo stdin / stdout). Va bene se hai bisogno di virgolette attorno all'ingresso N o se l'output è qualcosa di simile ans = ..., ecc.
  • Includi un link a uno snippet ideone del tuo codice con la tua Nmax come input.
  • Se si verifica un pareggio (probabilmente solo se più invii raggiungono il limite di caratteri di output di 64 KB), la risposta con il punteggio più alto vince.

3
Mmm ... torta ambigua.
Dennis,

Questo può essere molto facilmente un codice-golf e preferirebbe essere più divertente.
Ottimizzatore,

2
@Optimizer Potrebbe essere un codice-golf, ma sarebbe praticamente come ogni altro codice-generazione di cifre. Volevo provare un concorso a tempo che poteva essere verificato online. (Anche se un problema più complesso dal punto di vista computazionale avrebbe potuto essere migliore.)
Calvin's Hobbies

se questo è il codice golf APL probabilmente vincerebbe (meno la parte di precisione arbitraria)
TwiNight

1
Ho il sospetto che questi programmi saranno interamente legati all'IO cercando di scrivere le cifre su stdout. Cinque secondi sono molto tempo per qualcosa come Y-Cruncher .
Sarà il

Risposte:


12

Python - 65535

http://ideone.com/knKRhn

from math import exp, log

def divnr(p, q):
  """
    Integer division p/q using Newton-Raphson Division.
    Assumes p > q > 0.
  """

  sp = p.bit_length()-1
  sq = q.bit_length()-1
  sr = sp - sq + 1

  s = []
  t = sr
  while t > 15:
    s = [t] + s
    t = (t>>1) + 1
  # Base-case division
  r = (1 << (t<<1)) / (q >> sq-t)

  for u in s:
    r = (r << u-t+1) - (r*r * (q >> sq-u) >> (t<<1))
    t = u
  return (r * (p >> sq)) >> sr

def pibs(a, b):
  if a == b:
    if a == 0:
      return (1, 1, 1123)
    p = a*(a*(32*a-48)+22)-3
    q = a*a*a*24893568
    t = 21460*a+1123
    return (p, -q, p*t)
  m = (a+b) >> 1
  p1, q1, t1 = pibs(a, m)
  p2, q2, t2 = pibs(m+1, b)
  return (p1*p2, q1*q2, q2*t1 + p1*t2)

def ebs(a, b):
  if a == b:
    if a == 0:
      return (1, 1)
    return (1, a)
  m = (a+b) >> 1
  p1, q1 = ebs(a, m)
  p2, q2 = ebs(m+1, b)
  return (p1*q2+p2, q1*q2)

if __name__ == '__main__':
  n = input()

  pi_terms = int(n*0.16975227728583067)

  # 10^n == e^p
  p = n*2.3025850929940457

  # Lambert W_0(p/e) a la Newton
  k = log(p) - 1
  w = k - (k-1)/(k+1)
  while k > w:
    k = w
    w -= (k - p*exp(-k-1))/(k+1)

  # InverseGamma(e^p) approximation
  e_terms = int(p / w)

  pp, pq, pt = pibs(0, pi_terms)
  ep, eq = ebs(0, e_terms)

  z = 10**n
  p = 3528*z*ep*abs(pq)
  q = eq*abs(pt)

  pie = divnr(p, q)
  print pie,

Sembra che Ideone non sia stato gmpy2installato, il che è un peccato per almeno due motivi. Uno, perché renderebbe il calcolo molto più veloce, e due, perché rende impraticabile qualsiasi formula che richieda una radice quadrata di precisione arbitraria.

La formula che uso per π è stata elencata da Ramanujan come Formula (39):

che converge al ritmo di ~ 5,89 cifre per termine. Per quanto ne sappia, questa è la serie a più rapida convergenza nel suo genere che non richiede la valutazione di una radice quadrata di precisione arbitraria. La formula (44) nello stesso articolo (tasso di convergenza ~ 7,98 cifre per termine) viene spesso definita la formula di Ramanujan.

La formula che uso per e è la somma dei fattoriali inversi. Il numero di termini richiesti è calcolato come Γ -1 ( 10 n ), usando un'approssimazione che ho trovato su mathoverflow . Il componente Lambert W 0 viene trovato usando il metodo di Newton.

Il calcolo di ciascuna di queste sommazioni viene effettuato tramite la funzione di valutazione rapida elettronica (più comunemente nota come divisione binaria), originariamente ideata da Karatsuba. Il metodo riduce una somma di n termini a un singolo valore razionale p / q . Questi due valori vengono quindi moltiplicati per produrre il risultato finale.

Aggiornamento: la
profilazione ha rivelato che oltre la metà del tempo necessario per il calcolo è stata impiegata nella divisione finale. Per ottenere la massima precisione sono necessari solo i bit più alti del registro 2 (10 n ) di q , quindi ne taglio alcuni in anticipo. Il codice ora riempie il buffer di output di Ideone in 3.33s .

Aggiornamento 2:
Poiché si tratta di una sfida di , ho deciso di scrivere la mia routine di divisione per combattere la lentezza di CPython. L'implementazione di cui divnrsopra utilizza la divisione Newton-Raphson . L'idea generale è di calcolare d = 1 / q · 2 n usando il metodo di Newton, dove n è il numero di bit richiesti dal risultato e calcolare il risultato come p · d >> n . Il tempo di esecuzione è ora di 2,87 secondi - e questo senza nemmeno tagliare i bit prima del calcolo; non è necessario per questo metodo.


4

PARI / GP: 33000

Questo è fondamentalmente il programma fornito da OEIS , modificato per accettare e formattare correttamente l'output. Dovrebbe servire come base da battere, se non altro.

Mi assumo questo è accurato. L'ho controllato a 100 e 20k contro OEIS, ed è stato abbinato per entrambi. È abbastanza difficile trovare ulteriori cifre online per verificare.

Per 33.000 sono necessari circa 4,5 secondi, quindi probabilmente potrebbe essere urtato un po '. Mi sono solo stancato di armeggiare con l'input e il ciclo slow submit / compil / run di ideone.

{ 
    m=eval(input());
    default(realprecision, m+80); 
    x=Pi*exp(1);
    s="8.";
    d=floor(x);
    x=(x-d)*10;
    for (n=1, m, d=floor(x); 
         x=(x-d)*10; 
         s=Str(s,d));
    print(s);
}

Link Ideone.com


Le tue cifre corrispondono alle mie, quindi vado su un arto e dirò che probabilmente sono corrette.
primo

Questo programma trascorre essenzialmente tutto il suo tempo nel ciclo, generando cifre una per una. Se lo prendi Str(floor(frac(x)*10^m)va centinaia / migliaia di volte più veloce.
Charles,

2

Python 3

Dato che i pi integrati ee non hanno abbastanza cifre, ho deciso di calcolare il mio.

import decimal
import math
decimal.getcontext().prec=1000000
decimal=decimal.Decimal;b=2500
print(str(sum([decimal(1/math.factorial(x)) for x in range(b)])*sum([decimal(1/16**i*(4/(8*i+1)-2/(8*i+4)-1/(8*i+5)-1/(8*i+6))) for i in range(b)]))[0:int(input())+2])

IDEOne link

Uscita per STDIN = 1000:

8.5397342226735669504281233688422467204743749305568824722710929852470173635361001388261308713809518841081669216573834376992066672804294594807026609638293539437286935503772101801433821053915371716284188665787967232464763808892618434263301810056154560438283877633957941572944822034479453916753507796910068912594560500836608215235605783723340714760960119319145912948480279651779184356994356172418603464628747082162475871780202868607325544781551065680583616058471475977367814338295574582450942453416002008665325253385672668994300796223139976640645190237481531851902147391807396201201799703915343423499008135819239684881566321559967077443367982975103648727755579256820566722752546407521965713336095320920822985129589997143740696972018563360331663471959214120971348584257396673542429063767170337770469161945592685537660073097456725716654388703941509676413429681372333615691533682226329180996924321063261666235129175134250645330301407536588271020457172050227357541822742441070313522061438812060477519238440079

Nmax è il valore di input più grande che puoi dare al tuo programma prima che ideone non lo esegua più.
Calvin's Hobbies,

1
@Calvin'sHobbies Penso che nmax sia arbitrariamente grande anche se ...
Decadimento beta

1
ideone non ti dà una potenza di calcolo infinita. Qual è il valore di input più grande che il tuo programma può eseguire su ideone? (Anche se in realtà il tuo programma non segue la should be able to work for any N, given unlimited resourcesregola. La maggior parte dell'output è zero intorno a N = 10000.)
Calvin's Hobbies

Non è python3: NameError: name 'xrange' not defined.
Bakuriu,

2

Scala - 1790

IDEOne su http://ideone.com/A2CIto .

Usiamo la formula di Wetherfield per π (e il codice della formula di Machin trasferito rozzamente da qui ). Calcoliamo e utilizzando le serie di potenze ordinarie.

object Main extends App {
  import java.math.{BigDecimal => JDecimal}
  import java.math.RoundingMode._
  import scala.concurrent.Future
  import scala.concurrent.Await
  import scala.concurrent.ExecutionContext.Implicits._
  import scala.concurrent.duration._
  val precision = 1800

  def acotPrecision(numDigits: Int)(x: BigDecimal) = {
    val x1 = x.underlying
    val two = JDecimal.valueOf(2)
    val xSquared = x1 pow 2
    val unity = JDecimal.ONE.setScale(numDigits, HALF_EVEN)
    var sum = unity.divide(x1, HALF_EVEN)
    var xpower = new JDecimal(sum.toString)
    var term = unity

    var add = false

    var n = JDecimal.valueOf(3).setScale(numDigits)
    while (term.setScale(numDigits, HALF_EVEN).compareTo(JDecimal.ZERO) != 0) {
      xpower = xpower.divide(xSquared, HALF_EVEN)
      term = xpower.divide(n, HALF_EVEN)
      sum = if (add) sum add term else sum subtract term
      add = !add
      n = n add two
    }
    sum
  }

  def ePrecision(numDigits: Int) = {
    val zero = JDecimal.ZERO
    var sum = zero
    var term = JDecimal.ONE.setScale(numDigits, HALF_EVEN)
    var n = JDecimal.ONE.setScale(numDigits, HALF_EVEN)
    while(term.setScale(numDigits, HALF_EVEN).compareTo(zero) != 0) {
      sum = sum add term
      term = term.divide(n, HALF_EVEN)
      n = n add JDecimal.ONE
    }
    sum
  }

  val acot = acotPrecision(precision) _

  def d(x: Int) = JDecimal.valueOf(x)

  def piFuture = Future(d(4) multiply (
    (d(83) multiply acot(107)) add (d(17) multiply acot(1710)) subtract (d(22) multiply acot(103697))
    subtract (d(24) multiply acot(2513489)) subtract (d(44) multiply acot(18280007883L))
   add (d(12) multiply acot(7939642926390344818L))
   add (d(22) multiply acot(BigDecimal("3054211727257704725384731479018")))
  ))

  def eFuture = Future(ePrecision(precision))

  Await.result(
    for (pi <- piFuture;
         e <- eFuture) yield println((pi multiply e).setScale(precision - 10, DOWN))
  , 5 seconds) 
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.