在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的内存问题(尽管我不确定如何实现任意精度整数)二郎)。
我有办法做到这一点吗?
答案 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 = 3
,Pattern = 52
,Width = 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