M中选N个数的算法 ( 积分: 100 )

  • 主题发起人 主题发起人 密码空间
  • 开始时间 开始时间

密码空间

Unregistered / Unconfirmed
GUEST, unregistred user!
就象福彩中的那样,比如36选7好像creation-zy有说过如下:
================
M选N号
下面的算法可以将组合的结果存放到一个二维Byte数组中。

type
BBA=array of array of Byte;
function CNMNum(N,M:Integer):Integer;
var
i:Integer;
c:Int64;
begin
c:=1;
for i:=N-M+1 to Ndo
c:=c*i;
for i:=2 to Mdo
c:=c div i;
Result:=c;
end;

function CNM(const N,M:Byte):BBA;
var
R:array of Byte;
Count:Integer;
StartTime:DWord;
TotalCount:Integer;
procedure Gen(Level:Integer);
var
j,s:Integer;
begin
if Level=M then
begin
SetLength(Result[Count],M);
System.Move(R[0],Result[Count][0],M);
Inc(Count);
if Count and $FFFF=0 then
begin
Form1.Label1.Caption:=Format('%.3f%% %.7d Time: %.2fs',
[100*Count/TotalCount,Count,(GetTickCount-StartTime)/1000]);
Application.ProcessMessages;
end;
exit;
end;
if Level=0 then
s:=0
else
s:=R[Level-1]+1;
for j:=s to N-M+Leveldo
begin
R[Level]:=j;
Gen(Level+1);
end;
end;
begin
if (N<1) or (M>N) or (M<1) then
exit;
TotalCount:=CNMNum(N,M);
SetLength(Result,TotalCount);
Count:=0;
SetLength(R,M);
StartTime:=GetTickCount;
Gen(0);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
Rlt:BBA;
T:DWord;
begin
T:=GetTickCount;
Rlt:=CNM(SpinEdit1.Value,SpinEdit2.Value);
Label1.Caption:=IntToStr(High(Rlt)+1)+' Time:'+FloatToStr((GetTickCount-T)/1000)+' s';
end;

但是不大看得懂,把上面的数输入MEMO一看,也好像不对,不过CNMNUM是正确的
不知其它朋友还有没有什么好方法或请creation-zy帮忙解释一下上面的代码,
最好能把源码发一下给我:Email:yufusheng7838@sohu.com谢
 
就象福彩中的那样,比如36选7好像creation-zy有说过如下:
================
M选N号
下面的算法可以将组合的结果存放到一个二维Byte数组中。

type
BBA=array of array of Byte;
function CNMNum(N,M:Integer):Integer;
var
i:Integer;
c:Int64;
begin
c:=1;
for i:=N-M+1 to Ndo
c:=c*i;
for i:=2 to Mdo
c:=c div i;
Result:=c;
end;

function CNM(const N,M:Byte):BBA;
var
R:array of Byte;
Count:Integer;
StartTime:DWord;
TotalCount:Integer;
procedure Gen(Level:Integer);
var
j,s:Integer;
begin
if Level=M then
begin
SetLength(Result[Count],M);
System.Move(R[0],Result[Count][0],M);
Inc(Count);
if Count and $FFFF=0 then
begin
Form1.Label1.Caption:=Format('%.3f%% %.7d Time: %.2fs',
[100*Count/TotalCount,Count,(GetTickCount-StartTime)/1000]);
Application.ProcessMessages;
end;
exit;
end;
if Level=0 then
s:=0
else
s:=R[Level-1]+1;
for j:=s to N-M+Leveldo
begin
R[Level]:=j;
Gen(Level+1);
end;
end;
begin
if (N<1) or (M>N) or (M<1) then
exit;
TotalCount:=CNMNum(N,M);
SetLength(Result,TotalCount);
Count:=0;
SetLength(R,M);
StartTime:=GetTickCount;
Gen(0);
end;

procedure TForm1.Button2Click(Sender: TObject);
var
Rlt:BBA;
T:DWord;
begin
T:=GetTickCount;
Rlt:=CNM(SpinEdit1.Value,SpinEdit2.Value);
Label1.Caption:=IntToStr(High(Rlt)+1)+' Time:'+FloatToStr((GetTickCount-T)/1000)+' s';
end;

但是不大看得懂,把上面的数输入MEMO一看,也好像不对,不过CNMNUM是正确的
不知其它朋友还有没有什么好方法或请creation-zy帮忙解释一下上面的代码,
最好能把源码发一下给我:Email:yufusheng7838@sohu.com谢
 
邮件收到,已回信,请查收。
CNM的算法比较简单,就是在若干个数中进行不分先后的不重复取数——将所有的情况都
列出即可。
要避免重复取数,而又可以忽略先后顺序,最简单的方法就是让被取的数一个比一个大。
至于穷举所有的情况,就只要对每个层次进行循环,遍历所有 上一层的数+1..N-M+Level
的数即可。
下面是C(12,4)的部分结果:
0 1 2 3
0 1 2 4
0 1 2 5
0 1 2 6
0 1 2 7
0 1 2 8
......
6 9 10 11
7 8 9 10
7 8 9 11
7 8 10 11
7 9 10 11
8 9 10 11
 
后退
顶部