-module (psrs).
-export ([start/2,handle/1]).
% 测试数据:psrs:start([15,46,48,93,39,6,72,91,14,36,69,40,89,61,97,12,21,54,53,97,84,58,32,27,33,72,20],3).
% 将数据分给各个进程,并创建
nodecreater([],Pids) -> Pids;
nodecreater([Hlist|TLists],Pids) ->
Pid = spawn(?MODULE,handle,[Hlist]),
nodecreater(TLists,[Pid|Pids]).
% 将列表Data 分成M份均匀的子列表
divList(Data,M) ->
Len = lists:flatlength(Data),
PerLen = Len div M,
dividing(Data,PerLen,[]).
dividing(Data,PerLen,T) ->
Sublist = lists:sublist(Data, PerLen),
Len = lists:flatlength(Data),
if
PerLen < Len ->
NewData = lists:sublist(Data,PerLen+1,lists:flatlength(Data)-PerLen),
dividing(NewData,PerLen,[Sublist|T]);
true ->
[Sublist|T]
end.
% 将进程号列表 告知 所有进程
sendpids(Pids,[H]) -> H ! {pids,Pids};
sendpids(Pids,[H|T]) ->
H ! {pids,Pids},
sendpids(Pids,T).
% 对数据进行采样
getsample(MyPart,M) ->
Len = lists:flatlength(MyPart),
PerLen = Len div M,
getsamples(MyPart,PerLen,[]).
getsamples(MyPart,PerLen,T) ->
[H|_T] = MyPart,
Len = lists:flatlength(MyPart),
if
PerLen < Len ->
NewData = lists:nthtail(PerLen,MyPart),
getsamples(NewData,PerLen,[H|T]);
true ->
[H|T]
end.
% 接收其他进程的样本
receivesample(0,Sample) -> Sample;
receivesample(M,Sample) ->
receive
{sample,OtherSample} ->
receivesample(M-1,lists:append(Sample,OtherSample))
end.
% 从选出的样本中找到新的样本
finditem(Data,M) ->
Len = lists:flatlength(Data),
PerLen = Len div (M+1),
NewData = lists:sublist(Data,PerLen+1,Len-PerLen),
finditems(NewData,PerLen,[]).
finditems(Data,PerLen,T) ->
[H|_T] = lists:sublist(Data,PerLen),
Len = lists:flatlength(Data),
if
PerLen < Len ->
NewData = lists:sublist(Data,PerLen+1,Len-PerLen),
finditems(NewData,PerLen,[H|T]);
true ->
[H|T]
end.
% 告知其他进程主元
sendItems(_Items,[]) -> sendItemsdone;
sendItems(Items,[Hid|Pids]) ->
Hid ! {masteritems , Items},
sendItems(Items,Pids).
% 按主元进行划分
partlist(MyPart,Items) ->
parting(MyPart,Items,[]).
parting(LastPart,[],T) -> [LastPart|T];
parting(MyPart,[H|Items],T) ->
{NewPart,LastPart} = {[X || X <- MyPart , X =< H],[X || X <- MyPart , X > H]},
parting(LastPart,Items,[NewPart|T]).
% 全局交换列表,分成两步先发送完自己持有的列表,然后再开始接收
swap(Lists,Pids) ->
swapsend(Lists,lists:reverse(Pids)),
swapreceive(lists:flatlength(Pids),[]).
% 发送: 逐个取出各个列表和各个pid ,将对应列表发给对应进程
swapsend([],[]) -> swapdone;
swapsend([Hlist|NewpartList],[Hids|Pids]) ->
Hids ! {otherlist,Hlist},
swapsend(NewpartList,Pids).
% 接收
swapreceive(0,R) -> R;
swapreceive(M,R) ->
receive
{otherlist,Otherlist} ->
swapreceive(M-1,[Otherlist|R])
end.
% 对排好序的小列表的列表 进行归并排序
merge_sort(SortedLists) ->
NewList = listsappend(SortedLists,[]),
lists:sort(NewList).
listsappend([],R) -> R;
listsappend([Hlist|SortedLists],R) ->
listsappend(SortedLists,lists:append(R,Hlist)).
% 逐次给辅助进程发送输出信号
merge_inorder([],R) ->R;
merge_inorder([Hid|Pids],R) ->
receive
{result,Hid,Res} ->
merge_inorder(Pids,lists:append(R,Res))
end.
% 主进程 与辅助进程的区别 主要在采样 是由主进程做,以及其他协调工作
start(Data,M) ->
% 均匀划分
[FirstPart|PartLists] = lists:reverse(divList(Data,M)),
%io:format("~w~n",[FirstPart|PartLists]),
% 创建其他辅助进程
Pids = nodecreater(PartLists,[]),
% 使全部进程都知道其他进程的进程号,后来的全局交换需要
sendpids([self()|Pids],Pids),
SortList = lists:sort(FirstPart),
Sample = getsample(SortList,M),
% 接收其他进程的样本,排序选出主元
NewSample = receivesample(M-1,Sample),
SortSample = lists:sort(NewSample),
Items = lists:reverse(finditem(SortSample,M-1)),
% 告知其他进程主元
sendItems(Items,Pids),
% 按主元进行划分
NewpartList = partlist(FirstPart,Items),
% 全局交换
SortLists = swap(NewpartList,[self()|Pids]),
NewSortList = merge_sort(SortLists),
Result = merge_inorder(Pids,NewSortList),
io:format("~w~n",[Result]).
% 辅助进程
handle(MyPart) ->
SortList = lists:sort(MyPart),
% 接受其他进程的Pids
receive
{pids,PPids} ->
[Master|_Pids] = PPids
end,
M = lists:flatlength(PPids),
% 找出样本
Sample = getsample(SortList,M),
% 向主进程发送样本
Master ! {sample,Sample},
% 接收主进程筛选出的主元
receive
{masteritems,Items} ->
Items
end,
NewpartList = partlist(MyPart,Items),
% 全局交换
SortLists = swap(NewpartList,PPids),
% 归并排序
NewSortList = merge_sort(SortLists),
Master ! {result,self(),NewSortList}.
erlang 实现psrs 并行排序算法
最新推荐文章于 2021-04-29 15:55:00 发布