Обчислення числа e за допомогою Raku


9

Я намагаюся обчислити константу e (число AKA Euler ) шляхом обчислення формули е

Для того, щоб обчислити факторіал і поділ за один кадр, я написав це:

my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;
say reduce  * + * , @e[^10];

Але це не вийшло. Як це зробити правильно?


Яким чином це зробили: "не вийшло"?
SherylHohman

1
Частка знаменника у зразковому коді не вийшла, оскільки вона використовувала попередню $_ змінну значення , намагаючись побудувати факторіал. Це було, очевидно, зайвим. У правильному рішенні нижче $_впали, і це вийшло чудово.
Lars Malmsteen

Дякую. Я думаю, я більше шукав, що саме малося на увазі під цим твердженням. Ніби була помилка, як не відповідала тому, що ви очікували, такого роду. Я думаю, ваш розрахунок не відповідав відомим відповідям на цей розрахунок. Радий, що ти це розробив !! Також чудовий опис відповіді щодо того, у чому полягає справжня проблема :-)
SherylHohman,

Відповіді:


11

Я аналізую ваш код у розділі Аналіз вашого коду . Перед цим я представляю пару веселих розділів бонусного матеріалу.

Один вкладиш Один лист 1

say e; # 2.718281828459045

"Трактат про декілька способів" 2

Клацніть вище посилання, щоб побачити надзвичайну статтю Даміана Конвей про обчислення eв Раку.

Стаття дуже весела (зрештою, це Даміан). Це дуже зрозуміле обговорення обчислень e. І це повагу до перевтілення бікарбонату Раку у філософію TIMTOWTDI, яку підтримує Ларрі Уолл. 3

Як закуска, ось цитата приблизно з півдороги статті:

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

І, як це часто буває в Раку, напрочуд легко побудувати саме те, що нам потрібно:

sub Σ (Unary $block --> Numeric) {
  (0..∞).map($block).produce(&[+]).&converge
}

Аналіз вашого коду

Ось перший рядок із створення серії:

my @e = 1, { state $a=1; 1 / ($_ * $a++) } ... *;

Закриття ( { code goes here }) обчислює термін. Закриття має підпис, неявний або явний, який визначає, скільки аргументів він прийме. У цьому випадку явного підпису немає. Використання $_( в «темі» змінних ) призводить до неявній підпису , який вимагає один аргумент , який прив'язаний до $_.

Оператор послідовності ( ...) неодноразово викликає завершення зліва, передаючи попередній термін як аргумент закриття, щоб ліниво побудувати ряд термінів до кінцевої точки праворуч, що в цьому випадку є *скороченням для Infака-нескінченності.

Тема в першому заклику до закриття є 1. Таким чином, обчислюється і повертається 1 / (1 * 1)завершення, даючи перші два терміни в ряду як 1, 1/1.

Тема другого дзвінка - це значення попереднього 1/1, тобто 1знову. Таким чином, завершення обчислює і повертається 1 / (1 * 2), продовжуючи серію до 1, 1/1, 1/2. Це все добре виглядає.

Наступне обчислювальне завершення, 1 / (1/2 * 3)яке є 0.666667. Цей термін повинен бути 1 / (1 * 2 * 3). На жаль

Здійснення відповідності коду формулі

Ваш код повинен відповідати формулі:
е

У цій формулі кожен термін обчислюється виходячи з його положення в ряді. До - го члена ряду (де до = 0 для першого 1) просто факторіала до «з взаємним.

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

Створимо факторний поштовий оператор:

sub postfix:<!> (\k) { [×] 1 .. k }

( ×це оператор множення інфіксації, приємніше виглядає псевдонім Unicode звичайної інфіксації ASCII *.)

Це скорочення для:

sub postfix:<!> (\k) { 1 × 2 × 3 × .... × k }

