6

我有一个包含 300 多个对象的 IQueryable:

public class Detail
{
    public int Id { get; set; }
    public int CityId { get; set; }
    public bool Chosen { get; set; }
}

IQueryable<Detail> details = ...

我怎么能反对这个随机挑选 50 个对象?我假设我需要用 .ToList() 转换它,但我不确定如何挑选随机元素。

4

6 回答 6

10

300 不是很多,所以是的,让它成为一个列表:

IQueryable<Detail> details = ...
IList<Detail> detailList = details.ToList();

现在你可以选择一个随机项目:

var randomItem = detailList[rand.Next(detailList.Count)];

你可以重复50次。然而,这会导致重复,消除它们的过程会变得混乱。

所以使用标准的洗牌算法,然后选择前 50 个:

Shuffle(detailList);
var selection = detailList.Take(50);
于 2013-11-07T12:53:59.003 回答
3
Random rnd = new Random();
IQueryable<Detail> details = myList.OrderBy(x => rnd.Next()).Take(50);
于 2013-11-07T12:57:22.897 回答
3

如果您事先知道要从中随机选择的项目总数,则无需先转换为列表即可。

以下方法将为您完成:

/// <summary>Randomly selects items from a sequence.</summary>
/// <typeparam name="T">The type of the items in the sequence.</typeparam>
/// <param name="sequence">The sequence from which to randomly select items.</param>
/// <param name="count">The number of items to randomly select from the sequence.</param>
/// <param name="sequenceLength">The number of items in the sequence among which to randomly select.</param>
/// <param name="rng">The random number generator to use.</param>
/// <returns>A sequence of randomly selected items.</returns>
/// <remarks>This is an O(N) algorithm (N is the sequence length).</remarks>

public static IEnumerable<T> RandomlySelectedItems<T>(IEnumerable<T> sequence, int count, int sequenceLength, System.Random rng)
{
    if (sequence == null)
    {
        throw new ArgumentNullException("sequence");
    }

    if (count < 0 || count > sequenceLength)
    {
        throw new ArgumentOutOfRangeException("count", count, "count must be between 0 and sequenceLength");
    }

    if (rng == null)
    {
        throw new ArgumentNullException("rng");
    }

    int available = sequenceLength;
    int remaining = count;
    var iterator  = sequence.GetEnumerator();

    for (int current = 0; current < sequenceLength; ++current)
    {
        iterator.MoveNext();

        if (rng.NextDouble() < remaining/(double)available)
        {
            yield return iterator.Current;
            --remaining;
        }

        --available;
    }
}

(这里的关键是在开始时需要知道可供选择的项目数量;这确实会在一定程度上降低实用性。但是如果快速获取计数并且缓冲所有项目会占用太多内存,这是一个有用的解决方案.)


这是另一种使用水库采样的方法

这种方法不需要知道可供选择的项目总数,但它确实需要缓冲输出。当然,它还需要枚举整个输入集合。

因此,这仅在您事先不知道可供选择的项目数量(或可供选择的项目数量非常多)时才有用。

我建议按照 Henk 的回答对列表进行改组,而不是这样做,但为了感兴趣,我将其包括在此处:

// n is the number of items to randomly choose.

public static List<T> RandomlyChooseItems<T>(IEnumerable<T> items, int n, Random rng)
{
    var result = new List<T>(n);
    int index = 0;

    foreach (var item in items)
    {
        if (index < n)
        {
            result.Add(item);
        }
        else
        {
            int r = rng.Next(0, index + 1);

            if (r < n)
                result[r] = item;
        }

        ++index;
    }

    return result;
}

作为 Henk 答案的附录,这是他提到的 Shuffle 算法的规范实现。在此,_rng是一个实例Random

/// <summary>Shuffles the specified array.</summary>
/// <typeparam name="T">The type of the array elements.</typeparam>
/// <param name="array">The array to shuffle.</param>

public void Shuffle<T>(IList<T> array)
{
    for (int n = array.Count; n > 1;)
    {
        int k = _rng.Next(n);
        --n;
        T temp = array[n];
        array[n] = array[k];
        array[k] = temp;
    }
}
于 2013-11-07T12:55:32.470 回答
1
var l = new List<string>();
l.Add("A");
l.Add("B");
l.Add("C");
l.Add("D");
l.Add("E");
l.Add("F");
l.Add("G");
l.Add("H");
l.Add("I");

var random = new Random();
var nl = l.Select(i=> new {Value=i,Index = random.Next()});

var finalList = nl.OrderBy(i=>i.Index).Take(3);
foreach(var i in finalList)
{
    Console.WriteLine(i.Value);
}
于 2013-11-07T12:59:12.703 回答
1
IQueryable<Detail> details = myList.OrderBy(x => Guid.NewGuid()).ToList();

在此之后,只需线性地穿过它:

var item1 = details[0];

这将避免重复。

于 2013-11-07T12:54:05.493 回答
0

这最终对我有用,它确保不返回重复项:

public List<T> GetRandomItems(List<T> items, int count = 3)
{
    var length = items.Count();
    var list = new List<T>();
    var rnd = new Random();
    var seed = 0;

    while (list.Count() < count)
    {
        seed = rnd.Next(0, length);
        if(!list.Contains(items[seed]))
            list.Add(items[seed]);
    }

    return list;
}
于 2019-03-29T04:15:16.547 回答