Расчет числа е с использованием Raku

9

Я пытаюсь вычислить постоянную е (число Эйлера Эйка ), вычисляя формулу е

Чтобы вычислить факториал и деление за один снимок, я написал это:

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

Но это не сработало. Как это сделать правильно?

Ларс Мальмстин
источник
Каким образом это произошло: «это не сработало»?
Шерил Хохман
1
Часть знаменателя в примере кода не сработала, потому что она использовала предыдущую $_ переменную- значение , пытаясь построить факториал. Это было явно излишним. В правильном решении ниже, $_был сброшен, и это сработало отлично.
Ларс Мальмстин
Спасибо. Я думаю, я больше искал, что именно подразумевалось под этим утверждением. Как будто была ошибка, как не соответствовало тому, что вы ожидали, такого рода вещи. Я полагаю, ваш расчет не соответствует известным ответам для этого расчета. Рад, что у вас все получилось !! Кроме того, отличное описание после ответа о том, что было на самом деле проблема :-)
SherylHohman

Ответы:

11

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

Один вкладыш Одна буква 1

say e; # 2.718281828459045

«Трактат о нескольких способах» 2

Нажмите на ссылку выше, чтобы увидеть необычную статью Дамиана Конвея о компьютерах eв Раку.

Статья очень веселая (в конце концов, это Дамиан). Это очень понятное обсуждение вычислительной техники e. И это дань уважения бикарбонатному перевоплощению Раку философии TIMTOWTDI, поддерживаемой Ларри Уоллом. 3

В качестве закуски вот цитата из середины статьи:

Учитывая, что все эти эффективные методы работают одинаково - суммируя (начальное подмножество) бесконечный ряд терминов - возможно, было бы лучше, если бы у нас была функция, которая сделала бы это для нас. И, конечно, было бы лучше, если бы функция могла сама определить, сколько именно этого начального подмножества ряда она должна фактически включить, чтобы получить точный ответ ... вместо того, чтобы требовать от нас вручную прочесывать результаты несколько испытаний, чтобы обнаружить это.

И, как это часто бывает в Raku, на удивление легко построить именно то, что нам нужно:

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

Анализируя ваш код

Вот первая строка, генерирующая серию:

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

Закрытие ( { code goes here }) вычисляет термин. Замыкание имеет подпись, неявную или явную, которая определяет, сколько аргументов оно примет. В этом случае нет явной подписи. Использование $_( переменная "topic" ) приводит к неявной подписи, которая требует одного аргумента, с которым связан $_.

Оператор sequence ( ...) неоднократно вызывает замыкание слева от него, передавая предыдущий член в качестве аргумента замыкания, чтобы лениво построить ряд терминов до конечной точки справа, что в данном случае является *сокращением для 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 }

( ×это оператор умножения инфиксов, более привлекательный псевдоним Юникода для обычного инфикса ASCII *.)

Это сокращение для:

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

(Я использовал псевдо метасинтаксические обозначения внутри фигурных скобок, чтобы обозначить идею добавления или вычитания столько терминов, сколько требуется.

В более общем смысле, помещая инфиксный оператор opв квадратные скобки в начале выражения, формируется составной префиксный оператор, эквивалентный reduce with => &[op],. См. Сокращение метаоператора для получения дополнительной информации.

Теперь мы можем переписать замыкание для использования нового факториального постфиксного оператора:

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

Бинго. Это производит правильную серию.

... пока этого не произойдет, по другой причине. Следующая проблема - численная точность. Но давайте разберемся с этим в следующем разделе.

Одна строка, полученная из вашего кода

Может быть, сжать три строки до одной:

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

.[^10]относится к теме, которая установлена given. ( ^10это условное 0..9обозначение, поэтому вышеприведенный код вычисляет сумму первых десяти членов в ряду.)

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

Теперь у нас осталась последняя (большая!) Проблема, указанная вами в комментарии ниже.

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

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

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

Я подтвердил это до 500 десятичных цифр. Я ожидаю, что он останется точным до тех пор, пока не произойдет сбой программы из-за превышения некоторого ограничения языка Raku или компилятора Rakudo. (См. Мой ответ на Невозможно распаковать bigint 65536 бит в родное целое для некоторого обсуждения этого.)

Сноски

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" числа Эйлера " .

3 См. TIMTOWTDI против TSBO-APOO-OWTDI для одного из более сбалансированных представлений TIMTOWTDI, написанных поклонником python. Но есть минусы принимать TIMTOWTDI слишком далеко. Чтобы отразить эту последнюю «опасность», сообщество Perl придумало юмористически длинный, нечитаемый и недооцененный TIMTOWTDIBSCINABTE - существует больше, чем один способ сделать это, но иногда последовательность не является плохой вещью, произносится «Тим Бобикарбонат Тим». Как ни странно , Ларри применил бикарбонат к дизайну Раку, а Дамиан применил его к вычислениям eв Раку.

raiph
источник
Спасибо за ответ. Раздел под названием « Мой путь, основанный на вашем пути» решает его довольно хорошо. Мне нужно посмотреть на тонкости, хотя. Я не знал, что равнина $была сокращением state $, это довольно удобно.
Ларс Мальмстин
Есть ли способ указать количество цифр eдля 3-го решения (под названием « Мой путь основан на вашем способе» )? Я пытался добавить FatRat (500) рядом с 1 в: ... given 1.FatRat(500), ...для точности 500 цифр, но это не сработало.
Ларс Мальмстин
@LarsMalmsteen Я рассмотрел ваш очень важный FatRatвопрос в последнем разделе. Я также отточил весь ответ, хотя единственное существенное изменение - это FatRatматериал. (Между прочим, я понимаю, что большая часть моего ответа действительно имеет отношение к вашему первоначальному вопросу; я надеюсь, вы не возражали против того, чтобы я писал весь этот дополнительный пух, чтобы развлечь себя и, возможно, быть интересным для более поздних читателей.)
raiph
Спасибо за дополнительные усилия. Таким образом, .FatRatрасширение должно быть помещено в генератор кода. Теперь я попробовал это с FatRatдобавленным способом, и он вычислил e с точностью до 1000+ цифр. Добавленный лишний пух стоит. Например, я не знал, что sayон усекает длинные массивы / последовательности. Такие биты информации полезно знать.
Ларс Мальмстин
@LarsMalmsteen :) "Таким образом, .FatRatрасширение должно быть помещено в генератор кода." Да. В более общем смысле, если выражение, включающее деление, уже было оценено, будет слишком поздно отменить ущерб, вызванный превышением Ratточности. Если это так, он оценивается как Num(float), и это, в свою очередь, портит любые дальнейшие вычисления, связанные с ним, делая их также Num . Единственный способ гарантировать, что вещи остаются, FatRatсостоит в том, чтобы начать их FatRatи избежать любых Nums. Ints и Rats в порядке, при условии, что есть хотя бы один, FatRatчтобы Раку знал, что он придерживается FatRats.
Раиф
9

Есть дроби $_. Таким образом, вам нужно, 1 / (1/$_ * $a++)а точнее $_ /$a++.

С помощью Raku вы можете сделать этот шаг за шагом

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.
Холли