Обчисліть пі до 5 десятків


15

Це відбувається з http://programmers.blogoverflow.com/2012/08/20-controversial-programming-opinions/

"Враховуючи, що Pi можна оцінити за допомогою функції 4 * (1 - 1/3 + 1/5 - 1/7 + ...) з більшою кількістю термінів, що дають більшу точність, напишіть функцію, яка обчислює Pi з точністю до 5 знаків після коми. "

  • Зауважимо, оцінку необхідно виконати шляхом обчислення послідовності, наведеної вище.

8
Вам, мабуть, слід додати ще кілька правил, інакше ви отримаєте відповіді на кшталт (python)p=lambda:3.14159
Метт

1
Ви бачили codegolf.stackexchange.com/questions/506/… , який дуже схожий? Принаймні, для цієї проблеми слід заборонити триггерні функції, оскільки вони дозволяють отримати тривіальні рішення, такі як ця програма QBASIC:? INT (4E5 * ATN (1)) /
1E5

Я думаю, вам слід зажадати, щоб алгоритм був послідовним наближенням: чим довше ви обчислюєте, тим ближче ви підходите до pi.
DavidC

@DavidCarraher, хоча математично неминуче використання цієї серії, з числової аналітичної точки зору це дуже сумнівно. Повільно зближуються серії, що чергуються, є дитиною, що втрачає значення.
Пітер Тейлор

2
Дуп, але він такий старий, його тут немає: stackoverflow.com/q/407518/12274
JB

Відповіді:


10

JavaScript, 46 58 56 45 байт

Оновлення ES6 : виявляється, що доступно більше функцій, коли минуло п'ять років.

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

Ця версія ( 45 байт; так, letпотрібно) працює в ES6 суворого режиму в теорії . На практиці ви можете запустити його в V8 (наприклад, з вузлом) за допомогою --use-strict --harmony-tailcalls; На жаль, функція належних викликів поки що не широко застосовується. Однак це конкретна поведінка, тому воно повинно бути добре.

Якщо ми хочемо дотримуватися широко розповсюдженого і не вимагає суворого режиму, ми можемо просто використовувати синтаксис жирної стрілки ES6 для функцій, але в іншому випадку збережемо ту саму реалізацію, що і раніше (запропонована Брайаном Н) вартістю 48 байт.

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

Вибір імені для одного варіанту не дійсно питання, але ми могли б також вибрати один з імен , які ми використовуємо для того, щоб звести до мінімуму забруднення глобальному контексті.


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

Ця версія є виразом функції; додайте два символи (наприклад, " f"), якщо ви хочете, щоб його назвали. Ця версія клобутує глобальні aта i; це можна запобігти, якщо ми додамо " a,i" до списку параметрів.

Використовує переформульовану версію алгоритму, щоб обійти необхідність віднімання.

 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)   + ...

Ось "звичайна" версія без цього коригування:

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

який працює на 64 62 символи.

Дякуємо @ardnew за пропозицію позбутися 4*попереднього return.


Історія

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}

oО дуже гарна робота, розбиваючи віднімання.
аколіт

1
чудова робота, але її потрібно писати як належну функцію
ardnew

@ardnew: Дякую, я, мабуть, пропустив цю деталь, коли читав опис проблеми. Я оновив його, і тепер це вираз функції виклику (лямбда); не впевнений, чи дозволено це чи потрібно йому дати ім’я. Якщо це так, все одно це лише додаткові два символи.
FireFly

1
@FireFly ви також можете поголити 2 символи, змінивши a+=2/i/-~-~i;return 4*aнаa+=8/i/-~-~i;return a
ardnew

@ardnew: о, приголомшливий; не думав про це. : D
FireFly

8

Python 59 байт

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

Це видає 1000 цифр; трохи більше, ніж потрібно 5. Замість використання встановленої ітерації він використовує це:

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

The 6637(Найбільш внутрішній знаменник) може бути сформульований наступним чином:

цифр * 2 * журнал 2 (10)

Це передбачає лінійну конвергенцію. Кожна більш глибока ітерація дасть ще один бінарний біт пі .

