不得不说用 erlang 写一些算法实现是给自己找抽。。。
但游戏服务端选择使用erlang必能在其他方面占据优势,而我们能做便是把牺牲降低到最小
需求:长度N的列表中,随机出M个不同的数值。
因为游戏中业务基数量极小,一般是100个物品中随机10出来,所以使用最暴力的方法反而是最快...切勿使用复杂的数据结构搞...切记
%% List 中选取 N 个不同的对象
rand_n(N, List) when is_list(List) ->
%% Len = length(List),
%% Ns = rand_n(N, 1, Len),
%% [lists:nth(Index, List) || Index <- Ns];
rand_n(N, list_to_tuple(List));
rand_n(N, Tuple) when is_tuple(Tuple) ->
Len = tuple_size(Tuple),
Ns = rand_n1(N, 1, Len),
[element(Index, Tuple) || Index <- Ns].
%% 从[min , max] 中取出 N个数,不重复
rand_n1(Count, Min, Max)
when (Max - Min)+1 > Count->
rand_n2(Count, Min, Max, []);
rand_n1(_Count, Min, Max) ->
shuffle(lists:seq(Min, Max)).
rand_n2(0, _Min, _Max, List) ->
List;
rand_n2(Count, Min, Max, List) ->
Num = rand(Min, Max),
case lists:member(Num, List) of
false->
rand_n2(Count - 1, Min, Max, [Num|List]);
true ->
rand_n2(Count, Min, Max, List)
end.
shuffle(L) ->
List1 = [{rand(1, 10000000), X} || X <- L],
List2 = lists:keysort(1, List1),
[E || {_, E} <- List2].
以下方案弄巧成捉了。。。
网络上关乎随机算法就不介绍了,我使用也是网络上泛用的两个随机算法结合,互补优缺点。
算法一:每次随机出一个值放到容器中,随机出的数,先去容器中找是否存在,有则抛弃并再次重复随机,无则存入容器
算法二:分两个容器A、B,每次在A中随机出一个数Num,A中删除Num, 并将Num放进B中
明显算法二比算法一好,当能C、C++写最好,但是若是基于Erlang来做,算法二的牺牲会超过算法一,所以跑测试用例
时便能找出猫腻。
结论: 当 M div N < 0.6, 算法一占有优势,否则算法二
(可以自己测)测试数据对比:
算法一:
M = 99999, N = 100000 T = 1925383(微妙)
M = 80000, N = 100000 T = 394174(微妙)
M = 10000, N = 100000 T = 36135(微妙)
算法二:
M = 99999, N = 100000 T = 350859(微妙)
M = 80000, N = 100000 T = 288753(微妙)
M = 10000, N = 100000 T = 53911(微妙)
%% ------------------------------------------------------
rand_n(N, List) ->
Len = length(List),
if
N/Len > 0.6 ->
Ns = rand_n1(N, 1, Len, List);
true ->
NewNs = rand_n2(N, 1, Len),
traversal_list(NewNs, 1, List, [])
end.
%% 从[min , max] 中取出 N个数,不重复
rand_n1(Count, Min, Max, List)
when (Max - Min)+1 > Count->
{Tree, _, []} = make_tree(Min, Max, List),
rand_n1(Count, Min, Max, Tree, []).
rand_n1(0, _Min, _Max, _Tree, List) ->
List;
rand_n1(Count, Min, Max, Tree, List) ->
Num = hmisc:rand(Min, Max),
{NTree, Val} = queue_tree(Num, Tree),
rand_n1(Count - 1, Min, Max-1, NTree, [Val | List]). %% Max - 1
% {sum, left_chail, right_chail} 线段树
make_tree(Left, Right, [H|T])
when Left =:= Right ->
{{1, none, none, H}, 1, T};
make_tree(Left, Right, List) ->
Mid = (Left + Right) bsr 1,
{LeftChail, LSum, NList} = make_tree(Left, Mid, List),
{RightChail, RSum, NewList} = make_tree(Mid+1, Right, NList),
Sum = LSum + RSum,
{{Sum, LeftChail, RightChail}, Sum, NewList}.
queue_tree(Key, {Sum, none, none, Index})
when Key =:= Sum ->
{{0, none, none, Index}, Index};
queue_tree(Key, {Sum, LeftChail, RightChail} = _Tree) ->
LSum = get_sum(LeftChail),
if
Key > LSum ->
{NewRightTree, Val} = queue_tree(Key - LSum, RightChail),
{{Sum-1, LeftChail, NewRightTree}, Val};
true ->
{NewLeftTree, Val} = queue_tree(Key, LeftChail),
{{Sum-1, NewLeftTree, RightChail}, Val}
end.
get_sum({Sum, _LeftChail, _RightChail}) ->
Sum;
get_sum({Sum, none, none, _Index}) ->
Sum.
%% 从[min , max] 中取出 N个数,不重复
rand_n2(Count, Min, Max)
when (Max - Min)+1 > Count->
rand_n2(Count, Min, Max, gb_sets:empty()). %% max 不变
rand_n2(0, _Min, _Max, GbSets) ->
gb_sets:to_list(GbSets);
rand_n2(Count, Min, Max, GbSets) ->
Num = hmisc:rand(Min, Max),
case gb_sets:is_member(Num, GbSets) of
false->
rand_n2(Count - 1, Min, Max, gb_sets:insert(Num, GbSets));
true ->
rand_n2(Count, Min, Max, GbSets)
end.
traversal_list([Index1 | TNs], Index2, [H|T], AccList)
when Index1 =:= Index2 ->
traversal_list(TNs, Index2+1, T, [H | AccList]);
traversal_list(Ns, Index2, [_H|T], AccList) ->
traversal_list(Ns, Index2+1, T, AccList);
traversal_list([], _, _, AccList) ->
AccList.
%% ------------------------------------------------------------------