(Я використовував псевдометасинтаксичні позначення всередині дужок, щоб позначити ідею додавання чи віднімання стільки термінів, скільки потрібно.

Більш загально, розміщення оператора інфіксації opу квадратних дужках на початку виразу утворює складений оператор префікса, який є еквівалентом reduce with => &[op],. Для отримання додаткової інформації див. Метаоператор скорочення .

Тепер ми можемо переписати закриття, щоб скористатися новим факторіальним оператором Postfix:

my @e = 1, { state $a=1; 1 / $a++! } ... *;

Бінго. Це дає правильний ряд.

... поки цього не стане, з іншої причини. Наступна проблема - числова точність. Але давайте розберемося з цим у наступному розділі.

Один лайнер, отриманий від вашого коду

Можливо, стисніть три лінії до однієї:

say [+] .[^10] given 1, { 1 / [×] 1 .. ++$ } ... Inf

.[^10]стосується теми, яку встановлює given. ( ^10це скорочення для 0..9, тому вищевказаний код обчислює суму перших десяти доданків у серії.)

Я усунув $aіз закінчення обчислювань наступний термін. Самотня $- це те саме (state $), що і анонімний скаляр стану. Я зробив це попереднім збільшенням замість післякріплення, щоб досягти такого ж ефекту, як і ви, ініціалізуючи $aдо 1.

Зараз ми залишилися з остаточною (великою!) Проблемою, яку ви вказали у коментарі нижче.

За умови, що жоден з його операндів не є Num(поплавком і, таким чином, наближеним), /оператор, як правило, повертає на 100% точність Rat(з обмеженою раціональністю точності). Але якщо знаменник результату перевищує 64 біт, то цей результат перетворюється на Num- який торгує продуктивністю для точності, компромісу ми не хочемо робити. Нам потрібно це врахувати.

Щоб вказати необмежену точність , а також 100% точність, просто примушуйте операцію використовувати FatRats. Щоб зробити це правильно, просто зробіть (принаймні) один з операндів FatRat(а жоден інший не буде а Num):

say [+] .[^500] given 1, { 1.FatRat / [×] 1 .. ++$ } ... Inf

Я підтвердив це до 500 десяткових цифр. Я очікую, що вона буде точною, поки програма не завершиться через перевищення деякого ліміту мови Raku або компілятора Rakudo. (Дивіться мою відповідь на Неможливо розв’язати 65536 біт шириною bigint в нативне ціле число для обговорення цього.)

Виноски

1 Раку має кілька важливих математичних констант , побудованих в тому числі e, iі pi(і його псевдонім π). Таким чином, можна написати особистість Ейлера в Раку дещо так, як це виглядає в математичних книгах. Залучивши запис Raku RosettaCode для особи Юлера :

# There's an invisible character between <> and i⁢π character pairs!
sub infix:<⁢> (\left, \right) is tighter(&infix:<**>) { left * right };

# Raku doesn't have built in symbolic math so use approximate equal 
say e**i⁢π + 1 ≅ 0; # True

2 Стаття Даміана обов'язково прочитана. Але це лише одна з кількох захоплюючих процедур, які входять у 100+ матчів для google за номером "raku" euler's number " .

3 Дивіться TIMTOWTDI проти TSBO-APOO-OWTDI для одного з більш збалансованих поглядів TIMTOWTDI, написаного шанувальником пітона. Але є й недоліки прийняти TIMTOWTDI занадто далеко. Щоб відобразити цю останню "небезпеку", спільнота Perl створила жартівливо довгий, нечитабельний і занижений TIMTOWTDIBSCINABTE - Існує більше одного способу зробити це, але іноді послідовність не є поганою річчю, вимовляється "Тім Тоді Бікарбонат". Як не дивно , Ларрі застосував бікарбонат до дизайну Раку, а Даміан застосовує його до обчислень eу Раку.


Дякую за відповідь. Розділ під назвою Мій шлях на основі вашого шляху вирішує його досить добре. Мені потрібно шукати тонкощі. Я не знав, що рівнина $була скороченою state $, це досить зручно.
Lars Malmsteen

Чи є спосіб вказати кількість цифр eдля третього рішення (під назвою « Мій шлях на основі вашого шляху» )? Я намагався додати FatRat (500) поруч із 1 дюймом: ... given 1.FatRat(500), ...щоб зробити цифри точністю 500 цифр, але це не вийшло.
Lars Malmsteen

@LarsMalmsteen Я вирішив ваше дуже важливе FatRatпитання в останньому розділі. Я також відточив усю відповідь, хоча єдина основна зміна - це FatRatматеріал. (До речі, я розумію, що більша частина моєї відповіді насправді дотична до вашого первісного питання; я вірю, ви не заперечували мені, щоб написати увесь зайвий пух, щоб розважити себе і, можливо, буде цікавим для пізніших читачів.)
raiph

Дякую за додаткові зусилля. Отже, .FatRatрозширення потрібно помістити всередині генератора коду. Тепер я спробував це з FatRatдоданим таким чином, і він розрахував е до точності 1000+ цифр. Додатковий пух додається суто. Наприклад, я не знав, що sayобрізання довгих масивів / послідовностей. Такі шматочки інформації добре знати.
Lars Malmsteen

@LarsMalmsteen :) "Отже, .FatRatрозширення потрібно ввести в генератор коду." Так. Більш загально, якщо вираз, що включає поділ, вже був оцінений, занадто пізно відміняти шкоду, заподіяну, якщо вона переповнювала Rats точність. Якщо у нього є, він буде оцінювати з Num(поплавком) і що , в свою чергу будь-які подальші вад розрахунки з участю його, роблячи їх також Num . Єдиний спосіб забезпечити залишення речей FatRat- це запустити їх FatRatі уникнути будь-яких Nums. Ints і Rats все в порядку, за умови, що принаймні один, FatRatщоб повідомити Раку дотримуватися FatRats.
raiph

9

У фракціях є фракції $_. Таким чином , вам необхідно 1 / (1/$_ * $a++)або , вірніше $_ /$a++.

Раку ви могли зробити цей розрахунок поетапно

1.FatRat,1,2,3 ... *   #1 1 2 3 4 5 6 7 8 9 ...
andthen .produce: &[*] #1 1 2 6 24 120 720 5040 40320 362880
andthen .map: 1/*      #1 1 1/2 1/6 1/24 1/120 1/720 1/5040 1/40320 1/362880 ...
andthen .produce: &[+] #1 2 2.5 2.666667 2.708333 2.716667 2.718056 2.718254 2.718279 2.718282 ...
andthen .[50].say      #2.71828182845904523536028747135266249775724709369995957496696762772

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