【问题标题】:Erlang; form massive repeating integer二郎;形成大量重复整数
【发布时间】:2016-03-01 21:51:08
【问题描述】:

在 Erlang 中,有没有办法在没有列表的情况下形成大量重复整数?

我写道:

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

形成一个由 100 个 9 组成的列表,然后是整数 999....(100 个 9)。但是,如果我想形成一个包含十亿个 9 的列表,这将失败:

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

(永远不会结束)。

列表方面,10 亿个整数列表的内存占用应该约为 4GB,因此保存单个大容量 int 不应该是内存问题(尽管我不确定在 Erlang 中如何实现任意精度整数) .

有什么办法可以做到吗?

【问题讨论】:

  • 你为什么要这样做?
  • @SteveVinoski 我后来意识到这是解决我的问题的错误方法,但话虽如此,我仍然想知道如何做我想做的事情。所以除了“好奇”之外,我不再有充分的理由......

标签: erlang


【解决方案1】:

您不能真正创建这么长的列表然后尝试将其转换为整数。 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 算法创建大数的算法的问题,然后在此处询问此类算法的具体实现。

【讨论】:

    【解决方案2】:

    使用中间列表没有用,位串不是直接转换成整数所以我认为它也没有用,所以最好的解决方案是直接使用大数字。 假设您要创建一个整数,该整数通过重复 N 次宽度为 Width 的模式以十进制表示:

    使用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 分钟)并且呈指数增长。

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

    -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
    

    【讨论】:

    • 很好,它肯定比我的(天真)示例更聪明,但我不想故意让它更聪明。我只想举一些例子,因为这样的算法可以通过数百种方式实现。也许 Erlang VM 会比将大数乘以大数更快地转换由 9 组成的巨大二进制文件?也许有一种特定的数学算法比你更聪明的实现还要快?也许更简单的方法是构造一个 .erl 文件,其中大数已经用 erlc 编译为整数?
    • 我同意,这就是为什么它没有被命名为“smartest”:o)
    猜你喜欢
    • 2021-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-13
    • 2010-11-02
    • 2010-10-02
    • 2011-03-06
    相关资源
    最近更新 更多