我想创建一个函数get_id(max_length)。起初想要数学:pow / 2,但它返回浮点数据类型。这似乎不是一个好主意。
代码如下,但只支持max length = 20,因为它是硬编码的,有什么好主意吗?
seed()->
{M_a,M_b,M_c} = now(),
random:seed(M_a,M_b,M_c),
ok.
get_id(1)-> random:uniform(1);
get_id(2) -> random:uniform(10);
get_id(3) -> random:uniform(100);
get_id(4) -> random:uniform(1000);
get_id(5) -> random:uniform(10000);
get_id(6) -> random:uniform(100000);
get_id(7) -> random:uniform(1000000);
get_id(8) -> random:uniform(10000000);
get_id(9) -> random:uniform(100000000);
get_id(10) -> random:uniform(1000000000);
get_id(11) -> random:uniform(10000000000);
get_id(12) -> random:uniform(100000000000);
get_id(13) -> random:uniform(1000000000000);
get_id(14) -> random:uniform(10000000000000);
get_id(15) -> random:uniform(100000000000000);
get_id(16) -> random:uniform(1000000000000000);
get_id(17) -> random:uniform(10000000000000000);
get_id(18) -> random:uniform(100000000000000000);
get_id(19) -> random:uniform(1000000000000000000);
get_id(20) -> random:uniform(10000000000000000000).
答案 0 :(得分:4)
遗憾的是,您的方法不起作用。实际上,虽然random:uniform/1
接受任何正整数作为其参数,但它不会为非常大的N值提供在1和N之间均匀分布的随机整数(尽管有文档声明)。
原因是random:uniform/1
实际上是通过random:uniform/0
的值截断其参数的乘积(并且为[1-N]范围而不是[0-(N-1)加1) ])。
请参阅源代码:https://github.com/erlang/otp/blob/maint/lib/stdlib/src/random.erl#L112
浮点数是IEEE 754双倍,53位尾数,这意味着get_id/1
不会返回从17到20(16位或更多位数)输入的所有可能值。
random:uniform/0,1
被称为差的随机生成器,如果您想生成可再现的伪随机序列(给定的种子值将始终生成相同的序列),则大多适合。出于这个原因,我建议使用crypto:rand_uniform / 2。
一个简单的解决方案是使用整数算术计算10^(N-1)
(以避免53位尾数问题),然后调用crypto:rand_uniform/2
。您可以使用简单的递归实现(下面为pow1/1
)执行此操作,或使用二进制取幂(下面为pow2/1
)。
-define(BASE, 10).
-spec pow1(non_neg_integer()) -> pos_integer().
pow1(N) when N >= 0 ->
pow1(N, 1).
pow1(0, Acc) -> Acc;
pow1(N, Acc) ->
pow1(N - 1, Acc * ?BASE).
-spec pow2(non_neg_integer()) -> pos_integer().
pow2(N) when N >= 0 ->
pow2(?BASE, N, 1).
pow2(_X, 0, Acc) ->
Acc;
pow2(X, N, Acc) when N rem 2 =:= 0 ->
pow2(X * X, N div 2, Acc);
pow2(X, N, Acc) ->
pow2(X * X, N div 2, Acc * X).
您的功能可以简单地写成:
-spec get_id2(pos_integer()) -> non_neg_integer().
get_id2(N) ->
1 + crypto:rand_uniform(0, pow2(N - 1)).
或者,您可以使用统一随机变量的组合,每个数字一个(而两个随机统一变量的总和通常不是一个统一的随机变量,如果这样组合)或者在几个数字的情况下二进制取幂。
随着天真的指数化:
-spec get_id3(pos_integer()) -> pos_integer().
get_id3(N) when N > 0 ->
get_id3(N - 1, 0).
get_id3(0, Acc) -> 1 + Acc;
get_id3(N, Acc) ->
Acc1 = crypto:rand_uniform(0, ?BASE) + (Acc * ?BASE),
get_id3(N - 1, Acc1).
使用二进制求幂:
-spec get_id4(pos_integer()) -> pos_integer().
get_id4(N) when N > 0 ->
get_id4(?BASE, N - 1, 0).
get_id4(_X, 0, Acc) ->
1 + Acc;
get_id4(X, N, Acc) when N rem 2 =:= 0 ->
get_id4(X * X, N div 2, Acc);
get_id4(X, N, Acc) ->
Acc1 = crypto:rand_uniform(0, X) + (Acc * X),
get_id4(X * X, N div 2, Acc1).
答案 1 :(得分:1)
为什么不使用trunc/1
将math:pow/2
返回的浮点数转换为整数? http://www.erlang.org/doc/man/erlang.html#trunc-1
答案 2 :(得分:1)
就像在任何语言中一样,你可以通过左移数字1得到2的幂:
1> 1 bsl 3.
8
2> 1 bsl 8.
256
3> 1 bsl 852.
30030067315218800919884630782037027445247038374198014146711597563050526250476926831789640794321325523394216076738821850476730762665208973047045843626559620640158907690363610309346513399556581649279919071671610504617321356178738468477058455548958390664298496
4>
如您所见,整数的大小不受erlang的限制。它既好又坏,因为小整数(在大多数语言中表示为单个worg的整数)受限于架构:
对于更大的整数,使用另一种表示:大整数,在内存中占用更多空间并花费更长时间来计算。