zoukankan      html  css  js  c++  java
  • 如何从数组中随机取出多个不重复的项

    如何从数组中随机取出多个不重复的项1.问题
    如何随机取数组中的多个值?给定一个数组,需要从此数组中随机取出不重复几项;

    2.应用举例
    从20个会员中,随机抽出5个做为幸运奖;
    对于这个应用,一个办法是可以在数据库层面解决,如(sql server):
    select top 5 * from  (select top 20 * from userid order by userid desc) as b order by newid()
    可是有时候,我们需要在编程语言中完成这样的功能,而不是从数据库,下边说的就是使用编程语言实现的方法;

    3.实现思路
    思路一:写一个无限循环,或者数组长度的循环,使用随机函数,每次从数组中随机抽取1项,同时判断此项是否已被抽取,如果被抽取,则继续循环重新抽取,直到取出不重复5项时,跳出循环;
    这办法简单吧?但这个办法不太好,如果需求要求从10万个中随机取出1万个,使用此办法,那么将有1/10的概率每次都取出了曾经取出的那项了,于是需要重新再取,效率自然不高。另外,无限循环或循环数组长度次总让人心里觉得不踏实。

    思路二:写一个循环,循环次数为要取出的个数,即取几个就刚好循环几次,每取一次,将取出的项从数组中删除,下次循环再取时,保证不会重复,少了是否重复的判断。

    4.几种语言的实现代码
    这里采用第二种思路,下面有as,js,C#的实现代码。

    ActionScript 2.0代码:

    //从一个给定的数组arr中,随机返回num个不重复项
    function getArrayItems(arr:Array, num:Number):Array {
        //新建一个数组,将传入的数组复制过来,用于运算,而不要直接操作传入的数组;
        var temp_array:Array = new Array();
        for (var index in arr) {
            temp_array.push(arr[index]);
        }
        //取出的数值项,保存在此数组
        var return_array:Array = new Array();
        for (var i = 0; i<num; i++) {
            //判断如果数组还有可以取出的元素,以防下标越界
            if (temp_array.length>0) {
                //在数组中产生一个随机索引
                var arrIndex:Number = Math.floor(Math.random()*temp_array.length);
                //将此随机索引的对应的数组元素值复制出来
                return_array[i] = temp_array[arrIndex];
                //然后删掉此索引的数组元素,这时候temp_array变为新的数组
                temp_array.splice(arrIndex, 1);
            } else {
                //数组中数据项取完后,退出循环,比如数组本来只有10项,但要求取出20项.
                break;
            }
        }
        return return_array;
    }
    //测试
    var my_array:Array = new Array();
    for (var i = 0; i<20; i++) {
        my_array[i] = "Num:"+i;
    }
    my_array = getArrayItems(my_array, 5);
    for (var i = 0; i<my_array.length; i++) {
        trace(my_array[i]);
    }


    JavaScript代码,和AS类似:

    <script language="javascript">
    //从一个给定的数组arr中,随机返回num个不重复项
    function getArrayItems(arr, num) {
        //新建一个数组,将传入的数组复制过来,用于运算,而不要直接操作传入的数组;
        var temp_array = new Array();
        for (var index in arr) {
            temp_array.push(arr[index]);
        }
        //取出的数值项,保存在此数组
        var return_array = new Array();
        for (var i = 0; i<num; i++) {
            //判断如果数组还有可以取出的元素,以防下标越界
            if (temp_array.length>0) {
                //在数组中产生一个随机索引
                var arrIndex = Math.floor(Math.random()*temp_array.length);
                //将此随机索引的对应的数组元素值复制出来
                return_array[i] = temp_array[arrIndex];
                //然后删掉此索引的数组元素,这时候temp_array变为新的数组
                temp_array.splice(arrIndex, 1);
            } else {
                //数组中数据项取完后,退出循环,比如数组本来只有10项,但要求取出20项.
                break;
            }
        }
        return return_array;
    }

    //测试
    var my_array = new Array();
    for (i = 0; i < 20; i++)
    {
       my_array[i] = "Num:"+i;
    }

    my_array = getArrayItems(my_array,5);
    for (i = 0; i < my_array.length; i++)
    {
       alert(my_array[i]);
    }

    </script>


    C#代码:

            public static IList<string> getListItems(IList<string> list, int num)
            {
                //新建一个泛型列表,将传入的列表复制过来,用于运算,而不要直接操作传入的列表;    

                //这样写是引用复制,不对啦,谢谢Osamede指出.
                //IList<string> temp_list = list;

                //另外这样写也要注意,也不是深度复制喽,关于深度复制可以做为一个新话题来说,这儿就不说啦;
                IList<string> temp_list = new List<string>(list);

                //取出的项,保存在此列表
                IList<string> return_list = new List<string>();

                //Random random = new Random(unchecked((int)DateTime.Now.Ticks));
                Random random = new Random();

                for (int i = 0; i < num; i++)
                {
                    //判断如果列表还有可以取出的项,以防下标越界
                    if (temp_list.Count > 0)
                    {
                        //在列表中产生一个随机索引
                        int arrIndex = random.Next(0, temp_list.Count);
                        //将此随机索引的对应的列表元素值复制出来
                        return_list.Add(temp_list[arrIndex]);
                        //然后删掉此索引的列表项
                        temp_list.RemoveAt(arrIndex);
                    }
                    else
                    {
                        //列表项取完后,退出循环,比如列表本来只有10项,但要求取出20项.
                        break;
                    }
                }
                return return_list;
            }


    测试:

    static void Main(string[] args)
            {
                IList<string> temp_list = new List<string>();
                for (int i = 0; i < 20; i++)
                {
                    temp_list.Add("Num:" + i.ToString());
                }

                temp_list = getListItems(temp_list, 5);

                for (int i = 0; i < temp_list.Count; i++)
                {
                    WL(temp_list[i]);
                }

                RL();
            }

            Helper methods:
               private static void WL(object text, params object[] args)
            {
                Console.WriteLine(text.ToString(), args);
            }

            private static void RL()
            {
                Console.ReadLine();
            }

            private static void Break()
            {
                System.Diagnostics.Debugger.Break();
            }
  • 相关阅读:
    linux常见错误1 -> E: 无法获得锁 /var/lib/apt/lists/lock open
    std::ostringstream用法浅析
    (译)C++11中的Move语义和右值引用
    C++11学习笔记(1) Rangebased for loop
    (转)Linux 下压缩与解压.zip和.rar及.7z文件
    (转)linux 查看当前用户及用户所属组别
    ubuntu 源码编译安装cmake2.8.10.2
    开始自学H5前端第一天
    自学H5第二天
    顶部滚动菜单栏
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4316848.html
Copyright © 2011-2022 走看看