使用中间列表没有用,位串不是直接转换成整数所以我认为它也没有用,所以最好的解决方案是直接使用大数字。
假设您要创建一个整数,该整数通过重复 N 次宽度为 Width 的模式以十进制表示:
使用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 分钟)并且呈指数增长。
减少这种情况的想法是成倍地减少要执行的操作数量。这个更智能的版本使用的模式在每次迭代时宽度加倍,并在需要时连接到当前结果(我使用了 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