我程序的一部分要求我能够随机洗牌列表元素。我需要一个函数,当我给它一个列表时,它将伪随机地重新排列列表中的元素。
每次调用时,安排的变化必须相同的清单。
我的实现似乎工作得很好,但我觉得它相当长,并且正在增加我的代码库,而且,我觉得它不是这样做的最佳解决方案。所以我需要一个更短的实现。这是我的实施:
-module(shuffle). -export([list/1]). -define(RAND(X),random:uniform(X)). -define(TUPLE(Y,Z,E),erlang:make_tuple(Y,Z,E)). list(L)-> Len = length(L), Nums = lists:seq(1,Len), tuple_to_list(?TUPLE(Len,[],shuffle(Nums,L,[]))). shuffle([],_,Buffer)-> Buffer; shuffle(Nums,[Head|Items],Buffer)-> {Pos,NewNums} = pick_position(Nums), shuffle(NewNums,Items,[{Pos,Head}|Buffer]). pick_position([N])-> {N,[]}; pick_position(Nos)-> T = lists:max(Nos), pick(Nos,T). pick(From,Max)-> random:seed(begin (case random:seed(now()) of undefined -> NN = element(3,now()), {?RAND(NN),?RAND(NN),?RAND(NN)}; Any -> Any end) end ), T2 = random:uniform(Max), case lists:member(T2,From) of false -> pick(From,Max); true -> {T2,From -- [T2]} end.
在shell中运行它:
F:\> erl Eshell V5.8.4 (abort with ^G) 1> c(shuffle). {ok,shuffle} 2> shuffle:list([a,b,c,d,e]). [c,b,a,e,d] 3> shuffle:list([a,b,c,d,e]). [e,c,b,d,a] 4> shuffle:list([a,b,c,d,e]). [a,b,c,e,d] 5> shuffle:list([a,b,c,d,e]). [b,c,a,d,e] 6> shuffle:list([a,b,c,d,e]). [c,e,d,b,a]我的动机是在 STDLIB 中没有这样的功能。在我游戏的某个地方,我需要洗牌,我还需要找到解决问题的最有效方法,而不仅仅是一个有效的解决方案。
答案 0 :(得分:70)
1> L = lists:seq(1,10).
[1,2,3,4,5,6,7,8,9,10]
通过制作元组{R,X}的列表,将随机数R与L中的每个元素X相关联。对此列表进行排序并解压缩元组以获得洗牌版本的L.
1> [X||{_,X} <- lists:sort([ {random:uniform(), N} || N <- L])].
[1,6,2,10,5,7,9,3,8,4]
2>
答案 1 :(得分:4)
请注意karl's answer更加简洁明了。
这是一个相当简单的解决方案,虽然不一定是最有效的方法:
-module(shuffle).
-export([list/1]).
list([]) -> [];
list([Elem]) -> [Elem];
list(List) -> list(List, length(List), []).
list([], 0, Result) ->
Result;
list(List, Len, Result) ->
{Elem, Rest} = nth_rest(random:uniform(Len), List),
list(Rest, Len - 1, [Elem|Result]).
nth_rest(N, List) -> nth_rest(N, List, []).
nth_rest(1, [E|List], Prefix) -> {E, Prefix ++ List};
nth_rest(N, [E|List], Prefix) -> nth_rest(N - 1, List, [E|Prefix]).
例如,人们可能会取消++
中的nth_rest/3
操作。您不需要在每次调用random
时播种随机算法。在您启动程序时最初将其播种,如下所示:random:seed(now())
。如果您为每次调用uniform/1
而播种,那么您的搜索结果就会出现偏差(尝试使用[shuffle:list([1,2,3]) || _ <- lists:seq(1, 100)]
)。
答案 2 :(得分:1)
-module(shuffle).
-compile(export_all).
shuffle(L) ->
shuffle(list_to_tuple(L), length(L)).
shuffle(T, 0)->
tuple_to_list(T);
shuffle(T, Len)->
Rand = random:uniform(Len),
A = element(Len, T),
B = element(Rand, T),
T1 = setelement(Len, T, B),
T2 = setelement(Rand, T1, A),
shuffle(T2, Len - 1).
主() - &GT; shuffle(列表:seq(1,10))。