我的原始代码在这里..
function AddNumStrings(Str1, Str2: string): string;
var
i: integer;
carryStr: string;
worker: integer;
workerStr: string;
begin
Result:= inttostr(length(Str1));
Result:= '';
carryStr:= '0';
// make numbers the same length
while length(Str1) < length(Str2) do Str1:= '0' + Str1;
while length(Str1) > length(Str2) do Str2:= '0' + Str2;
i:= 0;
while i < length(Str1) do begin
worker:= strtoint(copy(Str1, length(Str1) - i, 1)) + strtoint(copy(Str2, length(Str2) - i, 1)) + strtoint(carryStr);
if worker > 9 then begin
workerStr:= inttostr(worker);
carryStr:= copy(workerStr, 1, 1);
Result:= copy(workerStr, 2, 1) + Result;
end else begin
Result:= inttostr(worker) + Result;
carryStr:= '0';
end;
inc(i);
end; { while }
if carryStr <> '0' then Result:= carryStr + Result;
Application.ProcessMessages;
end;
function yeni(s1, s2: string): string;
var
j, i, k: integer;
c: char;
s: string;
begin
k:= length(s2);
j:= length(s1) - length(s2);
for i:= j downto 1 do begin
c:= s1[i];
k:= k + 1;
if (c <> '9') and (c <> '0') then begin
s:= copy(s1, i, k);
s:= AddNumStrings(s, s2);
Setlength(s1, i - 1);
Result:= s1 + s;
break;
end;
Application.ProcessMessages;
end;
procedure TForm1.Button13Click(Sender: TObject);
var
i, k: integer;
s: Ansistring;
begin
s:= '1111';
for i:= 1 to 1000000 do begin
for k:= 1 to 120 do begin
s:= yeni(s, '4');
end;
end;
end;
end.
最后我的字符串s
必须是1,000,000,000长度。但我想我需要68天。
我怎样才能加快这段代码的速度?
答案 0 :(得分:5)
您的代码对Shlemiel The Painter's Algorithm的变体感到窒息。每次你像这样添加两个字符串时,Delphi RTL必须增加字符串的大小,这可能涉及分配一个新的,更大的字符串并复制旧字符串,然后释放旧字符串。当你这样做1000万次时,所有的内存复制和重新分配都会加起来。
要让它快速运行,您需要做的是预先确定完成的字符串的长度,然后在SetLength
上调用s
并从头开始分配大字符串。之后,跟踪尚未“填写”的最高char的索引。您的每个IntToStr
调用都会生成一个新字符串;插入|
字符,然后复制IntToStr
结果,随时更新索引。这应该会大大减少运行此代码所需的时间。
答案 1 :(得分:3)
您正在尝试使用字符串进行任意精度算术 这是一个坏主意。
请改用bigint库。 它使用整数数组来完成这项工作 一个不错的可下载:http://www.delphiforfun.org/Programs/Library/big_integers.htm
或根据LURD的建议:https://code.google.com/p/gmp-wrapper-for-delphi/
答案 2 :(得分:1)
显然这是一个愚蠢的例子,但你可以做一些事情:
让我详细说明
了解调用Delphi函数时会发生什么
当您调用RTL函数时,例如string:= string + someotherstring
Delphi将执行concat
操作。这包括调整字符串分配的大小(如果需要)并将字符串(如果需要)移动到内存中的新位置
如果您多次执行此操作,则所有存储分配将开始累计。事实上,你将功能加倍到20,000,000次运行,你会发现运行时间超过了两倍
所以你要避免这种废话并将那些昂贵的功能移出循环。
了解您的数据
只要s[index] = 0
,任何乘法都将为0.您可以使用此知识来加速很多。有人称这种作弊行为;在这种情况下可能是真的,但在更复杂的情况下,知道你的数据将会做什么可以让你获得非常好的速度
此外,你知道将单个数字乘以9会产生结果&gt; 81所以你不需要检查超过2个字符。
将内容移出循环
一旦你知道你的数据,就可以把东西移出循环,所以不要多次这样做,你只需要做一次
即使循环外的东西比循环内的东西长了10,000倍。这仍然是一个很好的协议。
除非可预测,否则不要跳
在这种情况下,这不适用,因为该函数在输出稳定的零流时会很快稳定下来,但无论如何我都将其用于演示目的。
事实上,此处if
将比没有if
更快,因为跳转会破坏依赖关系链。
代码示例
procedure TForm1.Button4Click(Sender: TObject);
const
NumberOfCharsForKToSettleTo0 = 10;
ManyTimes = 10 * 1000 * 1000
var
i,j,k,l:integer;
s:string;
begin
//preallocate the length of the string
SetLength(s, 4 + (ManyTimes * length('|1')) + NumberOfCharsForKToSettleTo0);
s:='1369';
l:= 4;
for i:=1 to ManyTimes do begin
j:=i mod 9;
//k:=strtoint(s[Length(s)])*j; don't use strtoint
k:= ord(s[l]) - ord('0');
k:= k*j;
//s:=s+'|'+inttostr(k);
inc(l);
s[l]:= '|';
inc(l);
s[l]:= chr(k div 10+ord('0'));
inc(l,integer(k>9)); //avoid jumps/ifs that are hard to predict.
s[l]:= chr(k+ord('0'));
end;
end;