Якщо ж ви наполягаєте на використанні загару -1 ідентичності , подібне зближення може бути досягнуто, якщо ви не заперечуєте про проблему дещо інакше. Ознайомившись з частковими сумами:

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

очевидно, що кожен доданок стрибає вперед і назад в будь-яку сторону точки конвергенції; серія має змінну конвергенцію. Крім того, кожен доданок ближче до точки зближення, ніж був попередній термін; вона абсолютно монотонна щодо точки зближення. Поєднання цих двох властивостей означає, що середнє арифметичне будь-яких двох сусідніх доданків ближче до точки зближення, ніж будь-який із самих доданків. Щоб краще зрозуміти, що я маю на увазі, розгляньте наступне зображення:

Partial Sums

Зовнішній ряд є оригінальним, а внутрішній ряд виявляється, беручи середнє значення кожного із сусідніх членів. Чудова різниця. Але що справді примітно, це те, що ця нова серія також має чергування конвергенції та абсолютно монотонна щодо точки зближення. Це означає, що цей процес можна застосовувати знову і знову, ad nauseum.

Добре. Але як?

Деякі формальні визначення. Нехай Р 1 (п) бути п - й член першої послідовності, Р 2 (п) бути п - й член другий послідовності, і аналогічно Р до (п) п - е доданок з до - й послідовності , як визначено вище .

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, ...]

Не дивно, що ці коефіцієнти точно відповідають двочленним коефіцієнтам і можуть виражатися як один ряд трикутника Паскаля. Оскільки довільний ряд трикутника Паскаля є тривіальним для обчислення, довільно 'глибокий' ряд можна знайти, просто взявши перші n часткових сум, помноживши кожен на відповідний додаток у k- му рядку трикутника Паскаля і діливши на 2 k-1 .

Таким чином, можна досягти повної 32-бітової точності з плаваючою комою (~ 14 десяткових знаків) за допомогою всього 36 ітерацій, при цьому часткові суми навіть не зблизилися на другому знаку після коми. Це, очевидно, не гольф:

# 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

Якщо ви хотіли довільної точності, цього можна досягти з невеликою модифікацією. Тут ще раз обчислюємо 1000 цифр:

# 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

Початкове значення p починається на 2 10 більше, щоб протидіяти цілому поділу ефектів s / d, оскільки d стає більшим, внаслідок чого останні кілька цифр не збігаються. Зверніть увагу ще раз, що 3318також:

цифри * журнал 2 (10)

Така ж кількість ітерацій, що й перший алгоритм (вдвічі зменшується, оскільки t зменшується на 1, а не на 2 кожної ітерації). Ще раз це вказує на лінійну конвергенцію: один бінарний біт пі за ітерацію. В обох випадках для обчислення 1000 цифр пі потрібно 3318 ітерацій , як трохи краща квота, ніж 1 мільйон ітерацій для обчислення 5.


Це набагато краще, ніж моє рішення:4 * sum(1/(1+i*2) if not i%2 else -1/(1+i*2) for i in xrange(places*10**(places)))
Аарон Хол

1
Це дуже схоже на мій підхід , який, здається , є вашою іншою формою. Моє, як k → ∞, f(-1,k)підходить до вашої Ейлерової суми.
Просто красиве мистецтво

1
Дуже круто; чудовий аналіз та пояснення, дякую.
jeremy radcliff

Просто маленька річ. Чи не ви мали на увазі після P_1 = ..., P_2 = ..., P_3 = ..., P_4 = ..."... помножити кожен на відповідний член у kthрядку трикутника Паскаля та ділити на 2^{k-1}.", А не на nthрядок і 2^{n-1}?.
jeremy radcliff

@jeremyradcliff Я так, так. Дякуємо за виправлення.
прим

5

Математика 42 39 34 33 31 26 32

Підхід Архімеда 26 годин

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

Це досягає критерію, коли вхід дорівнює 822.

Питання: Хто-небудь знає, як він обчислив Гріх 180 градусів? Я не.


Підхід Лейбніца (серія Грегорі) 32 символи

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

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

Мадхава-Лейбніц Підхід до 37 знаків

Ця варіація використовує ще кілька символів, але сходить до критерію лише у 9 ітераціях!

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

