Swift 选取样本算法(从一组数据中随机选取N个数)

本文的思路来源于Swift 算法俱乐部的一篇文章,从一组数据中选取n个数,结合之前项目中碰到的问题,感觉很有用,能大大提升工作效率,在此记录加复习一下。(●ˇ∀ˇ●)

首先看样例:从n个项的集合中随机选择k个项。

假设你有一副52张牌,你需要随机抽取10张牌。 

方法一:

最开始自己用的笨办法是复制这个集合到一个新集合中,之后随机k次,每取出一个值之后就从新集合中移除这个值,保证随机出来的值不会重复。

代码

 /// <summary>
        /// 从list中随机取出count个值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="count"></param>
        static List<int> SampleAlgorithm(List<int> value,int count)
        {
            List<int> tempList = new List<int>();
            tempList.AddRange(value);
            List<int> targetValue= new List<int>();
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                int randomIndex= random.Next(0,tempList.Count);
                Console.WriteLine(randomIndex);
                if (randomIndex >= tempList.Count)
                    break;
                targetValue.Add(tempList[randomIndex]);
                tempList.RemoveAt(randomIndex);
            }
            return targetValue;
        }

方法二:

方法二参考文章中示例,通过用C#的方法实现,这个方法的原理是通过每次从当前索引到总数之间取一个随机数,之后将这个索引处的值和当前索引处值进行交换,最后的列表前n位就是随机出来的数,但是在C#中因为List是对象引用,所以这样也会修改原集合中的值,所以我们在随机之前需要先创建一个新的集合。

        static List<int> Method2(List<int> value, int count)
        {
            List<int> tempValue = new List<int>();
            tempValue.AddRange(value);
            Random random = new Random();
            for (int i = 0; i < count; i++)
            {
                if (i >= tempValue.Count)
                    break;
                int randomIndex=random.Next(i, tempValue.Count);
                if(randomIndex != i)
                {
                    Swap(tempValue, randomIndex, i);
                }
            }
            if(count>= tempValue.Count)
                count= tempValue.Count;
            return tempValue.GetRange(0, count);
        }

方法三:

被称为“水库抽样”(Reservoir Sampling),他的原理有两步

  1. 使用原始数组中的k个元素填充result数组。 这被称为“水库”。
  2. 用剩余池中的元素随机替换水库中的元素

它的最大优点是它可以用于太大而无法容纳在内存中的数组,即使你不知道数组的大小是多少(在Swift中这可能类似于读取文件元素的懒惰生成器)。这种算法可以保证原始数组的数据不发生改变,但是他会比较费时和费空间。

static List<int> Method3(List<int> value, int count)
        {
            int examined=0, selected=0;
            List<int> result=new List<int>();
            Random random = new Random();
            while (selected < count)
            {
                double r = random.NextDouble();
                int leftToexamine = value.Count - examined;
                int leftToAdd = count - selected;
                if (leftToexamine * r < leftToAdd)
                {
                    selected++;
                    result.Add(value[examined]);
                }
                examined++;
            }

            return result;
        }

参考资料:

Swift 算法俱乐部---选取样本

猜你喜欢

转载自blog.csdn.net/a1191835397/article/details/128163462