Current section

Files

Jump to
utility src rand1.erl
Raw

src/rand1.erl

%%----------------------------------------------------
%% @doc 随机数种子服务器
%%
%% @end
%%----------------------------------------------------
-module(rand1).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([
get_seed/0, range/2, range/1,
percent/0, permillage/0,
test/1,
split/2, split/1,
take_by_prop/2, take_by_weights_list/1,
rand_string/1
]).
-record(state, {seed}).
%% --- 系统调用 ---------------------------------
%% --- 对外接口 ---------------------------------
%% @spec get_seed() -> {integer(), integer(), integer()}
%% @doc 取得一个随机数种子
get_seed() ->
ensure_started(),
gen_server:call(?MODULE, get_seed).
%% 产生一个介于Min到Max之间的随机整数
range(Same, Same) -> Same;
range(Min, Max) ->
%% 如果没有种子,将从核心服务器中去获取一个种子,以保证不同进程都可取得不同的种子
case get('#rand_seed') of
undefined ->
RandSeed = rand1:get_seed(),
rand:seed(exs1024, RandSeed),
put('#rand_seed', RandSeed);
_ ->
skip
end,
M = Min - 1,
rand:uniform(abs(Max - M)) + M.
%% 从一个list中随机取出一项
%% null | term()
range([]) -> null;
range([I]) -> I;
range(List) ->
Len = length(List),
Index = range(1, Len),
lists:nth(Index, List).
%% 百分概率
%% @return int()
percent() ->
range(1, 100).
%% 千分概率
%% @return int()
permillage() ->
range(1, 1000).
%% 概率命中
%% @return true | false
test(Rate) ->
R = round(Rate * 100),
case range(1, 10000) of
N when N =< R ->
true;
_ ->
false
end.
%% 从一个list中随机取出N项,并返回这N项和剩余的项
%% @return {TakenList, RestList}
split(N, List) ->
split(N, List, []).
%%
split(N, List, List1) when (N > 0 andalso length(List) > 0) ->
case split(List) of
{Term, RestList} -> split(N - 1, RestList, [Term | List1]);
_ -> {List1, List}
end;
split(_N, List, List1) -> {List1, List}.
%% same as take(List, 1)
%% 从一个list中随机取出一项,并返回这个项,和剩余的项
%% @return {Term, RestList} | null
split([]) -> null;
split(List) ->
Len = length(List),
Index = range(1, Len),
take_and_rest(List, Index, []).
%%
take_and_rest(List, 1, Rest) ->
[Term | R] = List,
{Term, R ++ Rest};
take_and_rest(List, Index, Rest) ->
[H | R] = List,
Nrest = [H | Rest],
take_and_rest(R, Index - 1, Nrest).
%% @doc 从[{Item, Proportion}...]列表中按照概率抽取不重复的多个元组
%% @spec [{Item, Proportion}...], int() -> [{Item, Proportion}...]
take_by_prop(List, N) when is_list(List) andalso N > 0 ->
Length = length(List),
if
N > Length ->
[];
N =:= Length ->
List;
N < Length ->
%% TotalProp = lists:foldl(fun({_Item, Prop}, Sum) ->
%% Prop + Sum
%% end, 0, List),
take_by_prop(List, Length, N, []);
true ->
[]
end.
%% 内部函数
take_by_prop(List, Length, N, Result) when N > 0 ->
RandIndex = range(1, Length),
Elem = lists:nth(RandIndex, List),
take_by_prop(lists:delete(Elem, List), Length-1, N-1, [Elem | Result]);
take_by_prop(_List, _Length, N, Result) when N =:= 0 ->
Result.
%% 内部函数
%% 根据随机数RandIndex遍历数组,找到一个元组
%% take_one_by_prop(List, RandIndex, ArrayNth, LeftValue) ->
%% {Item, Prop} = lists:nth(ArrayNth, List),
%% RightValue = LeftValue + Prop,
%% case RandIndex > LeftValue andalso RandIndex =< RightValue of
%% true ->
%% {Item, Prop};
%% false ->
%% take_one_by_prop(List, RandIndex, ArrayNth + 1, RightValue)
%% end.
%% 权重获取列表中的序列
%% List::[{ID, Weights} | #{weights := Weights}]
%% ID::integer(), Weights::integer()
take_by_weights_list(List) ->
{TotalWeights, SerialList} = serialize_weight_list(List),
select_by_weights(TotalWeights, SerialList).
serialize_weight_list(List) ->
{T, L} = lists:foldl(fun(X, {TW, LI}) ->
{H, Acc} =
case X of
#{weights := Weight} ->
A = TW + Weight,
{X#{weights => A}, A};
{ID, Weight} ->
A = TW + Weight,
{{ID, A}, A}
end,
{Acc, [H | LI]}
end, {0, []}, List),
{T, lists:reverse(L)}.
select_by_weights(TotalWeights, List) ->
RandNumber = range(1, TotalWeights),
pick_nth(List, RandNumber).
pick_nth([X = #{weights := Weights} | T], RandNumber) ->
case RandNumber =< Weights of
true ->
X;
false ->
pick_nth(T, RandNumber)
end;
pick_nth([{Nth, Weights} | T], RandNumber) ->
case RandNumber =< Weights of
true ->
Nth;
false ->
pick_nth(T, RandNumber)
end.
rand_string(Amount) ->
rand_string(Amount, []).
rand_string(0, R) ->
R;
rand_string(Amount, R) ->
Rand =
case rand1:range(1, 62) of
Ra when Ra =< 8 ->
rand1:range("@$^&=;+-");
Ra when Ra =< 18 ->
rand1:range(49, 57);
Ra when Ra =< 42 ->
rand1:range(65, 90);
_ ->
rand1:range(97, 122)
end,
rand_string(Amount - 1, [Rand | R]).
%% --- 服务器内部实现 ---------------------------------
%% @hidden
init([]) ->
State = #state{},
{ok, State}.
%% @hidden
%% 返回一个随机数组合做为其它进程的随机数种子
handle_call(get_seed, _From, State) ->
case State#state.seed of
undefined ->
rand:seed(exs1024, erlang:timestamp());
S ->
rand:seed(exs1024, S)
end,
Seed = {rand:uniform(99999), rand:uniform(999999), rand:uniform(999999)},
{reply, Seed, State#state{seed = Seed}};
handle_call(_Request, _From, State) ->
{noreply, State}.
%% @hidden
handle_cast(_Msg, State) ->
{noreply, State}.
%% @hidden
handle_info(_Info, State) ->
{noreply, State}.
%% @hidden
terminate(_Reason, _State) ->
ok.
%% @hidden
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% -----------
%% 启动服务器
ensure_started() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).