从C#中的List <t>中选择N个随机元素的算法

时间:2017-02-02 12:34:19

标签: c# algorithm random collections element

我需要一个快速算法来从通用列表中选择4个随机元素。例如,我想从List中获取4个随机元素,然后根据一些计算,如果找到的元素无效,那么它应该再次从列表中选择接下来的4个随机元素。

6 个答案:

答案 0 :(得分:1)

您可以将索引存储在某个列表中以获取非重复索引:

List<T> GetRandomElements<T>(List<T> allElements, int randomCount = 4)
{
    if (allElements.Count < randomCount)
    {
        return allElements;
    }

    List<int> indexes = new List<int>();

    // use HashSet if performance is very critical and you need a lot of indexes
    //HashSet<int> indexes = new HashSet<int>(); 

    List<T> elements = new List<T>();

    Random random = new Random(); 
    while (indexes.Count < randomCount)
    {
        int index = random.Next(allElements.Count);
        if (!indexes.Contains(index))
        {
            indexes.Add(index);
            elements.Add(allElements[index]);
        }
    }

    return elements;
}

然后你可以做一些计算并调用这个方法:

void Main(String[] args)
{
    do
    {
        List<int> elements = GetRandomelements(yourElements);
        //do some calculations
    } while (some condition); // while result is not right
}

答案 1 :(得分:1)

类似的东西:

using System;
using System.Collections.Generic;

        public class Program
        {
            public static void Main()
            {
                var list = new List<int>();

                list.Add(1);
                list.Add(2);
                list.Add(3);
                list.Add(4);
                list.Add(5);

                int n = 4;

                var rand = new Random();

                var randomObjects = new List<int>();

                for (int i = 0; i<n; i++)
                {
                    var index = rand.Next(list.Count);

                    randomObjects.Add(list[index]);
                }       

            }
        }

答案 2 :(得分:1)

你可以这样做

Test1

然后使用这样的扩展方法:

Test

答案 3 :(得分:0)

假设List的长度为N.现在假设您将这4个数字放在另一个被调用的List中。然后你可以循环遍历List,你选择的元素的概率是

angular.module('MyApp', ['ngMaterial'])
.controller('csrClrt', function ($scope) {
    stylesdata = [{
            name: "a",
            id: 0
        }, {
            name: "b",
            id: 1
        }, {
            name: "c",
            id: 3
        }
    ];
    $scope.items = ["0"];
    var style = [];
    for (var i = 0; i < stylesdata.length; i++) {
        style.push({
            name: stylesdata[i].name,
            id: stylesdata[i].id
        })
    }
    $scope.styles = style;
})

答案 4 :(得分:0)

funcion (list)
(
loop i=0 i < 4
  index = (int) length(list)*random(0 -> 1)  
  element[i] = list[index] 
return element
) 

while(check  == false)
(
   elements = funcion (list)

    Do some calculation which returns check == false /true
)

这是伪代码,但我认为你应该自己想出来。 希望它有所帮助:)

答案 5 :(得分:0)

到目前为止,所有答案都有一个根本性的缺陷;您要求的算法将生成n元素的随机组合,并且遵循某些逻辑规则,此组合将有效或无效。如果不是,则应生成新的组合。显然,这种新组合应该是以前从未生产过的组合。所有提出的算法都没有强制执行。例如,如果1000000个可能的组合中只有一个是有效的,那么在产生特定的唯一组合之前,您可能会浪费大量资源。

那么,如何解决这个问题呢?嗯,答案很简单,创建所有可能的唯一解决方案,然后简单地以随机顺序生成它们。警告:我认为输入流没有重复元素,如果有,那么某些组合将不是唯一的。

首先,让我们自己写一个方便的不可变堆栈:

class ImmutableStack<T> : IEnumerable<T>
{
    public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
    private readonly T head;
    private readonly ImmutableStack<T> tail;
    public int Count { get; }

    private ImmutableStack()
    {
        Count = 0;
    }

    private ImmutableStack(T head, ImmutableStack<T> tail)
    {
        this.head = head;
        this.tail = tail;
        Count = tail.Count + 1;
    }

    public T Peek()
    {
        if (this == Empty)
            throw new InvalidOperationException("Can not peek a empty stack.");

        return head;
    }

    public ImmutableStack<T> Pop()
    {
        if (this == Empty)
            throw new InvalidOperationException("Can not pop a empty stack.");

        return tail;
    }

    public ImmutableStack<T> Push(T item) => new ImmutableStack<T>(item, this);

    public IEnumerator<T> GetEnumerator()
    {
        var current = this;

        while (current != Empty)
        {
            yield return current.head;
            current = current.tail;
        }
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

通过递归生成所有组合,这将使我们的生活更轻松。接下来,让我们获得主方法的签名:

public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinationsInRandomOrder<T>(
    IEnumerable<T> data, int combinationLength)

好的,这看起来是正确的。现在让我们实现这个:

    var allCombinations = GetAllPossibleCombinations(data, combinationLength).ToArray();
    var rnd = new Random();
    var producedIndexes = new HashSet<int>();

    while (producedIndexes.Count < allCombinations.Length)
    {
        while (true)
        {
            var index = rnd.Next(allCombinations.Length);

            if (!producedIndexes.Contains(index))
            {
                producedIndexes.Add(index);
                yield return allCombinations[index];
                break;
            }
        }
    }

好的,我们在这里所做的就是生成随机索引,检查我们还没有生成它(我们使用HashSet<int>),并在该索引处返回组合。

很简单,现在我们只需要处理GetAllPossibleCombinations(data, combinationLength)

这很简单,我们将使用递归。我们的纾困条件是我们目前的组合是指定的长度。另一个警告:我在整个代码中省略了参数验证,比如检查null或者指定的长度是否大于输入长度等等,应该注意。

为了好玩,我将在这里使用一些次要的C#7语法:嵌套函数。

public static IEnumerable<IEnumerable<T>> GetAllPossibleCombinations<T>(
    IEnumerable<T> stream, int length)
{
    return getAllCombinations(stream, ImmutableStack<T>.Empty);

    IEnumerable<IEnumerable<T>> getAllCombinations<T>(IEnumerable<T> currentData, ImmutableStack<T> combination)
    {
        if (combination.Count == length)
            yield return combination;

        foreach (var d in currentData)
        {
            var newCombination = combination.Push(d);

            foreach (var c in getAllCombinations(currentData.Except(new[] { d }), newCombination))
            {
                yield return c;
            }
        }
    }
}

我们走了,现在我们可以使用它:

var data = "abc";
var random = GetAllPossibleCombinationsInRandomOrder(data, 2);

foreach (var r in random)
{
    Console.WriteLine(string.Join("", r));
}

果然,输出是:

bc
cb
ab
ac
ba
ca