всі вони обчислюють його за алгоритмом, наведеним у визначенні проблеми?
аколіт

Підхід @acolyte Leibniz (тепер перший у списку) справді той, який згадується в описі проблеми. Це дуже повільно сходиться. Невелика різниця на ньому (Мадхава-Лейбніц) конвергується дуже швидко.
DavidC

Синус 180 ° досить легко. Це 180 ° / с, що може отримати хитрість поза звичайними підозрюваними для Н.
JB

Поясніть, будь ласка, @JB Хитрість для вимірювання?
DavidC

Цей запис повинен бути "32", оскільки лише підхід Лейбніца відповідає вимогам (рахуючи символи коду як дані, я отримую 34, але обидва пробіли можуть бути безпечно видалені, даючи дійсно довжину 32).
celtschk


4

Java (67 символів)

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

Зауважте, що це дозволяє уникнути втрати значущості шляхом додавання чисел у правильному порядку.


це також повністю відповідає коду С. якщо в курсі , як C, ви могли б змінити , while(--i>0)щоб while(i--)і зберегти 2 символів
ardnew

1
@ardnew, правда, але з C є набагато цікавіші хитрощі ...
Пітер Тейлор



3

C (GCC) (44 символи)

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

Це 41 показник, але його також потрібно скласти, -O2щоб отримати оптимізатор для усунення хвостової рекурсії. Це також покладається на невизначену поведінку стосовно порядку, в якому ++вони виконуються; спасибі ugoren за те, що вказав на це. Я тестував gcc 4.4.3 під 64-розрядним Linux.

Зауважте, що якщо оптимізатор також не змінить суму, він додасть від найменшого числа, тож уникне втрати значущості.

Телефонувати як p().


Ваш рекурсивний дзвінок є q(), ні p(). І я не думаю, що -O2слід рахувати (але якщо ви все-таки порахуєте, це 4 символи через необхідний простір).
угорен

Також: 1. gcc 4.1.1 не оптимізує рекурсію (і я не бачу, як це могло б), тому стек переповнюється. 2. його слід називати як p(0). 3. Збережіть картку від return++i.... 4. Двоє ++iробить невизначеною поведінку.
угорен

@ugoren, дякую за ваші коментарі. Для того, щоб: q- це навчить мене повторно перевіряти після перейменування. Я думаю, що я дотримуюся звичайної практики, рахуючи -O23 знаки, але ми можемо відкрити її на мета, якщо хочете; meta.codegolf.stackexchange.com/questions/19 - єдина відповідна дискусія, яку я можу знайти. Я додав версію gcc, яку я використовую, і яка дозволяє мені називати її як p(). Збереження діаграми зупиняє оптимізатор і надає сегменти за замовчуванням. Я уточню,
Пітер Тейлор

Я додав відповідь на мета-запитання про прапори. Про p()- ви впевнені, що дзвінки p()з будь-якого контексту спрацюють? Або це просто те, що трапилось у вашому тесті?
угорен

@ugoren, можливо, мені пощастило послідовно. Навіть якщо я називаю це двічі поспіль, другий все одно повертає правильне значення. gcc, здається, створює дещо інший код для p()vs p(0), але я не знаю, яку поведінку він документує, і я насправді не програміст на C.
Пітер Тейлор

3

J, 26 символів

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

З 100 пунктів послідовності переміщено до 1e6 елементів. Також тепер це код з тегом і його можна без помилок скопіювати з браузера на консоль.

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

3
-/4%>:2*i.1e6- 13 символів. (Завдяки b_jonas в #jsoftware за те, що я зрозумів, що -/працює для обчислення суми з змінним знаком. [Це тому, що всі оператори в J мають однаковий пріоритет і право-асоціативні, тому -/ 1 2 3 4<=> 1 - (2 - (3 - 4))<=> 1 - 2 + 3 - 4.)
FireFly

це акуратно і вдвічі приголомшливіше. Або навіть 2 ^ 10 більш дивовижно!
fftw

@FireFly, що прекрасно
Йона,

2

Javascript - 33 символи

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

Зателефонуйте, pпередаючи додатне непарне число, xі він обчислить Pi з (x-1)/2умовами.


2

Рубін - 82 ч

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)

