5 секунд, щоб знайти пиріг


11

Пі разів e (або Пиріг, якщо вам подобається неоднозначне позначення) до 100 знаків після коми:

8.5397342226735670654635508695465744950348885357651149618796011301792286111573308075725638697104739439...

( OIES A019609 ) ( аргумент щодо можливої ​​ірраціональності )

Ваше завдання - написати програму, яка займає додатне ціле число N, і виводить Pi * e, усічений до N десяткових знаків. наприклад, якщо N = 2, то вихід повинен бути 8.53.

Це проблема оптимізації, тому подання, яке може дати правильний вихід для найвищого значення N, виграє.

Щоб переконатися, що всі подання оцінюються за допомогою однакової обчислювальної потужності, ваш код повинен працювати на ideone , використовуючи будь-яку мову, яку вони підтримують. Відповідно до ideone faq , для користувачів, які не входили в систему, існує обмеження часу на 5 секунд. Ця 5-секундна межа є тією, яку ви повинні використовувати, а не обмеження 15 секунд для ввійшли користувачів. (Див. Faq для інших обмежень, таких як пам'ять, розмір коду тощо).

Зокрема, кожен, хто не ввійшов у ideone, повинен мати змогу запускати вашу програму на ideone для всіх значень N від 1 до деякого максимального Nmax і бачити правильний вихід майже весь час . без будь - яких Time limit exceededабо Memory limit exceeded, і т.д. помилки. Виграє подання з найбільшим Nmax.

(Незалежно від того, чи є фактичний час, який займає штрих, протягом 5 секунд не має значення, доки ideone не помиляється. " Майже весь час " визначається як більше 95% часу для будь-якої конкретної N.)

Деталі

  • Ви можете використовувати будь-який математичний метод, який вам подобається, щоб обчислити Pi * e, але ви можете не жорстко кодувати висновок за межі перших десятків цифр Pi, e або Pi * e .
    • Ваша програма повинна мати можливість працювати для будь-якого N, враховуючи необмежені ресурси.
    • Ви можете використовувати вбудовані константи Pi або e, якщо ваша мова має їх.
  • Ви не можете отримувати доступ до веб-сайтів або ресурсів, що не відповідають коду (якщо ideone це навіть дозволяє).
  • Крім жорсткого кодування та доступу до зовнішніх ресурсів, все, що дозволяє ideone, майже напевно добре.
  • Ваш вхід і вихід повинні (очевидно) працювати з тим, що ідеон передбачає введення / виведення (stdin / stdout тільки здається). Це добре, якщо вам потрібні лапки навколо вхідного сигналу N або вихід є щось на зразок ans = ...тощо.
  • Будь ласка, включіть посилання на фрагмент ідеону вашого коду разом із своїм Nmax як вхідним.
  • Якщо трапляється нічия (лише ймовірно, якщо декілька подань досягають межі символу вихідного коду 64 кБ), виграє найвищий голос.

3
Ммм ... неоднозначний пиріг.
Денніс

Це дуже легко може бути кодом-гольф і, скоріше, буде веселіше.
Оптимізатор

2
@Optimizer Це може бути кодовий гольф, але тоді він буде дуже схожий на будь-який інший кодовий генератор код-гольф. Я хотів спробувати змагання на основі часу, яке можна перевірити в Інтернеті. (Хоча більш складно обчислювальна проблема могла бути і кращою.)
Хобі Кальвіна

якщо це код гольфу APL, мабуть, виграє (мінус довільну точність частини)
TwiNight,

1
Я підозрюю, що ці програми будуть повністю пов'язані з IO, намагаючись виписати цифри в stdout. П’ять секунд - це довгий час для чогось на зразок y-cruncher .
Буде

Відповіді:


12

Пітон - 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,

Схоже, Ideone не gmpy2встановлено, що шкода принаймні з двох причин. Одне, тому що це зробило б обчислення набагато швидшим, і два, тому що робить будь-яку формулу, що вимагає довільної точності квадратного кореня, недоцільним.

Формула, яку я використовую для π, була вказана Рамануджаном як Формула (39):

який конвергується зі швидкістю ~ 5,89 цифр за термін. Наскільки мені відомо, це така швидка збіжна серія такого роду, яка не потребує оцінки довільного точного квадратного кореня. Формула (44) в тій же роботі (швидкість збіжності ~ 7.98 цифри в перспективу) найбільш часто згадуються як в Рамануджаном формулу.

Формула, яку я використовую для e, є сумою зворотних факторіалів. Кількість необхідних термінів обчислюється як Γ -1 ( 10 n ), використовуючи наближення, яке я знайшов у mathoverflow . Компонент Lambert W 0 знайдено за методом Ньютона.

Розрахунок кожного з цих підсумків проводиться за допомогою швидкого оцінювання E-функції (більш відомого як двійкове розщеплення), спочатку розробленого Карацубою. Метод зводить підсумовування до n доданків до одного раціонального значення p / q . Ці два значення потім множать для отримання кінцевого результату.

Оновлення:
Профілювання показало, що більше половини часу, необхідного для розрахунку, було витрачено в остаточному підрозділі. Для досягнення повної точності потрібні лише найвищі верхні 2 біти log (10 n ) q , тому я попередньо обрізаю кілька. Код тепер заповнює вихідний буфер Ideone за 3,33 секунди .

Оновлення 2:
Оскільки це завдання , я вирішив написати власний розпорядок поділу для боротьби з повільністю CPython. Реалізація divnrвище використовує відділ Ньютона-Рафсона . Загальна ідея полягає в тому, щоб обчислити d = 1 / q · 2 n за допомогою методу Ньютона, де n - кількість бітів, необхідний результату, і обчислити результат як p · d >> n . Час виконання зараз становить 2,87s - і це навіть без відсікання бітів перед розрахунком; це зайве для цього методу.


4

PARI / GP: 33000

Це, в основному, програма, представлена ​​на OEIS , модифікована для правильного введення та виведення формату. Він повинен слугувати базовою лінією, якщо нічого іншого.

Я припускаю, що це точно. Я перевірив це на 100 і 20 к проти OEIS, і він збігався для обох. В Інтернеті досить важко знайти додаткові цифри для перевірки.

Для 33 000 це займає приблизно 4,5 секунди, тож, напевно, можна було бити невеликий шматочок. Мені просто набридло байдикувати з введенням та циклом подання / збирання / компіляції / запуску 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);
}

