Erlang 编程和计算 pi 的新手
New to Erlang programming and calculate pi
我是 erlang(和一般编程)的新手。所以我试图让 Erlang 准确地输出 Pi 的计算结果,精确到小数点后 5 位。我已经连续 5 天(可能大约 35 小时)尝试不同的事情并进行研究,但我就是想不通。这是我的思考过程和到目前为止所做的工作:
注意:我想从更小的东西开始(精确到 2 位小数),然后我想一旦我能达到 2,我就可以调整到 5 位小数。
因此,通过对 4* ( 1 - 1/3 + 1/5 - 1/7 ...) 求和来使用 pi 近似值,您可以使用更多的术语准确地获得 pi 的值。我想要做的是让它在正确到达所需小数点的那一刻停止递归。现在我已经能够通过反复试验计算出有多少项可以准确地得到 1、2、3、4、5、6 位小数,但我正在尝试获得一种通用的方法,因为我们假设您一开始不知道 pi 的值。
所以我想做的是让 erlang 提出 2 个求和...N 个项的求和和 N+1 个项的求和,然后 N 个求和amount 匹配下一个 (N+1) 项的总和,它应该停止并输出该匹配值。
我的想法是,由于我不能运行计算某个小数,所以我不能运行计算一个整数...所以求和乘以 10 ^P(其中 P 是小数位数,所以在我下面的示例中 P 是输入 2),然后 t运行cate 检查两个求和何时匹配,当它们匹配时,给出输出,我可以用它除以 10^P 得到小数。
-module (difpi).
-export [(sumpi1/2)].
-export [(sumpi2/2)].
sumpi1(628,_) -> 0;
sumpi1(N,P) -> trunc((((math:pow(-1,N))*(4*(math:pow(10,P))/(2*N+1)) + (sumpi1(N+1,P))))).
sumpi2(628+1,_) -> 0;
sumpi2(K,P) -> trunc((((math:pow(-1,K))*(4*(math:pow(10,P))/(2*K+1)) + (sumpi2(K+1,P))))).
我能够计算出需要 628 个术语才能达到 314,而第 628 个术语也是 314,但是如果没有 "knowing" 多少个术语,我如何才能做到这一点?
所以我想让它说...sumPi1 在 1 个学期之后,sumPi2 在第 1 个 2 个学期之后...他们匹配吗(否),所以现在 sumPi1 前 2 个学期,sumPi2 前 3 个学期……他们配吗? (否)...但我希望该过程继续进行,直到它们匹配为止,然后我希望输出显示它们匹配,并且它们在 314 处匹配。
如您所见,我内置了 2 个参数,这样我就可以 运行 它并输入它可以到达的小数位数。所以我可以说,将 pi 计算为小数点后 1 位、小数点后 2 位、小数点后 3 位……等等。 (尽管我只需要它到 5,只是觉得这样做会很酷)。
我希望我的问题是有道理的。可以的话请帮忙,我的脑子快炸了!
这是显示 t运行 的第 628 项和第 629 项匹配的输出结果(显然只需除以 10^2 得到 3.14 以将其显示为 2 位小数)。
ကErlang/OTP 19 [erts-8.2] [64-bit] [smp:4:4] [async-threads:10]
Eshell V8.2 (abort with ^G)
1> cd("c:/erlang").
c:/erlang
ok
2> c(difpi).
{ok,difpi}
3> difpi:sumpi1(0,2).
314
4> difpi:sumpi2(0,2).
314
5>
恭喜您完成学习 Erlang 的任务。它确实是一种迷人的编程语言,运行在迷人的虚拟机之上运行。
在模式匹配的帮助下完全有可能解决这个问题。您必须采用保留计算的最后结果并在进一步迭代之前将其与当前步骤进行比较的策略。
这种级数收敛很慢,所以要特别注意利用尾调用优化,这意味着对函数的递归调用必须是函数本身的最后一条也是唯一一条语句。
我的实施方案如下。您可以通过这个简单的单元测试证明它有效:
%%% file: diff_pi_tests.erl
-module(diff_pi_tests).
-include_lib("eunit/include/eunit.hrl").
-import(diff_pi, [sum_pi/1]).
sum_pi_test_() ->
[
?_assertEqual(3.0, sum_pi(0)),
?_assertEqual(3.1, sum_pi(1)),
?_assertEqual(3.14, sum_pi(2)),
?_assertEqual(3.141, sum_pi(3)),
?_assertEqual(3.1415, sum_pi(4)),
?_assertEqual(3.14159, sum_pi(5))
].
实施:
%%% file: diff_pi.erl
-module(diff_pi).
-export([sum_pi/1]).
-spec sum_pi(integer()) -> float().
sum_pi(Precision) ->
sum_pi(4, -4, 3, Precision).
-spec sum_pi(number(), number(), number(), integer()) -> float().
sum_pi(LastResult, Numerator, Denominator, Precision) ->
NextResult = LastResult + Numerator/Denominator,
% Uncomment the following line to see the comparison of each step
% io:format("~p ~p~n", [LastResult, NextResult]),
case compare(LastResult, NextResult, Precision) of
true ->
Magnitude = math:pow(10, Precision),
trunc(NextResult*Magnitude)/Magnitude;
false ->
sum_pi(NextResult, -1*Numerator, Denominator+2, Precision)
end.
-spec compare(number(), number(), integer()) -> boolean().
compare(X, Y, Precision) ->
RoundX = trunc(X*math:pow(10, Precision)),
RoundY = trunc(Y*math:pow(10, Precision)),
RoundX =:= RoundY.
编译并运行测试:
$ erlc *.erl && erl -noshell -s eunit test diff_pi -s init stop
All 6 tests passed.
我是 erlang(和一般编程)的新手。所以我试图让 Erlang 准确地输出 Pi 的计算结果,精确到小数点后 5 位。我已经连续 5 天(可能大约 35 小时)尝试不同的事情并进行研究,但我就是想不通。这是我的思考过程和到目前为止所做的工作:
注意:我想从更小的东西开始(精确到 2 位小数),然后我想一旦我能达到 2,我就可以调整到 5 位小数。
因此,通过对 4* ( 1 - 1/3 + 1/5 - 1/7 ...) 求和来使用 pi 近似值,您可以使用更多的术语准确地获得 pi 的值。我想要做的是让它在正确到达所需小数点的那一刻停止递归。现在我已经能够通过反复试验计算出有多少项可以准确地得到 1、2、3、4、5、6 位小数,但我正在尝试获得一种通用的方法,因为我们假设您一开始不知道 pi 的值。
所以我想做的是让 erlang 提出 2 个求和...N 个项的求和和 N+1 个项的求和,然后 N 个求和amount 匹配下一个 (N+1) 项的总和,它应该停止并输出该匹配值。
我的想法是,由于我不能运行计算某个小数,所以我不能运行计算一个整数...所以求和乘以 10 ^P(其中 P 是小数位数,所以在我下面的示例中 P 是输入 2),然后 t运行cate 检查两个求和何时匹配,当它们匹配时,给出输出,我可以用它除以 10^P 得到小数。
-module (difpi).
-export [(sumpi1/2)].
-export [(sumpi2/2)].
sumpi1(628,_) -> 0;
sumpi1(N,P) -> trunc((((math:pow(-1,N))*(4*(math:pow(10,P))/(2*N+1)) + (sumpi1(N+1,P))))).
sumpi2(628+1,_) -> 0;
sumpi2(K,P) -> trunc((((math:pow(-1,K))*(4*(math:pow(10,P))/(2*K+1)) + (sumpi2(K+1,P))))).
我能够计算出需要 628 个术语才能达到 314,而第 628 个术语也是 314,但是如果没有 "knowing" 多少个术语,我如何才能做到这一点?
所以我想让它说...sumPi1 在 1 个学期之后,sumPi2 在第 1 个 2 个学期之后...他们匹配吗(否),所以现在 sumPi1 前 2 个学期,sumPi2 前 3 个学期……他们配吗? (否)...但我希望该过程继续进行,直到它们匹配为止,然后我希望输出显示它们匹配,并且它们在 314 处匹配。
如您所见,我内置了 2 个参数,这样我就可以 运行 它并输入它可以到达的小数位数。所以我可以说,将 pi 计算为小数点后 1 位、小数点后 2 位、小数点后 3 位……等等。 (尽管我只需要它到 5,只是觉得这样做会很酷)。
我希望我的问题是有道理的。可以的话请帮忙,我的脑子快炸了!
这是显示 t运行 的第 628 项和第 629 项匹配的输出结果(显然只需除以 10^2 得到 3.14 以将其显示为 2 位小数)。
ကErlang/OTP 19 [erts-8.2] [64-bit] [smp:4:4] [async-threads:10]
Eshell V8.2 (abort with ^G)
1> cd("c:/erlang").
c:/erlang
ok
2> c(difpi).
{ok,difpi}
3> difpi:sumpi1(0,2).
314
4> difpi:sumpi2(0,2).
314
5>
恭喜您完成学习 Erlang 的任务。它确实是一种迷人的编程语言,运行在迷人的虚拟机之上运行。
在模式匹配的帮助下完全有可能解决这个问题。您必须采用保留计算的最后结果并在进一步迭代之前将其与当前步骤进行比较的策略。
这种级数收敛很慢,所以要特别注意利用尾调用优化,这意味着对函数的递归调用必须是函数本身的最后一条也是唯一一条语句。
我的实施方案如下。您可以通过这个简单的单元测试证明它有效:
%%% file: diff_pi_tests.erl
-module(diff_pi_tests).
-include_lib("eunit/include/eunit.hrl").
-import(diff_pi, [sum_pi/1]).
sum_pi_test_() ->
[
?_assertEqual(3.0, sum_pi(0)),
?_assertEqual(3.1, sum_pi(1)),
?_assertEqual(3.14, sum_pi(2)),
?_assertEqual(3.141, sum_pi(3)),
?_assertEqual(3.1415, sum_pi(4)),
?_assertEqual(3.14159, sum_pi(5))
].
实施:
%%% file: diff_pi.erl
-module(diff_pi).
-export([sum_pi/1]).
-spec sum_pi(integer()) -> float().
sum_pi(Precision) ->
sum_pi(4, -4, 3, Precision).
-spec sum_pi(number(), number(), number(), integer()) -> float().
sum_pi(LastResult, Numerator, Denominator, Precision) ->
NextResult = LastResult + Numerator/Denominator,
% Uncomment the following line to see the comparison of each step
% io:format("~p ~p~n", [LastResult, NextResult]),
case compare(LastResult, NextResult, Precision) of
true ->
Magnitude = math:pow(10, Precision),
trunc(NextResult*Magnitude)/Magnitude;
false ->
sum_pi(NextResult, -1*Numerator, Denominator+2, Precision)
end.
-spec compare(number(), number(), integer()) -> boolean().
compare(X, Y, Precision) ->
RoundX = trunc(X*math:pow(10, Precision)),
RoundY = trunc(Y*math:pow(10, Precision)),
RoundX =:= RoundY.
编译并运行测试:
$ erlc *.erl && erl -noshell -s eunit test diff_pi -s init stop
All 6 tests passed.