我有一个包含10,000个条目的字符串列表。我有一个shuffle例程,但访问任何项目需要花费很多时间。浏览所有10k项目需要花费大量时间。
我想将它保存到磁盘上,然后使用其他方法对文件进行随机播放。
有什么建议吗?
答案 0 :(得分:7)
你的shuffle例程是如何实现的?特别是交换程序?如果您已经按照以下方式编写了自己的文字:
vTempSrting := vStringList[I];
vStringList.Delete(I);
vStringList.Insert(J,vTempString);
它会很慢。在stringlist上使用exchange-method。
这段代码在我平均水平(3岁)的计算机上耗时78毫秒:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,Classes,uIntegerList,Windows,Math;
procedure Shuffle(aSL : TStringList);
var I,J : integer;
begin
for I := 0 to aSL.Count-1 do
begin
J := randomrange(I,aSL.Count);
aSL.Exchange(I,J);
end;
end;
procedure CreateTestFile;
var
vSL : TStringList;
I : integer;
begin
vSL := TStringList.Create;
try
for I := 1 to 100000 do vSL.Add('Sample text #'+inttostr(I));
vSL.SaveToFile('c:\test.txt');
finally
vSL.Free;
end;
end;
function TestShuffle : longword;
var
vSL : TStringList;
vTick0 : longword;
begin
vSL := TStringList.Create;
try
vTick0 := gettickcount;
vSL.LoadFromFile('c:\test.txt');
Shuffle(vSL);
vSL.SaveToFile('c:\test.txt');
Result := gettickcount - vTick0;
finally
vSL.Free;
end;
end;
begin
CreateTestFile;
writeln(TestShuffle,' ms');
readln;
end.
答案 1 :(得分:1)
在内存中重新排列字符串列表很慢,因此我会将索引列表作为初始优化进行洗牌。
我猜你选择了stringlist是为了方便加载和保存到磁盘。一种更快捷的方法是改变索引。创建一个包含10,000个整数的数组,然后使用临时字符串变量来保存交换元素,并使用混洗索引值从上到下重新排列字符串列表。
重大改写将提供更大的改进,但如果您的字符串不是太大,这可能会有所帮助。
答案 2 :(得分:1)
一种简单的方法是生成一个随机数列表,对其进行排序,然后再进行成对的数据交换。排序可以作为o(n * log(n))算法完成,而交换总是一个o(n)算法,因此更快。
如果您没有想到它,请考虑保留数据,并保存额外的混洗索引。
答案 3 :(得分:1)
之前我问了一个关于创建一个洗牌范围的问题 - 而不是生成一个数字列表然后改组它们,我想要一个能够迭代返回一个洗牌数列表的函数,而不需要O(n)内存成本:
Generating shuffled range using a PRNG rather than shuffling
如果您在磁盘上为文件创建某种索引,那么您可以创建一个洗牌版本而无需支付内存成本,这对于非常大的文件非常重要。对于一个索引,我建议一些简单的东西,比如每行开始的位置(如32或64位整数)的平坦流。这样,要从文本文件中提取第N行,您可以简单地在索引流中寻找N * 4(或64位索引的N * 8)来发现行开始的偏移量,然后寻求该位置在文本文件流中并读出一行。
使用这种方法,您可以随机播放超大文件,而无需支付内存成本。当然,改组将意味着从源文件中随机提取行,这将不如内存中排序那么有效,除非文件非常小(几乎在第一次访问时适合缓存)或非常大(在这种情况下内存抖动)将比随机搜索更糟糕,或者如果您不使用机械硬盘(例如SSD)。
对于你的情况,10K真的不是一个大数字。在1000万行的区域中,可能会达到几千兆字节的文本(当然,取决于行长度)将更具挑战性,而且这种方法(或类似的东西)在32位中是必需的。< / p>