Посилання Ideone.com


Ваші цифри відповідають моїм, тому я збираюся вийти на кінцівку і сказати, що вони, мабуть, правильні.
primo

Ця програма проводить по суті весь свій час у циклі, генеруючи цифри по черзі. Якщо ви просто берете, Str(floor(frac(x)*10^m)це йде в сотні / тисячі разів швидше.
Чарльз

2

Пітон 3

Оскільки вбудованих пі і е не вистачає цифр, я вирішив обчислити власну.

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 посилання

Вихід для STDIN = 1000:

8.5397342226735669504281233688422467204743749305568824722710929852470173635361001388261308713809518841081669216573834376992066672804294594807026609638293539437286935503772101801433821053915371716284188665787967232464763808892618434263301810056154560438283877633957941572944822034479453916753507796910068912594560500836608215235605783723340714760960119319145912948480279651779184356994356172418603464628747082162475871780202868607325544781551065680583616058471475977367814338295574582450942453416002008665325253385672668994300796223139976640645190237481531851902147391807396201201799703915343423499008135819239684881566321559967077443367982975103648727755579256820566722752546407521965713336095320920822985129589997143740696972018563360331663471959214120971348584257396673542429063767170337770469161945592685537660073097456725716654388703941509676413429681372333615691533682226329180996924321063261666235129175134250645330301407536588271020457172050227357541822742441070313522061438812060477519238440079

Nmax - це найбільше вхідне значення, яке ви можете надати своїй програмі до того, як ideone більше не запустить її.
Захоплення Кальвіна

1
@ Calvin'sHobbies Я думаю, що nmax довільно велика, хоча ...
Beta Decay

1
ideone не дає вам нескінченну обчислювальну силу. Яке найбільше вхідне значення, яке може працювати ваша програма на ideone? (Хоча насправді ваша програма не дотримується цього should be able to work for any N, given unlimited resourcesправила. Більшість результатів - нулі приблизно в межах N = 10000.)
Хобі Кальвіна

Це не python3: NameError: name 'xrange' not defined.
Бакуріу

2

Скала - 1790 рік

IDEOne за адресою http://ideone.com/A2CIto .

Ми використовуємо формулу Вітерфілда для π (і код формули Махіна, який грубо переносять звідси ). Ми обчислюємо е, використовуючи звичайний ряд потужностей.

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