二郎神;形成大量重复整数

时间:2016-03-01 21:51:09

标签: erlang

在Erlang中,有没有办法形成一个没有列表的大型重复整数?

我写道:

{I, _} = string:to_integer(lists:concat([9 || _ <- lists:seq(1,trunc(math:pow(10,2)))])), I.

形成一个100 9s的列表,然后是整数999 ....(100 9s)。但是,如果我想要形成一个10亿9s的数字列表,这就失败了:

{I, _} = string:to_integer(lists:concat([9 || _ <- lists:seq(1,trunc(math:pow(10,9)))])), I.

(永远不会结束)。

从列表的角度来看,10亿个整数列表的内存占用量应该大约为4GB,因此不应该成为保存单个大量int的内存问题(尽管我不确定如何实现任意精度整数)二郎)。

我有办法做到这一点吗?

2 个答案:

答案 0 :(得分:3)

您无法真正创建如此长的列表,然后尝试将其转换为整数。 Erlang中的列表是linked list,每个元素包含值和下一个元素的地址。对于64位系统,这意味着每个元素16个字节:

64位系统上的字大小:

7> erlang:system_info(wordsize).
8

因此,构建一个包含1.0e9元素的列表需要1.6e10字节或15 Gb:

13> 1.6e10 / (1024*1024*1024).
14.901161193847656

这很可能超出单个流程在大多数系统上可以分配的范围。

唯一的方法是应用某种数学公式来算法创建这样的数字。一个天真的实现只需乘以10并多次添加数字:

-module(bigint).

-export([make/2]).

make(N, A) -> make(N, A, N).

make(_N, 1, R) -> R;
make(N, A, R) -> make(N, A-1, R*10+N).

结果:

2> bigint:make(3, 5).
33333

但它随着数量的增加而呈指数级减速,所以超过几十万个数字的数字计算成本可能过于昂贵。

你也可以尝试生成二进制而不是列表,因为二进制文件在内存段中是implemented on consecutive bytes,例如:

-module(bigint).

-export([make/2]).

make(N, A) ->
    Bin = integer_to_binary(N),
    make(Bin, A, Bin).

make(_B, 1, R) -> binary_to_integer(R);
make(B, A, R) -> make(B, A-1, <<R/binary, B/binary>>).

但计算上这与前面的解决方案类似 - 可以非常快速地构建一个大二进制文件,但后来将其转换为整数在计算上是昂贵的。

您可以尝试询问有关算法的问题,以最少的步骤或Mathematic SE在算法上创建大数字,然后在此处询问此类算法的具体实现。

答案 1 :(得分:1)

使用中间列表是没用的,比特串不会直接转换为整数,所以我认为它也没有用,所以最好的解决方案是直接使用大的nums。 假设你要创建一个整数,用十进制表示法表示,重复N次宽度宽度的图案:

N = 3Pattern = 52Width = 4,预期结果为5200520052

第一个简单的实现,通过移位计算结果并添加模式N次,但是对于大N来说是非常低效的。这是一个实现(请注意,为了测量我避免打印结果的性能,因为否则shell将转换iolist中的整数):

-module (pattern).

-export ([simple/3,perf/4]).

simple(N,Pattern,Width) when is_integer(N), N > 0 ->
    simple(N,Pattern,shift(1,Width),0).

simple(0,_,_,R) -> R;
simple(X,P,Shift,R) -> simple(X-1,P,Shift,R*Shift+P).


shift(X,0) -> X;
shift(X,N) -> shift(10*X,N-1).

perf(Type,N,P,S) ->
    {T,_} = timer:tc(?MODULE,Type,[N,P,S]),
    T.
用这个你得到结果:

1> pattern:simple(7,52,3).
52052052052052052052
2> pattern:simple(7,52,2).
52525252525252
3> pattern:perf(simple,1000,5,1).    
0
4> pattern:perf(simple,10000,5,1).
63000
5> pattern:perf(simple,100000,5,1).
1810000
6> pattern:perf(simple,1000000,5,1).
309522533

时间很长(1000000分钟为5分钟)并呈指数级增长。

减少这种情况的想法是以指数方式减少要执行的操作的数量。这个更智能的版本使用的是每次迭代时宽度加倍的模式,并在需要时以当前结果连接(我在2的幂中使用N的分解):

-module (pattern).

-export ([simple/3,smarter/3,perf/4]).

simple(N,Pattern,Width) when is_integer(N), N > 0 ->
    simple(N,Pattern,shift(1,Width),0).

simple(0,_,_,R) -> R;
simple(X,P,Shift,R) -> simple(X-1,P,Shift,R*Shift+P).


shift(X,0) -> X;
shift(X,N) -> shift(10*X,N-1).

perf(Type,N,P,S) ->
    {T,_} = timer:tc(?MODULE,Type,[N,P,S]),
    T.

smarter(1,Pattern,_Width) -> Pattern;
smarter(N,Pattern,Width) when is_integer(N), N > 0 ->
    smarter(N,Pattern,shift(1,Width),0).

smarter(1,Pattern,Shift,R) -> 
    R * Shift + Pattern;
smarter(X,Pattern,Shift,R) when (X rem 2) == 0 -> 
    smarter(X div 2, Shift * Pattern + Pattern, Shift * Shift, R);
smarter(X,Pattern,Shift,R) -> 
    smarter(X div 2, Shift * Pattern + Pattern, Shift * Shift, R * Shift + Pattern).

结果要好得多:1000000的5秒,由于乘法,n * n仍在增加。

1> pattern:smarter(7,52,4).          
52005200520052005200520052
2> pattern:smarter(7,9,1). 
9999999
3> pattern:perf(smarter,1000,5,1).
0
4> pattern:perf(smarter,10000,5,1).
0
5> pattern:perf(smarter,100000,5,1).
125000
6> pattern:perf(smarter,500000,5,1). 
1279007
7> pattern:perf(smarter,1000000,5,1).
5117000

enter image description here