Erlang 并发 Montecarlo Pi 估计
Erlang Concurrent Montecarlo Pi Estimation
对于一项作业,我需要使用 Montecarlo 方法在 Erlang 中估计 pi,但具有指定数量的参与者和迭代。我有一个不使用并发的工作版本(改编自 https://programmingpraxis.com/2009/10/09/calculating-pi/),因此采用一个参数,N = 迭代次数(点)。我试图通过创建另一个带有两个参数的 montecarlo() 函数来添加它,N = # of iterations 和 X = # of actors。我无法弄清楚如何使用(伪)循环来生成每个演员。
之前的版本 return 是 pi 估计,但在我弄清楚产卵之后,我假设我必须对每个演员的 return 值进行平均,以获得最终的 pi 估计。
这是我的资料:
-module(pi).
-export([montecarlo/1, montecarlo/2]).
montecarlo(N, X)->
NumIterPerActor = N div X,
%io:fwrite("Number of actors = ~w~n",[X]),
%io:fwrite("Number of iterations per actor = ~w~n",[NumIterPerActor]),
lists:seq(1, X),
spawn(pi, montecarlo, [NumIterPerActor]).
montecarlo(N)->
montecarlo(N,0,0).
montecarlo(0,InCircle,NumPoints)->
PiEst = 4*InCircle / NumPoints,
io:fwrite("Pi = ~w~n", [PiEst]);
montecarlo(N,InCircle,NumPoints)->
Xcoord = rand:uniform(),
Ycoord = rand:uniform(),
montecarlo(N-1,if Xcoord*Xcoord + Ycoord*Ycoord < 1 -> InCircle + 1; true -> InCircle end,NumPoints + 1).
通过研究问题,我看到了使用 map(),但据我了解,您是在 map() 内部创建一个新函数,而不是使用已经实现的函数。
编辑:
在截止日期之前没有收到任何建议(我的错等了这么久),所以我寻求同学的帮助并想出了一个递归的解决方案:
-module(pi).
-export([montecarlo/1, montecarlo/2, assign/3, compute/2, addPi/3]).
montecarlo(N, X)->
NumIterPerActor = N div X, %number of iterations for each actor
io:fwrite("Number of actors = ~w~n",[X]),
io:fwrite("Number of iterations per actor = ~w~n",[NumIterPerActor]),
%Actors = lists:seq(1, X), %generate desired number of actors
%Pi1 = spawn(pi, montecarlo, [NumIterPerActor]),
%Pi2 = spawn(pi, montecarlo, [NumIterPerActor]).
ReceiverID = spawn(pi, addPi, [0, X, X]),
assign(ReceiverID, X, NumIterPerActor).
assign(ReceiverID, 1, Iter)->
spawn(pi, compute, [Iter, ReceiverID]);
assign(ReceiverID, X, Iter)->
spawn(pi, compute, [Iter, ReceiverID]),
assign(ReceiverID, X-1, Iter).
compute(Iter, ReceiverID)->
Est = montecarlo(Iter),
ReceiverID ! {Est}.
addPi(Pies, X, 0)->
FinalPi = Pies/X,
io:fwrite("Pi = ~w~n", [FinalPi]);
addPi(Pies, X, Rem)->
receive
{Estimate} ->
addPi(Pies+Estimate, X, Rem-1)
end.
montecarlo(N)->
montecarlo(N,0,0).
montecarlo(0,InCircle,NumPoints)->
4*InCircle / NumPoints;
%io:fwrite("Pi = ~w~n", [PiEst]);
montecarlo(N,InCircle,NumPoints)->
Xcoord = rand:uniform(),
Ycoord = rand:uniform(),
montecarlo(N-1,if Xcoord*Xcoord + Ycoord*Ycoord < 1 -> InCircle + 1; true -> InCircle end,NumPoints + 1).
lists:foreach/2
似乎很适合当您需要迭代地做一些有副作用的事情时,例如生成一个进程。
您可以 map/2
将 NumIterPerActor
放入 IterList
列表中,并使用 foreach/2
在其上迭代生成进程。
montecarlo(N, X) ->
NumIterPerActor = N div X,
IterList = lists:map(fun(_) -> NumIterPerActor end, lists:seq(1, X)),
lists:foreach(fun(IPA) -> spawn(?MODULE, montecarlo, [IPA]) end, IterList).
要回答你的最后一个问题,请注意在 map/2
或 foreach/2
中你可以将任何函数调用包装在 lambda 函数中并将相关参数传递给它。
对于一项作业,我需要使用 Montecarlo 方法在 Erlang 中估计 pi,但具有指定数量的参与者和迭代。我有一个不使用并发的工作版本(改编自 https://programmingpraxis.com/2009/10/09/calculating-pi/),因此采用一个参数,N = 迭代次数(点)。我试图通过创建另一个带有两个参数的 montecarlo() 函数来添加它,N = # of iterations 和 X = # of actors。我无法弄清楚如何使用(伪)循环来生成每个演员。
之前的版本 return 是 pi 估计,但在我弄清楚产卵之后,我假设我必须对每个演员的 return 值进行平均,以获得最终的 pi 估计。
这是我的资料:
-module(pi).
-export([montecarlo/1, montecarlo/2]).
montecarlo(N, X)->
NumIterPerActor = N div X,
%io:fwrite("Number of actors = ~w~n",[X]),
%io:fwrite("Number of iterations per actor = ~w~n",[NumIterPerActor]),
lists:seq(1, X),
spawn(pi, montecarlo, [NumIterPerActor]).
montecarlo(N)->
montecarlo(N,0,0).
montecarlo(0,InCircle,NumPoints)->
PiEst = 4*InCircle / NumPoints,
io:fwrite("Pi = ~w~n", [PiEst]);
montecarlo(N,InCircle,NumPoints)->
Xcoord = rand:uniform(),
Ycoord = rand:uniform(),
montecarlo(N-1,if Xcoord*Xcoord + Ycoord*Ycoord < 1 -> InCircle + 1; true -> InCircle end,NumPoints + 1).
通过研究问题,我看到了使用 map(),但据我了解,您是在 map() 内部创建一个新函数,而不是使用已经实现的函数。
编辑:
在截止日期之前没有收到任何建议(我的错等了这么久),所以我寻求同学的帮助并想出了一个递归的解决方案:
-module(pi).
-export([montecarlo/1, montecarlo/2, assign/3, compute/2, addPi/3]).
montecarlo(N, X)->
NumIterPerActor = N div X, %number of iterations for each actor
io:fwrite("Number of actors = ~w~n",[X]),
io:fwrite("Number of iterations per actor = ~w~n",[NumIterPerActor]),
%Actors = lists:seq(1, X), %generate desired number of actors
%Pi1 = spawn(pi, montecarlo, [NumIterPerActor]),
%Pi2 = spawn(pi, montecarlo, [NumIterPerActor]).
ReceiverID = spawn(pi, addPi, [0, X, X]),
assign(ReceiverID, X, NumIterPerActor).
assign(ReceiverID, 1, Iter)->
spawn(pi, compute, [Iter, ReceiverID]);
assign(ReceiverID, X, Iter)->
spawn(pi, compute, [Iter, ReceiverID]),
assign(ReceiverID, X-1, Iter).
compute(Iter, ReceiverID)->
Est = montecarlo(Iter),
ReceiverID ! {Est}.
addPi(Pies, X, 0)->
FinalPi = Pies/X,
io:fwrite("Pi = ~w~n", [FinalPi]);
addPi(Pies, X, Rem)->
receive
{Estimate} ->
addPi(Pies+Estimate, X, Rem-1)
end.
montecarlo(N)->
montecarlo(N,0,0).
montecarlo(0,InCircle,NumPoints)->
4*InCircle / NumPoints;
%io:fwrite("Pi = ~w~n", [PiEst]);
montecarlo(N,InCircle,NumPoints)->
Xcoord = rand:uniform(),
Ycoord = rand:uniform(),
montecarlo(N-1,if Xcoord*Xcoord + Ycoord*Ycoord < 1 -> InCircle + 1; true -> InCircle end,NumPoints + 1).
lists:foreach/2
似乎很适合当您需要迭代地做一些有副作用的事情时,例如生成一个进程。
您可以 map/2
将 NumIterPerActor
放入 IterList
列表中,并使用 foreach/2
在其上迭代生成进程。
montecarlo(N, X) ->
NumIterPerActor = N div X,
IterList = lists:map(fun(_) -> NumIterPerActor end, lists:seq(1, X)),
lists:foreach(fun(IPA) -> spawn(?MODULE, montecarlo, [IPA]) end, IterList).
要回答你的最后一个问题,请注意在 map/2
或 foreach/2
中你可以将任何函数调用包装在 lambda 函数中并将相关参数传递给它。