请教该代码效能问题? ( 积分: 50 )

  • 主题发起人 主题发起人 optn3
  • 开始时间 开始时间
O

optn3

Unregistered / Unconfirmed
GUEST, unregistred user!
function TForm1.StrPos(Str:string;p:integer):string;
var
i,n:integer;
s:string;
begin
n:=0;
for i:=0 to length(str) do
begin
if copy(str,i,length(' '))=' ' then n:=n+1;
if n = p then
s := copy(str,i+2,2);
end;
StrPos:=s;
end;

function TForm1.StrPostime(Str:TStringList;P:string):integer;
var i,n:integer;
begin
n:=0;
for i:=0 to str.Count -1 do
if pos(p,str.Strings)>0 then n := n+1;
StrPostime:=n;
end;

procedure vn;
var
j,i:integer;
List :TStringList;
st1,p,m:string;
Lists: TStringList;
begin
Lists:= TStringList.Create;
randomize;
st1:='';
p:=' 02 07 24 26 32 05 17 25 28 33'; //从这里随机抽5个数.不能重复.
j:=0;
while j < 5 do //我认为慢在这里.如果能取消循环,直接取,
begin
i:= random(10)+1;
m:= form1.StrPos(p,i);
if form1.StrPostime(lists ,m) = 0 then
begin
st1 := st1+ ' '+form1.StrPos(p,i);
lists.Add(m);
j:=j+1;
end;
end;
Lists.Clear;
Lists.Free;
form1.memo1.Lines.Add(st1);
end;

上述代码本意,从字符串P中随机抽5个数.列入MEMO1
为什么老觉得慢慢,请教怎样提高代码速度.好的算法.可否贴出来.
 
delphi本来自带pos,这个代码的strpos效率更低。
循环里使用随机数和直接刷新VCL更是不明智选择
 
delphi自带的pos效率也不高。但楼主这个就更烂了。可以用网上公布的faststring单元
还有,对于不需要修改的字符串参数用传址比传值效率高,省去字符串拷贝的开销。方法为在参数前面加 const关键字。
 
那怎样才算是优秀代码?贴个看看.
 
比如您的StrPostime函数。本身pos效率不高,您还在循环中使用,呵呵,您可以试试看数千条字符串的速度。
下面是faststring单元内的两个pos函数,一个是charpos;一个是普通pos,有起点参数,可以直接把stringlist.text传过去,并分段pos。
function FastCharPos(const aSource : string; const C: Char; StartPos : Integer) : Integer;
var
L : Integer;
begin

Assert(StartPos > 0);

Result := 0;
L := Length(aSource);
if L = 0 then exit;
if StartPos > L then exit;
Dec(StartPos);
asm
PUSH EDI //Preserve this register

mov EDI, aSource //Point EDI at aSource
add EDI, StartPos
mov ECX, L //Make a note of how many chars to search through
sub ECX, StartPos
mov AL, C //and which char we want
@Loop:
cmp Al, [EDI] //compare it against the SourceString
jz @Found
inc EDI
dec ECX
jnz @Loop
jmp @NotFound
@Found:
sub EDI, aSource //EDI has been incremented, so EDI-OrigAdress = Char pos !
inc EDI
mov Result, EDI
@NotFound:

POP EDI
end;
end;

procedure MakeBMTable(Buffer: PChar; BufferLen: Integer; var JumpTable: TBMJumpTable);
begin
if BufferLen = 0 then raise Exception.Create('BufferLen is 0');
asm
push EDI
push ESI

mov EDI, JumpTable
mov EAX, BufferLen
mov ECX, $100
REPNE STOSD

mov ECX, BufferLen
mov EDI, JumpTable
mov ESI, Buffer
dec ECX
xor EAX, EAX
@@loop:
mov AL, [ESI]
lea ESI, ESI + 1
mov [EDI + EAX * 4], ECX
dec ECX
jg @@loop

pop ESI
pop EDI
end;
end;
function FastPos(const aSourceString, aFindString : string; const aSourceLen, aFindLen, StartPos : Integer) : Integer;
var
JumpTable: TBMJumpTable;
begin
//If this assert failed, it is because you passed 0 for StartPos, lowest value is 1 !!
Assert(StartPos > 0);
if aFindLen < 1 then begin
Result := 0;
exit;
end;
if aFindLen > aSourceLen then begin
Result := 0;
exit;
end;

MakeBMTable(PChar(aFindString), aFindLen, JumpTable);
Result := Integer(BMPos(PChar(aSourceString) + (StartPos - 1), PChar(aFindString),aSourceLen - (StartPos-1), aFindLen, JumpTable));
if Result > 0 then
Result := Result - Integer(@aSourceString[1]) +1;
end;
 
关于效率,给楼主两点:
1.不应该每次生成随机数后去pos,再通过pos去看是否重复。而是应该第一步就把原字符串拆解,这个操作仅做n-1次,再怎么效率低下也不成问题。
2.生成随机序列万万不能用类似
for i:=1 to n do
begin
m:=random(n)+1;
if m未重复 then
...
end;
这种算法在取前一两个数时几乎每次取都不会重复,而最后取一个数则非常痛苦——特别是要取的个数接近总数时,正确的做法是:
ArrLen:=n;
for i:=1 to n do
begin
m:=Random(ArrLen)+1;
处理Arr[m];
Arr[m]:=Arr[ArrLen];
ArrLen:=ArrLen-1;
end;
 
后退
顶部