Спробуй це : https://repl.it/LQ8w

Підхід використовує даний ряд побічно, використовуючи числовий підхід прискорення. Отриманий результат є

pi ≈ 3.14159265161

vs.

pi = 3.14159265359

Це починається з

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

І тоді, оскільки це чергується, ми можемо прискорити конвергенцію, використовуючи

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

І це неодноразово застосовується так:

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

І для простоти f(n) = f(n,n).


Рубін - 50 символів

Якщо ви не заперечуєте, щоб працювати дуже довго, то ви можете просто скористатися

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

або

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

1

C, 69 символів

float p,b;void main(a){b++<9e6?p+=a/b++,main(-a):printf("%f\n",4*p);}
  • Запустити без параметрів командного рядка (так a ініціалізується до 1).
  • Потрібно скласти з оптимізацією.
  • void mainдивно і нестандартно, але змушує роботу працювати. Без нього рекурсія реалізується як реальний виклик, що призводить до переповнення стека. Альтернатива - додаванняreturn .
  • Два символи 4*можна зберегти, якщо вони виконуються з трьома параметрами командного рядка.

Ви можете скоротити це до int main(a)або навіть main(a), GCC лише попереджає. І це все void mainодно дасть попередження , а може, навіть тому, що у вас є лише один аргумент main.
nyuszika7h

1

Clojure - 79 годин

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

Це створює функцію без аргументів, яка обчислює поплавок, який правильно наближає pi до п'яти знаків після коми. Зауважте, що це не пов'язує функцію з таким ім'ям, як pi, таким чином, цей код повинен бути або оцінений на місці з evalяк, (<code>)або пов'язаний з ім'ям, і в цьому випадку рішення є

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

на 82 ч

Про

(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 символів

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

Я не знаю, що я можу отримати його набагато менше, не порушуючи правило алгоритму.


1
Як щодо цього 45? <?for(;1e6>$j;)$p+=($i=-$i|4)/~-$j+=2;echo$p;
примо

Я намагався придумати це, але не зміг змусити оперативної роботи. Дякую за пропозицію!
TwoScoopsofPig

Ви можете видалити останню крапку з комою, щоб зберегти 1 символ.
nyuszika7h

1

Perl - 43 39 символів

не впевнений, що правила щодо анонімних підпрограм, але ось інша реалізація з використанням серії @ FireFly

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

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


0

Ява - 92 84 символів

Я далеко не можу перемогти результат Пітера Тейлора, але ось мій:

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

Негольована версія:

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

Редагувати: збережено кілька символів за допомогою потрійного оператора.


0

Пітон - 56 символів

Мех, мій пітон-фу недостатньо сильний. Я не міг побачити більше ярликів, але, можливо, досвідченіший гольфіст може знайти тут щось, що можна приправити?

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

Ви можете використовувати Python 3, щоб зберегти один байт для поділу float ( 4.-> 4). В інших новинах, я щойно знайшов випадок, коли Python 3 насправді б’є Python 2 у коді гольфу!
nyuszika7h

0

Рубін - 54 ч

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

Моя перша спробу на консолі

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

63 символи.


Ви можете зберегти байт, використовуючи def a;замість def a().
nyuszika7h

Ще один, видаливши останній крапку з комою.
nyuszika7h

0

Perl (76 символів)

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

(Результат: 3.14159052)

Не найкоротше можливе рішення, але, можливо, цікаве. Це геометричний. Я обчислюю площу під колом.

У мене з'явився ще один кумедний підхід, але він справді повільний. Він підраховує кількість дискретних точок у квадраті, що знаходиться нижче чверті кола, і обчислює пі від нього:

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

Очікується кількість ітерацій як аргумент командного рядка. Тут ви можете побачити, як час роботи залежить від точності. ;)

$ 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 символів)

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

Трохи коротше:

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




0

SQL, 253 байти

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+')')

Я б забезпечив SQL Fiddle, але це надто багато циклів, глибоко знаходячи 1/3 1/5 1/7 і т.д. дробів, і дає помилки, хаха. Однак якщо ви перейдете @B<100000до 1000цього, він працює (очевидно, не на ту ж кількість цифр точності).


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