Question

I want to create a function get_id(max_length). At first want to math:pow/2, but it return float data type. It seems not a good idea.

with code as follows, but only support max length=20, as it is hardcoded, any good idea?

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).
Was it helpful?

Solution

Your approach, unfortunately, doesn't work. Indeed, while random:uniform/1 accepts any positive integer as its argument, it does not deliver a random integer uniformly distributed between 1 and N for very large values of N (in spite of what documentation claims).

The reason is that random:uniform/1 is actually truncating the product of its argument by the value of random:uniform/0 (and adding 1 for [1-N] range instead of [0-(N-1)]).

See source code: https://github.com/erlang/otp/blob/maint/lib/stdlib/src/random.erl#L112

And floats are IEEE 754 doubles with 53 bits mantissa, which means that get_id/1 will not return all possible values for input from 17 to 20 (with 16 or more digits).

random:uniform/0,1 is known as a poor random generator, mostly suitable if you want to generate reproductible pseudo-random series (a given seed value will always generate the same series). For this reason, I would suggest using crypto:rand_uniform/2.

A simple solution would be to compute 10^(N-1) using integer arithmetics (to avoid the 53 bits mantissa issue) and then call crypto:rand_uniform/2. You can perform this with a naive recursive implementation (pow1/1 below), or use binary exponentiation (pow2/1 below).

-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).

Your function could simply be written as:

-spec get_id2(pos_integer()) -> non_neg_integer().
get_id2(N) ->
    1 + crypto:rand_uniform(0, pow2(N - 1)).

Alternatively, you could use a combination of uniform random variables, one per digit (while the sum of two random uniform variables is generally not a uniform random variable, it is if combined like this) or for several digits in the case of the binary exponentiation.

With the naive exponentiation:

-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).

With the binary exponentiation:

-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).

OTHER TIPS

Why not use trunc/1 to cast the floats returned by math:pow/2 to integers? http://www.erlang.org/doc/man/erlang.html#trunc-1

like in any language, you can get a power of 2 by shifting left the number 1:

1> 1 bsl 3.
8
2> 1 bsl 8.
256
3> 1 bsl 852.
30030067315218800919884630782037027445247038374198014146711597563050526250476926831789640794321325523394216076738821850476730762665208973047045843626559620640158907690363610309346513399556581649279919071671610504617321356178738468477058455548958390664298496
4>

As you can see, the size of integer is not limited in erlang. It is both good and bad since small integer (that is integer represented as a single worg like in most languages) are limited depending on the architecture:

  • On 32-bit architectures: -134217729 < i < 134217728 (28 bits)
  • On 64-bit architectures: -576460752303423489 < i < 576460752303423488 (60 bits)

for bigger integer, another representation is used: big integer, that takes more space in memory and take longer to compute.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top