也谈生成不重复数的算法

刚刚看到有人写生成不重复的随机数的三种方法,正好在几年前我也有过类似的要求,实现类似于双色球随机选号的功能,也就是在M个数中选取N个不重复的数(M>N>0),下面我也谈谈我曾经用过的几种做法,也算是对作者提出的方法的一种补充吧。

一、利用Contains()判断法

思路是每次在制定范围中随机生成一个随机数,然后判断在已经生成的集合中是否存在,如果不存在就添加到结果集合中去,代码如下:

public   static  List < int >  GenerateRandomNumber1()
{
    List
< int >  result  =   new  List < int > (Count);
    Random random 
=   new  Random();
    
int  temp  =   0 ;
    
// 判断是否已经生成了足够的随机数
     while  (result.Count  <  Count)
    {
        temp 
=  random.Next(Min, Max)  +   1 ;
        
// 判断在集合中是否存在
         if  ( ! result.Contains(temp))
        {
            result.Add(temp);
        }
    }
    
return  result;
}

 

这种做法的缺点是效率比较低,因为不是每一次循环生成的随机数都是有效地,特别是当M和N接近时,这种算法的缺点极其明显。

二、删除法

这种做法的实现过程时实现将所有可能的值填充到一个容器集合中,每次随机从这个容器集合中取一个值出来放到结果集合中,并且从容器集合中删除这个已经用过的值,再进行下一次循环,从容器集合中取一个随机值,代码如下:

 

public   static  List < int >  GenerateRandomNumber2()
{
    List
< int >  container  =   new  List < int > (Max);
    List
< int >  result  =   new  List < int > (Count);
    
int  index  =   0 ;
    Random random 
=   new  Random();
    
for  ( int  i  =  Min; i  <=  Max; i ++ )
    {
        container.Add(i);
    }
    
for  ( int  i  =   1 ; i  <=  Count; i ++ )
    {
        index 
=  random.Next(container.Count);
        result.Add(container[index]);
        container.RemoveAt(index);
    }
    
// result.Sort();
     return  result;
}

 

这种做法的优点是每次循环都能生成一个“有效”的随机数(指不会与现有的重复),缺点是需要先初始化所有可能的随机数容器集合。

三、调换位置法

这种算法的特点是使用数组,先初始化一个包含所有可能值的容器集合,然后再初始化一个存放结果的数组,每次从容器集合中随机取一个数出来用结果数组存放,然后在容器集合中调整钢材使用的那个数的位置,使它放在不适用的范围中(即标识为不再使用),下次再从容器集合中的所有标识为可用的数中随机取一个出来,代码如下:

 

public   static   int [] GenerateRandomNumber3()
{
    
int [] container  =   new   int [ 33 ];
    
int [] result  =   new   int [ 6 ];
    Random random 
=   new  Random();
    
int  index  =   0 ;
    
int  temp  =   0 ;
    
for  ( int  i  =  Min; i  <=  Max; i ++ )
    {
        container[i 
-   1 =  i;
    }

    
for  ( int  i  =   0 ; i  <  Count; i ++ )
    {
        index 
=  random.Next(container.Length  -  i);
        result[i] 
=  container[index];
        temp 
=  container[container.Length  -   1   -  i];
        container[container.Length 
-   1   -  i]  =  container[index];
        container[index] 
=  temp;
    }
    
// Array.Sort(result);
     return  result;
}

 

这种做法的优点也是不用每次判断选择的数是否在结果中是否存在,并且使用的数组,原理适用于不支持泛型集合的语言(如PHP等),缺点也是要先初始化所有可能值的容器集合。

 

总结,对于我个人来说,我比较倾向于后两种方法,因为不用去判断随机选择的数是否已经在结果集合中存在的情况,缺点也是这个,它需要在每次初始化容器集合(不过这个缺点也是它的优点,这种情况也可以应用于在指定范围的所有可能类型的集合中随机选择几个不重复的场合)。

最后,因为因为有其它事情,所以表达方式可能不是很清楚,请大家看看代码。

下面提供一个可运行的版本,代码如下:

 

using  System;
using  System.Collections;
using  System.Collections.Generic;

namespace  XMLApplication
{
    
class  Program
    {
        
private   const   int  Min  =   1 ; // 随机数的取值范围的最小值
         private   const   int  Max  =   33 ; // 随机数的取值范围的最大值
         private   const   int  Count  =   6 ; // 要取的随机数的个数
         static   void  Main( string [] args)
        {
           
// again:
            List < int >  result1  =  GenerateRandomNumber1();
            ShowResult(result1);
            List
< int >  result2  =  GenerateRandomNumber2();
            ShowResult(result2);
            
int [] result3  =  GenerateRandomNumber3();
            ShowResult(result3);
            
// Console.WriteLine("Repeat?Y|N");
            
// string key = Console.ReadLine();
            
// if (key.ToUpper().Equals("Y"))
            
// {
            
//     goto again;
            
// }
        }

        
public   static  List < int >  GenerateRandomNumber1()
        {
            List
< int >  result  =   new  List < int > (Count);
            Random random 
=   new  Random();
            
int  temp  =   0 ;
            
// 判断是否已经生成了足够的随机数
             while  (result.Count  <  Count)
            {
                temp 
=  random.Next(Min, Max)  +   1 ;
                
// 判断在集合中是否存在
                 if  ( ! result.Contains(temp))
                {
                    result.Add(temp);
                }
            }
            result.Sort();
            
return  result;
        }

        
public   static  List < int >  GenerateRandomNumber2()
        {
            List
< int >  container  =   new  List < int > (Max);
            List
< int >  result  =   new  List < int > (Count);
            
int  index  =   0 ;
            Random random 
=   new  Random();
            
for  ( int  i  =  Min; i  <=  Max; i ++ )
            {
                container.Add(i);
            }
            
for  ( int  i  =   1 ; i  <=  Count; i ++ )
            {
                index 
=  random.Next(container.Count);
                result.Add(container[index]);
                container.RemoveAt(index);
            }
            
// result.Sort();
             return  result;
        }

        
public   static   int [] GenerateRandomNumber3()
        {
            
int [] container  =   new   int [ 33 ];
            
int [] result  =   new   int [ 6 ];
            Random random 
=   new  Random();
            
int  index  =   0 ;
            
int  temp  =   0 ;
            
for  ( int  i  =  Min; i  <=  Max; i ++ )
            {
                container[i 
-   1 =  i;
            }

            
for  ( int  i  =   0 ; i  <  Count; i ++ )
            {
                index 
=  random.Next(container.Length  -  i);
                result[i] 
=  container[index];
                temp 
=  container[container.Length  -   1   -  i];
                container[container.Length 
-   1   -  i]  =  container[index];
                container[index] 
=  temp;
            }
            Array.Sort(result);
            
return  result;
        }

        
private   static   void  ShowResult(IList < int >  list)
        {
            Console.Write(
" [ " );
            
for  ( int  i  =   0 ; i  <  list.Count  -   1 ; i ++ )
            {
                Console.Write(list[i] 
+   " , " );
            }
            Console.WriteLine(list[list.Count 
-   1 +   " ] " );
        }
    }

}

 

 

转载于:https://www.cnblogs.com/zhoufoxcn/articles/1783523.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值