如何获得一个限制长度的随机整数?

时间:2014-05-11 01:07:16

标签: erlang

我想创建一个函数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).

3 个答案:

答案 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/1math: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的整数)受限于架构:

  • 在32位体系结构上:-134217729<我< 134217728(28位)
  • 在64位体系结构上:-576460752303423489<我< 576460752303423488(60位)

对于更大的整数,使用另一种表示:大整数,在内存中占用更多空间并花费更长时间来计算。