实验报告链接:
https://www.cnblogs.com/nnn13579/p/10992122.html
对于基于时间抽取不同数,容易出现:
这是某位同学的实验结果。
当中使用了这样的语句:
srand((unsigned)time(NULL));
和下面这条是一个效果:
srand((int)time(0));
问题就出在这里。
原因很简单,计算机运算速度很快,精确到秒对于计算机来说太大了。
那么将int改成double是否有效解决呢?
No,除非你用的是Linux系统的php,精确到毫秒。
而且当要抽的量大时,也可能重复。
所以我的第一想法是,当重复时,直接全部重抽好了。
所以有了如下代码:
1 int k=0; 2 do{ 3 srand((int)time(0)); 4 for(int i=0;i<n;i++) 5 luck[i]=rand()%num; 6 for(int i=0;i<n;i++) 7 for(int j=i+1;j<n;j++) 8 if(luck[i]==luck[j]){ 9 k=1;break; 10 } 11 }while(k);
通过检测是否有抽出了相同数来确定是否重抽。
但显然,这种做法效率低下。
于是考虑在抽出相同数时,反复更改抽出的数来确保没有相同数。
于是:
1 for(int i=0;i<n;i++){ 2 srand((double)time(0)); 3 luck[i]=rand()%num; 4 int j=0,k=0; 5 for(;j<i;j++) 6 if(luck[i]==luck[j]){ 7 k=1; 8 while(k){ 9 srand((double)time(0)); 10 luck[i]=rand()%num; 11 if(luck[i]!=luck[j]) { 12 int t=0; 13 for(;t<i;t++) 14 if(luck[t]==luck[i]) break; 15 if(t==i) k=0; 16 } 17 } 18 } 19 }
但是,当抽40人时,起码要40+s,
效率低下的问题依旧没有解决。
那么优化算法,利用C(n)(m)=C(m-n)(m),增添以下内容:
1 if(n<=num/2||n<=num/2+1){ 2 fun1(luck,n,date,num); 3 fun2(luck,n); 4 } 5 else{ 6 int tluck[num-n]; 7 fun1(tluck,num-n,date,num); 8 fun2(tluck,num-n); 9 int tluckn=0,luckn=0; 10 for(int i=0;i<n;i++){ 11 while(luckn==tluck[tluckn]) 12 luckn++; 13 luck[i]=luckn; 14 luckn++; 15 } 16 }
fun1()为抽取随机数的函数,fun2()为排序抽取的数。
但这治标不治本。
问题在哪里呢?
出在会抽出重复数。
对于抽n个数,极大可能抽了不止n次。
于是我有了一个想法,把抽过的数全部排除掉,然后在剩下数中抽取。
1 void fun1(int luck[],int n,int num,int i,int all[]){ 2 if(i<n){ 3 srand((double)time(0)); 4 int t=rand()%num; 5 luck[i]=all[t]; 6 for(int j=t;j<num-1;j++) 7 all[j]=all[j+1]; 8 fun1(luck,n,num-1,i+1,all); 9 } 10 } 11 …… 12 …… 13 …… 14 int all[num]; 15 for(int i=0;i<num;i++) 16 all[i]=i; 17 fun1(luck,n,num,0,all);
all[]用于存储所有人的序号,
每个人的序号都有两种,
一种是在班里的序号,
另一种是在数组中的“房间号”。
而我们每次抽的都是房间号。
每次抽一个人后,将这个人踢掉,然后剩下的人一次向前移一个“房间”填补空位,实现房间里的人更新。
之后再用抽剩的人去抽下一轮。
由于
int t=rand()%num;
中,num值一直在变,随机的数也会一直变化,实现刷新。
即便一直抽取同一个“房间”,例如1号房,
但由于房间里的人已经换过了,所以不会是抽出同一人。
经过这次算法优化,能够实现抽n人只需要抽n次,抽取40人的时间压缩到了1s内。
原文地址:https://www.cnblogs.com/nnn13579/p/11006246.html