老问题了。随机数。有段代码,请大家看看。(100分)

  • 主题发起人 主题发起人 sgwk
  • 开始时间 开始时间
S

sgwk

Unregistered / Unconfirmed
GUEST, unregistred user!
Function GetRandomString: String;
const
Max_Len = 10;
var
i: Byte;
s: string

begin
Randomize;
s :='0123456789';
Result := '';
for i := 0 to Max_Len-1 do
Result := Result + inttostr(Random(Length(s)));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
s1: TStringList;
s:string;
a:integer;
b:integer;
begin
edit1.Text:=timetostr(time);
b:=strtoint(edit3.text);
s1:= TStringList.Create;
for a:=1 to b do
begin
s:=GetRandomString;
s1.Add(s);
end;
s1.SaveToFile('d:/ok.txt');
edit2.Text:=timetostr(time);
beep;
showmessage('完成');
end;
end.

一次产生100万条10位的数字,偶尔会出现重复。想不通,random的 关系么? 请大家讨论一下。
 
你没有真正了解randomize的原理,Randomize是以当地时间做随机种子的, 只能用一次,因为你每次调用方法时都用Randomize, 反复使用, 所以偶尔会出现重复.
[red]你可以尝试这样解决:[/red]
//在程序初始化时
begin
Application.Initialize;
[red] Randomize
//放这里非常重要[/red]
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
//然后是你的代码
[blue]Function GetRandomString: String;
const
Max_Len = 10;
var
i: Byte;
s: string

begin
Randomize;
s :='0123456789';
Result := '';
for i := 0 to Max_Len-1 do
Result := Result + inttostr([red]Random(Length(s)-1)+1[/red]);//这里最好这样
end;[/blue]
 
to: wanchi
按照你的方法改了还是如此。你可以试一下。我的环境是d7.
 
想知道有没有重复,将txt文件导入access,索引选(有(无重复))就知道了。
 
这语句 randomize 确实只需要运行一次就可以了,但是即使运行了多次次也不会造成异常的,也不是必须放在工程单元中,只要保证在 Random 语句之前运行过一次就可以了。
还有,randomize 并不保证 Random 不会取得重复的随机数,要保证不会重复,应该靠自己写代码来保证的。
 
如果要筛选的话,恐怕速度会很慢。有没有好的算法?
 
听说guid的方式可以做到不重复,不知道怎么弄.有谁知道么?具体讲讲.
 
用 Random 的方法,在数量很多的情况下,确实效率会比较低,这恐怕没什么方法。
GUID 码确实可以在概率上保证在每秒产生10万个的情况下,连续运行32年不会产生重复的GUID 码。
它类似于 {256EA6B8-19C1-4529-8234-CB38F1079FA8} 这种字符串,并不是数字,位数也不是10位,恐怕不适合楼主。
 
是哟,Randomize只能用一次。
Random机制不能保证值不重复,它的功能是产生随机数,至于随到哪里了它并不关心

GUID拿来做产品序列号倒是不错,可惜楼主要的是10位数字,用个算法加工一下喽,但这个算法必须保证加工后的数字不重复也。

查询法当然可以保证唯一性,但10万条记录的效率,偶看只好省了。

偶倒觉得分段法适合些:
把0-1000000000分成适合的数字分段,分段随机,每段用查询法检索是否重复。
例如,0-1000,1001-2000等。记录少,链表等就可以小些,速度就要快些
这些数字分段的选择顺序也可以随机地取,并且要检验是否已经取过,保证每段只取1次。
 
s:=GetRandomString;
if s1.indexof(s)=-1 then
s1.Add(s);
 
10位数字的值域为 0..9999999999 ,在这样的范围内随机生成10000个数,存在两个或者
两个以上的数字重复的概率约为0.5%;如果随机生成100000个数,出现重复的概率就上升到
了39.3%;生成1000000个数,出现重复的概率几乎等于100%!
根据统计概率,一般说来,值域应该大于随机次数的平方(在这里,应该是12位数或者更
高),才能有效的减少重复。

用一个列表保存已经生成的数,如果重复就重新生成即可(不过,考虑到数量太大,效率
很可能难以接受)。
 
To sgwk:
抱歉开始我将问题看简单了,后来我和几位同事研究了一下,如果想用Random函数完全实现无重复数是不可能的,即便是INT64位运算也存在重复几率,只不过比较低而已。
况且10位数字存在的排列是有限的,只要超过了组合限制无论如何也要重复的。
确实要全球唯一的话建议使用GUID码。
 
这是排列和组合的区别,如果是全集的话就是随机排列,抽取一部分就是随机组合.
 
Uses Math;

function GetRandomStringList2(aCount: Integer): TStringList;
const
Max_Len = 10;
var
i, M, R: Integer;
S: String;
B: TBits;
begin
Randomize;
S := '%.' + IntToStr(Max_Len) + 'd';
M := Round(IntPower(10, Max_Len));

Result := nil;
if aCount > M then
Exit;
Result := TStringList.Create;
B := TBits.Create;
try
B.Size := M;
for i := 0 to aCount - 1 do
repeat
R := Random(M);
if not B[R] then
begin
B[R] := True;
Result.Add(Format(S, [R]));
Break;
end;
until False;
finally
B.Free;
end;
end;

procedure TForm1.Button1Click(Sender: TObject);
const
CountGet: Integer = 1000000
//超过10万的话就不要向 Memo放
var
StrLst: TStringList;
T1, T2: Cardinal;
begin
T1 := GetTickCount;
StrLst := GetRandomStringList2(CountGet);
T2 := GetTickCount;
Button1.Caption := Format('%d ms', [T2 - T1]);
try
// Memo1.Lines.Assign(StrLst);
finally
StrLst.Free;
end;
Caption := IntToStr(Memo1.Lines.Count);
end;
 
jeffrey_s的方法不措,速度也可以。我试了一次生成100万,1000万,2000万个都没有重复。但一次生产3000万个时程序发生错误。
 
30M个字符串?!乖乖... 内存不够用了不是? 30M*4*5=600M 到崩溃的时候至少用了这
么多的虚拟内存了。现实一点吧,您到底要做什么?把需求说清楚——1M的情况已经搞定了
啊。
可以考虑当Result列表的Count大于10M的时候整个Append到一个文本文件中,然后整个
Clear,释放内存,再继续运行——当然,为了保证不重复,TBits数组是不能清空的。
 
to creation-zy:
我的机子,barton2500+ 1g的内存。应该不是内存的关系。我做过5000万个的 ,但是那个有重复。
 
有重复?我这里没有试过。我试过生成 1亿个数字,但没有重复。
BitCount := 0;
for i := 0 to M - 1 do
if B then Inc(BitCount);
 
to jeffrey_s
你误会了。你的没有重复。但我这边只能生成2000万的。3000万以上都会发生错误。
我的机子barton 2500+ 1g内存。你觉得是哪里有问题?
 

Similar threads

I
回复
0
查看
503
import
I
I
回复
0
查看
687
import
I
I
回复
0
查看
531
import
I
I
回复
0
查看
577
import
I
后退
顶部