문제

필요한 빠른 알고리즘을 선택하려면 5 성에서 일반 목록입니다.예를 들어,받고 싶어요 5 무작위 요소 List<string>.

도움이 되었습니까?

해결책

을 반복하고 각 요소에 대한 확률을 만들의 선택=(수요)/(수 왼쪽)

그래서 만약 당신이 40 항목은,첫 번째 것 5/40 되는 기회를 선택합니다.는 경우,다음은 4/39 기회,그렇지 않으면 그것은 5/39 기회입니다.시간으로 당신은 결국 당신은 당신의 5 개 품목,자주 당신은 그들 모두입니다.

다른 팁

Linq 사용:

YourList.OrderBy(x => rnd.Next()).Take(5)
public static List<T> GetRandomElements<T>(this IEnumerable<T> list, int elementsCount)
{
    return list.OrderBy(arg => Guid.NewGuid()).Take(elementsCount).ToList();
}

이것은 실제로 더 열심히 문제보다 소리 같은 주로하기 때문에,많은 수학적으로 올바른 솔루션이 실패하는 실제로 허용할 모든 가능성을(아래).

첫째,여기에 몇 가지 구현하기 쉬운,올바른 경우-당신은 가지고 있 a-정-random-number generator:

(0)카일의 대답은 오(n).

(1)목록을 생성 n 쌍[(0,rand),(1,rand),(2,rand),...],그들을 정렬하여 두 번째정을 조정하여 첫 번째 k(당신을 위해,k=5)지수를 얻을 당신의 임의의 일부.내 생각에 이것은 쉽게 구현할 수 있지만,그것은 오(n 로그 n)시간입니다.

(2)Init 빈 목록 s=[]성장할 것입니다하수의 k 임의의 요소입니다.숫자를 선택 r 에서{0,1,2,...,n-1}에 랜덤,r=rand%n,아래와 같은 내용을 추가합니다.다음 r=rand%(n-1)스틱 s;추가 r#요소보다 적은 그것에서 들을 충돌을 피할 수 있습니다.다음 r=rand%(n-2),동일한 것은,등등.할 때까지는 k 뚜렷한 요소에 들.이는 최악의 경우 실행 시간 O(k^2).그래서 예 k << n 이 빠를 수 있습니다.을 유지하는 경우는 s 분류 및 추적 인접한 간격으로 그것은,당신은 그것을 구현할 수 있습니다 O(k 로그 k)지만,그것은 더 많은 작업입니다.

@카일 당신이 옳아,제 생각에 나는 너의 대답이다.나는 급하게 읽고 그것은 처음에는 잘못된 생각을 했는지를 나타내는 순차적으로 선택하고 각 요소에 고정 확률 k/n,는 것이 잘못되었을-하지만 당신의 적응형 접근 방식이 나타나 올바른다.죄송합니다.

확인을 위해 지금의 커:점근(에 대한 고 k,n 성장)에 있 n^k/k!선택 k 요소의 하위 집합의 요소 n[이의 근사치(n 선택 k)].N 경우가 크고,k 는 매우 작은,그 다음 이러한 숫자는 거대하다.최고의 사이클 길이는 당신할 수 있는 표준 32 비트 임의의 번호 생성기는 2^32=256^4.그렇다면 우리의 목록을 가지고 1000 요소,그리고 우리가 선택하는 5 에서 임의의 방법이 없 표준 임의의 번호 생성기는 모든 가능성이 있습니다.그러나는 한,당신은 확인과 함께하는 선택을 잘 작동을 위한 작은 세트,항상"외모"랜덤,다음 이러한 알고리즘을 확인해야합니다.

부록:한 후 이것을 쓰는 것을 깨달았다의 구현하기 까다로운 아이디어(2)이 올바르게,그래서 그 원한을 명확히 이 대답이다.을 얻을 O(k 로그 k)시간에,당신은 필요한 배열과 같은 구조를 지원하는 O(로그 m)검색하고 삽입 균형 바이너리 이것을 할 수 있습니다.를 사용하여 이러한 구조를 구축 라는 배열 s,여기에 몇 가지 pseudopython:

# Returns a container s with k distinct random numbers from {0, 1, ..., n-1}
def ChooseRandomSubset(n, k):
  for i in range(k):
    r = UniformRandom(0, n-i)                 # May be 0, must be < n-i
    q = s.FirstIndexSuchThat( s[q] - q > r )  # This is the search.
    s.InsertInOrder(q ? r + q : r + len(s))   # Inserts right before q.
  return s

나는 제안 실행하는 몇 가지를 통해 샘플을 경우 어떻게 돌아가는지를 확인하려면 효율적으로 구현하는 위의 영어로 설명이 있습니다.

내가 생각하는 선택한 답이 올바른다.내가 구현한 그것을 다르게 하지만,나는 또한 원하는 결과 무작위 순서입니다.

    static IEnumerable<SomeType> PickSomeInRandomOrder<SomeType>(
        IEnumerable<SomeType> someTypes,
        int maxCount)
    {
        Random random = new Random(DateTime.Now.Millisecond);

        Dictionary<double, SomeType> randomSortTable = new Dictionary<double,SomeType>();

        foreach(SomeType someType in someTypes)
            randomSortTable[random.NextDouble()] = someType;

        return randomSortTable.OrderBy(KVP => KVP.Key).Take(maxCount).Select(KVP => KVP.Value);
    }

나는 그냥 실행으로 이 문제,그리고 몇 가지 이상의 google 검색 나를 데 문제가 임의로 섞 목록: http://en.wikipedia.org/wiki/Fisher-Yates_shuffle

을 완전히 무작위로 섞 목록(장소에)당신이 이렇:

를 섞는 배열의 요소 n(인덱스 0..n-1):

  for i from n − 1 downto 1 do
       j ← random integer with 0 ≤ j ≤ i
       exchange a[j] and a[i]

만 필요한 경우 제 5 요소,다음을 실행하는 대신 나는 모든 방법으로 n-1 에서 1,당신은 단지 그것을 실행하는 n-5(ie:n-5)

고 말할 수 있습니다 당신이 필요 k 항목

이 된다:

  for (i = n − 1; i >= n-k; i--)
  {
       j = random integer with 0 ≤ j ≤ i
       exchange a[j] and a[i]
  }

각 항목을 선택한 스왑으로 배열의 끝,그래서 k 요소를 선택하는 마지막 k 의 요소를 배열입니다.

이 시간이 걸립 O(k),여기서 k 숫자 임의로 선택한 요소는 당신이 필요합니다.

또한,당신이 원하지 않는 경우 수정하려면 초기 목록을 작성할 수 있습니다 모든 스왑에 목록을 임시 역 목록,그리고 다시 적용하고,따라서 공연 역의 세트 스왑 돌아와 당신은 당신의 초기의 목록을 변경하지 않고 O(k)실행하는 시간입니다.

마지막으로,실제 중요시,if(n==k),당신은 당신을 중지해야에서 1,지 않 n-k 로서,무작위로 선택된 정수 항상 있을 것이 0.

당신이 사용할 수 있는 이 그러나 주문하는 일이 일어날 것에 클라이언트 측면

 .AsEnumerable().OrderBy(n => Guid.NewGuid()).Take(5);

드래곤에는 알고리즘, 는 해석은 C#:

int k = 10; // items to select
var items = new List<int>(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 });
var selected = new List<int>();
double needed = k;
double available = items.Count;
var rand = new Random();
while (selected.Count < k) {
   if( rand.NextDouble() < needed / available ) {
      selected.Add(items[(int)available-1])
      needed--;
   }
   available--;
}

이 알고리즘을 선택한 indicies 품목의 목록입니다.

에 대해 생각했다 코멘트@JohnShedletsky 에 수락 응답 에 대한(역):

할 수 있어야에서 이 O(일부.길이),보다는 오히려 O(originalList.길이)

기본적으로,당신을 생성할 수 있어야 subset 무작위수하고 다음을 뽑아서 그들을 원래의 목록입니다.

방법

public static class EnumerableExtensions {

    public static Random randomizer = new Random(); // you'd ideally be able to replace this with whatever makes you comfortable

    public static IEnumerable<T> GetRandom<T>(this IEnumerable<T> list, int numItems) {
        return (list as T[] ?? list.ToArray()).GetRandom(numItems);

        // because ReSharper whined about duplicate enumeration...
        /*
        items.Add(list.ElementAt(randomizer.Next(list.Count()))) ) numItems--;
        */
    }

    // just because the parentheses were getting confusing
    public static IEnumerable<T> GetRandom<T>(this T[] list, int numItems) {
        var items = new HashSet<T>(); // don't want to add the same item twice; otherwise use a list
        while (numItems > 0 )
            // if we successfully added it, move on
            if( items.Add(list[randomizer.Next(list.Length)]) ) numItems--;

        return items;
    }

    // and because it's really fun; note -- you may get repetition
    public static IEnumerable<T> PluckRandomly<T>(this IEnumerable<T> list) {
        while( true )
            yield return list.ElementAt(randomizer.Next(list.Count()));
    }

}

고 싶었으면 더욱 효과적이,당신은 아마 사용 HashSet인덱스, 가 아닌 실제 목록 요소(경우에 당신이 있어 복합 형식 또는 비싼 비교);

단위 테스트

고 우리는 확인하지 않는 모든 충돌,etc.

[TestClass]
public class RandomizingTests : UnitTestBase {
    [TestMethod]
    public void GetRandomFromList() {
        this.testGetRandomFromList((list, num) => list.GetRandom(num));
    }

    [TestMethod]
    public void PluckRandomly() {
        this.testGetRandomFromList((list, num) => list.PluckRandomly().Take(num), requireDistinct:false);
    }

    private void testGetRandomFromList(Func<IEnumerable<int>, int, IEnumerable<int>> methodToGetRandomItems, int numToTake = 10, int repetitions = 100000, bool requireDistinct = true) {
        var items = Enumerable.Range(0, 100);
        IEnumerable<int> randomItems = null;

        while( repetitions-- > 0 ) {
            randomItems = methodToGetRandomItems(items, numToTake);
            Assert.AreEqual(numToTake, randomItems.Count(),
                            "Did not get expected number of items {0}; failed at {1} repetition--", numToTake, repetitions);
            if(requireDistinct) Assert.AreEqual(numToTake, randomItems.Distinct().Count(),
                            "Collisions (non-unique values) found, failed at {0} repetition--", repetitions);
            Assert.IsTrue(randomItems.All(o => items.Contains(o)),
                        "Some unknown values found; failed at {0} repetition--", repetitions);
        }
    }
}

선택 N random 항목에서 그룹의 안무 주문!임의성에 대해 예측할지에 대한 셔플 위치에 있습니다.모든 답변을 다루는 일부를 좀 주문을 수밖에 없보다 효율적이지 사람하지 않습니다.부터 효율은 키에 여기에,나는 뭔가를 게시 변경되지 않는 항목의 순서 너무 많.

1)필요한 경우 true 임의의 값이 없다는 의미에 대한 제한하는 요소를 선택(예,일단 선택할 수 있는 아이템 reselected):

public static List<T> GetTrueRandom<T>(this IList<T> source, int count, 
                                       bool throwArgumentOutOfRangeException = true)
{
    if (throwArgumentOutOfRangeException && count > source.Count)
        throw new ArgumentOutOfRangeException();

    var randoms = new List<T>(count);
    randoms.AddRandomly(source, count);
    return randoms;
}

설정하는 경우를 제외 플래그,다음을 선택할 수 있습니다 임의의 품목의 수다.

이 있는 경우{ 1, 2, 3, 4 }, 그런 다음 그것을 할 수 있습 제공{ 1, 4, 4 }, { 1, 4, 3 } 등 3 개 품목 또는{ 1, 4, 3, 2, 4 } 5 는 아이템!

이것은 매우 빠르고,그것이 아무것도 확인합니다.

2)필요한 경우 개별 회원들 그룹에서없이 반복되고,그 다음에 의존 사전의(많은 사람들이 지적한 것처럼 이미).

public static List<T> GetDistinctRandom<T>(this IList<T> source, int count)
{
    if (count > source.Count)
        throw new ArgumentOutOfRangeException();

    if (count == source.Count)
        return new List<T>(source);

    var sourceDict = source.ToIndexedDictionary();

    if (count > source.Count / 2)
    {
        while (sourceDict.Count > count)
            sourceDict.Remove(source.GetRandomIndex());

        return sourceDict.Select(kvp => kvp.Value).ToList();
    }

    var randomDict = new Dictionary<int, T>(count);
    while (randomDict.Count < count)
    {
        int key = source.GetRandomIndex();
        if (!randomDict.ContainsKey(key))
            randomDict.Add(key, sourceDict[key]);
    }

    return randomDict.Select(kvp => kvp.Value).ToList();
}

코드를 조금 더 긴 보다는 다른 사전에 접근하기 때문에 여기에 내가 추가하지 않지만,또한 제거에서 목록,그래서 그것도 두 개의 루프가 있습니다.당신은 여기에서 볼 수 있는 나가지 않았 다시 정렬 에서 아무것도 모든 경우 count 동일하게 됩 source.Count.때문에 나는 믿 임의에 있어야 반 설정 전체.나는 것을 의미하려는 경우 5 임의 항목 1, 2, 3, 4, 5,그것이 문제가 되지 않을 경우 그 1, 3, 4, 2, 51, 2, 3, 4, 5, 지만,필요한 경우 4 항목에서 동일한 설정,다음은 예측할 수율 1, 2, 3, 4, 1, 3, 5, 2, 2, 3, 5, 4 etc.둘째, 횟수가 임의 항목을 반환되는 것은 절반 이상이 원래 그룹,다음의 제거하는 것이 더 쉽 source.Count - count 품 그룹에서 보다 추가 count 항목입니다.성능상의 이유로 사용 sourcesourceDict 을 얻을때 임의의 인덱스에서 제거 방법입니다.

그래서 만약 당신이{ 1, 2, 3, 4 }, 이에 종료 할 수 있습니다{ 1, 2, 3 }, { 3, 4, 1 } 등 3 대 항목입니다.

3)필요할 경우 정말 독특한 임의의 값에서 당신의 그룹에 의해 계정에서의 중복된 원래 그룹,다음 사용할 수 있습니다 같은 방식으로 위의지만, HashSet 보다 가벼운 것입니다는 강력한 소프트웨어입니다.

public static List<T> GetTrueDistinctRandom<T>(this IList<T> source, int count, 
                                               bool throwArgumentOutOfRangeException = true)
{
    if (count > source.Count)
        throw new ArgumentOutOfRangeException();

    var set = new HashSet<T>(source);

    if (throwArgumentOutOfRangeException && count > set.Count)
        throw new ArgumentOutOfRangeException();

    List<T> list = hash.ToList();

    if (count >= set.Count)
        return list;

    if (count > set.Count / 2)
    {
        while (set.Count > count)
            set.Remove(list.GetRandom());

        return set.ToList();
    }

    var randoms = new HashSet<T>();
    randoms.AddRandomly(list, count);
    return randoms.ToList();
}

randoms 변수가 만든 HashSet 중복을 피하기 위해 추가의 희귀에서 가장 희귀한 경우 Random.Next 를 얻을 수 있습니 같은 값을 때,특히 입력 목록을 작습니다.

그래서{ 1, 2, 2, 4 } => 3 임의 항목=>{1,2,4}지 않{1,2,2}

{ 1, 2, 2, 4 } => 4 임의 항목=>예외는 아니다!!또{1,2,4}에 따라 깃발을 설정합니다.

의 일부를 확 방법을 사용:

static Random rnd = new Random();
public static int GetRandomIndex<T>(this ICollection<T> source)
{
    return rnd.Next(source.Count);
}

public static T GetRandom<T>(this IList<T> source)
{
    return source[source.GetRandomIndex()];
}

static void AddRandomly<T>(this ICollection<T> toCol, IList<T> fromList, int count)
{
    while (toCol.Count < count)
        toCol.Add(fromList.GetRandom());
}

public static Dictionary<int, T> ToIndexedDictionary<T>(this IEnumerable<T> lst)
{
    return lst.ToIndexedDictionary(t => t);
}

public static Dictionary<int, T> ToIndexedDictionary<S, T>(this IEnumerable<S> lst, 
                                                           Func<S, T> valueSelector)
{
    int index = -1;
    return lst.ToDictionary(t => ++index, valueSelector);
}

는 경우에는 그것의 모든에 대한 성능과 수십 1000 항목의 목록을 반복 10000 시간에,당신은 할 수 있습야 빠르게 임의의 클래스System.Random, 지만,나는 생각하지 않는 것에 큰 거래를 고려하고 후자는 대부분의 아마가지 않는 병목 현상,그것의 아주 빠른 충분히..

편집: 필요하신 경우에는 다시 정렬 순서의 반환 항목뿐만 아니라,그 후 아무것도 없는 이길 수 있는 dhakim 의 셔-예이츠 접근법 -단,달콤하고 있습니다.

간단한 솔루션을 사용(아마도 좋지 않아 큰 목록):사본을 목록으로 임시면,목록 루프에서 무작위로 선택 항목에서 temp 목록에 넣고 항목을 선택한 목록을 제거하는 동안 그것을 형태로 temp 목록(할 수 없습니다 그래서 reselected).

예제:

List<Object> temp = OriginalList.ToList();
List<Object> selectedItems = new List<Object>();
Random rnd = new Random();
Object o;
int i = 0;
while (i < NumberOfSelectedItems)
{
            o = temp[rnd.Next(temp.Count)];
            selectedItems.Add(o);
            temp.Remove(o);
            i++;
 }

내가 결합된 여러 가지 위의 답변을 만들기를 느리게 평가 extension 방법입니다.테스트 보여주는 카일의 접근 방식은(주문(N))은 많은 시간보다 느리게 drzaus'의 설정을 제안하는 임의의 지표를 선택하는(주문(K)).전 수행하는 더 많은 통화를하기에 더 반복하여 더 많은 시간 이상의 항목입니다.

의 목표를 나의 구현했:

1)실현하지 않는 전체 목록을 주는 경우에는 폐쇄되지 않는 명령.내가 주어진 시퀀스의 엄청나게 많은 항목,나를 실행하고 싶지 않습니다.사용하여 카일의 접근을 위해서 온라인 솔루션입니다.

2)는 경우 내가 말할 수 있는 명령을 사용 drzaus'방식으로 트위스트.는 경우 K 의 절반 이상 N,위험 탈곡으로 선택하는 많은 임의의 지시를 건너 뛸 수 있다.따라서 내가 목록을 작성 지수를 유지되지 않습니다.

3)나는 보증 항목은 반환되는 것과 같은 순서로 그들이 발생했습니다.카일의 알고리즘을 필요 없이 변경.drzaus'알고리즘을 필요는 방출하지 않는 항목에서는 임의의 지표들이 선택됩니다.내가 모든 지표로 SortedSet,다음을 방출 품목에 따라 정렬된 인덱스 순서로 수행됩니다.

4)경우 K 에 비해 큰 N 나는 반전의 설정,다음 내가 열거한 모든 항목을 테스트하는 경우에 없을 설정합니다.즉 을 잃을 순서(K)실행 시간이 있지만,이후 K 서 가까운 거 N 에서는 이러한 경우,을 잃지 않는다.

는 코드는 다음과 같습니다:

    /// <summary>
    /// Takes k elements from the next n elements at random, preserving their order.
    /// 
    /// If there are fewer than n elements in items, this may return fewer than k elements.
    /// </summary>
    /// <typeparam name="TElem">Type of element in the items collection.</typeparam>
    /// <param name="items">Items to be randomly selected.</param>
    /// <param name="k">Number of items to pick.</param>
    /// <param name="n">Total number of items to choose from.
    /// If the items collection contains more than this number, the extra members will be skipped.
    /// If the items collection contains fewer than this number, it is possible that fewer than k items will be returned.</param>
    /// <returns>Enumerable over the retained items.
    /// 
    /// See http://stackoverflow.com/questions/48087/select-a-random-n-elements-from-listt-in-c-sharp for the commentary.
    /// </returns>
    public static IEnumerable<TElem> TakeRandom<TElem>(this IEnumerable<TElem> items, int k, int n)
    {
        var r = new FastRandom();
        var itemsList = items as IList<TElem>;

        if (k >= n || (itemsList != null && k >= itemsList.Count))
            foreach (var item in items) yield return item;
        else
        {  
            // If we have a list, we can infer more information and choose a better algorithm.
            // When using an IList, this is about 7 times faster (on one benchmark)!
            if (itemsList != null && k < n/2)
            {
                // Since we have a List, we can use an algorithm suitable for Lists.
                // If there are fewer than n elements, reduce n.
                n = Math.Min(n, itemsList.Count);

                // This algorithm picks K index-values randomly and directly chooses those items to be selected.
                // If k is more than half of n, then we will spend a fair amount of time thrashing, picking
                // indices that we have already picked and having to try again.   
                var invertSet = k >= n/2;  
                var positions = invertSet ? (ISet<int>) new HashSet<int>() : (ISet<int>) new SortedSet<int>();

                var numbersNeeded = invertSet ? n - k : k;
                while (numbersNeeded > 0)
                    if (positions.Add(r.Next(0, n))) numbersNeeded--;

                if (invertSet)
                {
                    // positions contains all the indices of elements to Skip.
                    for (var itemIndex = 0; itemIndex < n; itemIndex++)
                    {
                        if (!positions.Contains(itemIndex))
                            yield return itemsList[itemIndex];
                    }
                }
                else
                {
                    // positions contains all the indices of elements to Take.
                    foreach (var itemIndex in positions)
                        yield return itemsList[itemIndex];              
                }
            }
            else
            {
                // Since we do not have a list, we will use an online algorithm.
                // This permits is to skip the rest as soon as we have enough items.
                var found = 0;
                var scanned = 0;
                foreach (var item in items)
                {
                    var rand = r.Next(0,n-scanned);
                    if (rand < k - found)
                    {
                        yield return item;
                        found++;
                    }
                    scanned++;
                    if (found >= k || scanned >= n)
                        break;
                }
            }
        }  
    } 

내가 사용하는 전문 임의의 번호 생성기,하지만 당신이 사용할 수 있습니다 C#'s Random 당신이 원하는 경우.(FastRandom 에 의해 작성되었 린 녹색과의 일부입 SharpNEAT.그것은 기간 2^128-1 는 것보다 더 많은 RNGs.)

여기에는 단위의 테스트:

[TestClass]
public class TakeRandomTests
{
    /// <summary>
    /// Ensure that when randomly choosing items from an array, all items are chosen with roughly equal probability.
    /// </summary>
    [TestMethod]
    public void TakeRandom_Array_Uniformity()
    {
        const int numTrials = 2000000;
        const int expectedCount = numTrials/20;
        var timesChosen = new int[100];
        var century = new int[100];
        for (var i = 0; i < century.Length; i++)
            century[i] = i;

        for (var trial = 0; trial < numTrials; trial++)
        {
            foreach (var i in century.TakeRandom(5, 100))
                timesChosen[i]++;
        }
        var avg = timesChosen.Average();
        var max = timesChosen.Max();
        var min = timesChosen.Min();
        var allowedDifference = expectedCount/100;
        AssertBetween(avg, expectedCount - 2, expectedCount + 2, "Average");
        //AssertBetween(min, expectedCount - allowedDifference, expectedCount, "Min");
        //AssertBetween(max, expectedCount, expectedCount + allowedDifference, "Max");

        var countInRange = timesChosen.Count(i => i >= expectedCount - allowedDifference && i <= expectedCount + allowedDifference);
        Assert.IsTrue(countInRange >= 90, String.Format("Not enough were in range: {0}", countInRange));
    }

    /// <summary>
    /// Ensure that when randomly choosing items from an IEnumerable that is not an IList, 
    /// all items are chosen with roughly equal probability.
    /// </summary>
    [TestMethod]
    public void TakeRandom_IEnumerable_Uniformity()
    {
        const int numTrials = 2000000;
        const int expectedCount = numTrials / 20;
        var timesChosen = new int[100];

        for (var trial = 0; trial < numTrials; trial++)
        {
            foreach (var i in Range(0,100).TakeRandom(5, 100))
                timesChosen[i]++;
        }
        var avg = timesChosen.Average();
        var max = timesChosen.Max();
        var min = timesChosen.Min();
        var allowedDifference = expectedCount / 100;
        var countInRange =
            timesChosen.Count(i => i >= expectedCount - allowedDifference && i <= expectedCount + allowedDifference);
        Assert.IsTrue(countInRange >= 90, String.Format("Not enough were in range: {0}", countInRange));
    }

    private IEnumerable<int> Range(int low, int count)
    {
        for (var i = low; i < low + count; i++)
            yield return i;
    }

    private static void AssertBetween(int x, int low, int high, String message)
    {
        Assert.IsTrue(x > low, String.Format("Value {0} is less than lower limit of {1}. {2}", x, low, message));
        Assert.IsTrue(x < high, String.Format("Value {0} is more than upper limit of {1}. {2}", x, high, message));
    }

    private static void AssertBetween(double x, double low, double high, String message)
    {
        Assert.IsTrue(x > low, String.Format("Value {0} is less than lower limit of {1}. {2}", x, low, message));
        Assert.IsTrue(x < high, String.Format("Value {0} is more than upper limit of {1}. {2}", x, high, message));
    }
}

여기에서 당신은 하나의 구현을 기반으로 Fisher-Yates 섞 는 알고리즘의 복잡도 O(n)n 의 일부 또는 샘플 크기,대신 목록의 크기로,존 Shedletsky 지적했다.

public static IEnumerable<T> GetRandomSample<T>(this IList<T> list, int sampleSize)
{
    if (list == null) throw new ArgumentNullException("list");
    if (sampleSize > list.Count) throw new ArgumentException("sampleSize may not be greater than list count", "sampleSize");
    var indices = new Dictionary<int, int>(); int index;
    var rnd = new Random();

    for (int i = 0; i < sampleSize; i++)
    {
        int j = rnd.Next(i, list.Count);
        if (!indices.TryGetValue(j, out index)) index = j;

        yield return list[index];

        if (!indices.TryGetValue(i, out index)) index = i;
        indices[j] = index;
    }
}

에 기반한 카일의 대답은,여기에 내 c#구현합니다.

/// <summary>
/// Picks random selection of available game ID's
/// </summary>
private static List<int> GetRandomGameIDs(int count)
{       
    var gameIDs = (int[])HttpContext.Current.Application["NonDeletedArcadeGameIDs"];
    var totalGameIDs = gameIDs.Count();
    if (count > totalGameIDs) count = totalGameIDs;

    var rnd = new Random();
    var leftToPick = count;
    var itemsLeft = totalGameIDs;
    var arrPickIndex = 0;
    var returnIDs = new List<int>();
    while (leftToPick > 0)
    {
        if (rnd.Next(0, itemsLeft) < leftToPick)
        {
            returnIDs .Add(gameIDs[arrPickIndex]);
            leftToPick--;
        }
        arrPickIndex++;
        itemsLeft--;
    }

    return returnIDs ;
}

이 방법이 될 수 있 해당하는 카일습니다.

말 목록은 크기의 n 고 당신이 원하는 k 요소입니다.

Random rand = new Random();
for(int i = 0; k>0; ++i) 
{
    int r = rand.Next(0, n-i);
    if(r<k) 
    {
        //include element i
        k--;
    }
} 

처럼 작동이 잘 보존되어 있는 곳입니다.)

알렉스-길버

에서 연장@ers 의 대답은 경우 걱정이 가능한 다른 구현 OrderBy,이는 안전해야:

// Instead of this
YourList.OrderBy(x => rnd.Next()).Take(5)

// Temporarily transform 
YourList
    .Select(v => new {v, i = rnd.Next()}) // Associate a random index to each entry
    .OrderBy(x => x.i).Take(5) // Sort by (at this point fixed) random index 
    .Select(x => x.v); // Go back to enumerable of entry

이것은 오를 수 있던에서 첫 번째 컷:

public List<String> getRandomItemsFromList(int returnCount, List<String> list)
{
    List<String> returnList = new List<String>();
    Dictionary<int, int> randoms = new Dictionary<int, int>();

    while (randoms.Count != returnCount)
    {
        //generate new random between one and total list count
        int randomInt = new Random().Next(list.Count);

        // store this in dictionary to ensure uniqueness
        try
        {
            randoms.Add(randomInt, randomInt);
        }
        catch (ArgumentException aex)
        {
            Console.Write(aex.Message);
        } //we can assume this element exists in the dictonary already 

        //check for randoms length and then iterate through the original list 
        //adding items we select via random to the return list
        if (randoms.Count == returnCount)
        {
            foreach (int key in randoms.Keys)
                returnList.Add(list[randoms[key]]);

            break; //break out of _while_ loop
        }
    }

    return returnList;
}

목록을 사용하의 불규칙적의 범위 내에서 1-총 목록 수고 다음 단순히 당기는 그 항목에서 목록을 보였는 가장 좋은 방법이 될지만,사전에 고유성을 보장하기 위해 뭔가가 나는 아직도 궁리 끝났습니다.

또한 내가 사용하는 문자열을 목록으로 대체 필요합니다.

왜 이런:

 Dim ar As New ArrayList
    Dim numToGet As Integer = 5
    'hard code just to test
    ar.Add("12")
    ar.Add("11")
    ar.Add("10")
    ar.Add("15")
    ar.Add("16")
    ar.Add("17")

    Dim randomListOfProductIds As New ArrayList

    Dim toAdd As String = ""
    For i = 0 To numToGet - 1
        toAdd = ar(CInt((ar.Count - 1) * Rnd()))

        randomListOfProductIds.Add(toAdd)
        'remove from id list
        ar.Remove(toAdd)

    Next
'sorry i'm lazy and have to write vb at work :( and didn't feel like converting to c#

그것은 많은 보다 더 열심히 한 것이라 생각합니다.보 훌륭한 문서"섞" 에서 제프.

를 작성했는 매우 짧은 문서에 그 주제를 포함하여 C#코드:
반 임의의 하위 집합 N 원소의 주어진 배열

목표:선택하는 N 개의 품목에서 컬렉션을 소스 없이 복제.내가 만들어 확장자에 대한 일반적인 컬렉션입니다.여기에 어떻게 그것을 했:

public static class CollectionExtension
{
    public static IList<TSource> RandomizeCollection<TSource>(this IList<TSource> source, int maxItems)
    {
        int randomCount = source.Count > maxItems ? maxItems : source.Count;
        int?[] randomizedIndices = new int?[randomCount];
        Random random = new Random();

        for (int i = 0; i < randomizedIndices.Length; i++)
        {
            int randomResult = -1;
            while (randomizedIndices.Contains((randomResult = random.Next(0, source.Count))))
            {
                //0 -> since all list starts from index 0; source.Count -> maximum number of items that can be randomize
                //continue looping while the generated random number is already in the list of randomizedIndices
            }

            randomizedIndices[i] = randomResult;
        }

        IList<TSource> result = new List<TSource>();
        foreach (int index in randomizedIndices)
            result.Add(source.ElementAt(index));

        return result;
    }
}

나는 최근에 이 사용하여 프로젝트 아이디어와 비슷 타일러의점 1.
나는 로드 무리의 질문을 선택하면 다섯니다.정렬이 사용하여 달성되는 IComparer.
모든 질문이 로드에서 QuestionSorter 목록이었다,다음 사용하여 정렬 목록을 정렬 기능 첫 번째 k 요소가 선택됩니다.

    private class QuestionSorter : IComparable<QuestionSorter>
    {
        public double SortingKey
        {
            get;
            set;
        }

        public Question QuestionObject
        {
            get;
            set;
        }

        public QuestionSorter(Question q)
        {
            this.SortingKey = RandomNumberGenerator.RandomDouble;
            this.QuestionObject = q;
        }

        public int CompareTo(QuestionSorter other)
        {
            if (this.SortingKey < other.SortingKey)
            {
                return -1;
            }
            else if (this.SortingKey > other.SortingKey)
            {
                return 1;
            }
            else
            {
                return 0;
            }
        }
    }

사용법:

    List<QuestionSorter> unsortedQuestions = new List<QuestionSorter>();

    // add the questions here

    unsortedQuestions.Sort(unsortedQuestions as IComparer<QuestionSorter>);

    // select the first k elements

여기에 내가 방법(전체 텍스트가 여기에 http://krkadev.blogspot.com/2010/08/random-numbers-without-repetition.html ).

그것을 실행해야에 O(K)대 O(N),여기서 K 은 수의 원하는 요소 N 의 크기 목록에서 선택:

public <T> List<T> take(List<T> source, int k) {
 int n = source.size();
 if (k > n) {
   throw new IllegalStateException(
     "Can not take " + k +
     " elements from a list with " + n +
     " elements");
 }
 List<T> result = new ArrayList<T>(k);
 Map<Integer,Integer> used = new HashMap<Integer,Integer>();
 int metric = 0;
 for (int i = 0; i < k; i++) {
   int off = random.nextInt(n - i);
   while (true) {
     metric++;
     Integer redirect = used.put(off, n - i - 1);
     if (redirect == null) {
       break;
     }
     off = redirect;
   }
   result.add(source.get(off));
 }
 assert metric <= 2*k;
 return result;
}

이 되지 않으로 우아하거나 효율적으로 받아들인 솔루션이지만,그것은 빠른 쓰기.첫째,permute 배열,무작위로 선택한 첫 번째 K 요소입니다.Python,

import numpy

N = 20
K = 5

idx = np.arange(N)
numpy.random.shuffle(idx)

print idx[:K]

나는 확장자를 사용 방법입니다.

    public static IEnumerable<T> TakeRandom<T>(this IEnumerable<T> elements, int countToTake)
    {
        var random = new Random();

        var internalList = elements.ToList();

        var selected = new List<T>();
        for (var i = 0; i < countToTake; ++i)
        {
            var next = random.Next(0, internalList.Count - selected.Count);
            selected.Add(internalList[next]);
            internalList[next] = internalList[internalList.Count - selected.Count];
        }
        return selected;
    }
public static IEnumerable<T> GetRandom<T>(this IList<T> list, int count, Random random)
    {
        // Probably you should throw exception if count > list.Count
        count = Math.Min(list.Count, count);

        var selectedIndices = new SortedSet<int>();

        // Random upper bound
        int randomMax = list.Count - 1;

        while (selectedIndices.Count < count)
        {
            int randomIndex = random.Next(0, randomMax);

            // skip over already selected indeces
            foreach (var selectedIndex in selectedIndices)
                if (selectedIndex <= randomIndex)
                    ++randomIndex;
                else
                    break;

            yield return list[randomIndex];

            selectedIndices.Add(randomIndex);
            --randomMax;
        }
    }

메모리:~계산
복잡:O(계산2)

N 은 매우 큰 정상적인 방법은 무작위로 섞 N 번호 선택,말,첫 번째 k 숫자를 금지 될 수 있습 때문에 공간 복잡합니다.다음과 같은 알고리즘을 필요한만 O(k)모두를 위해 시간 및 공간 복잡.

http://arxiv.org/abs/1512.00501

def random_selection_indices(num_samples, N):
    modified_entries = {}
    seq = []
    for n in xrange(num_samples):
        i = N - n - 1
        j = random.randrange(i)

        # swap a[j] and a[i] 
        a_j = modified_entries[j] if j in modified_entries else j 
        a_i = modified_entries[i] if i in modified_entries else i

        if a_i != j:
            modified_entries[j] = a_i   
        elif j in modified_entries:   # no need to store the modified value if it is the same as index
            modified_entries.pop(j)

        if a_j != i:
            modified_entries[i] = a_j 
        elif i in modified_entries:   # no need to store the modified value if it is the same as index
            modified_entries.pop(i)
        seq.append(a_j)
    return seq

LINQ 를 사용하여 큰 목록(우 비용이 많이 드는 서로 접촉하는 요소)면 살 수 있는 가능성의 중복:

new int[5].Select(o => (int)(rnd.NextDouble() * maxIndex)).Select(i => YourIEnum.ElementAt(i))

에 대한 내용가 목록 100.000 요소,그리고 그 때문에 그들을 뽑아서 DB 나는에 대해 halfed(또는 더 나)시간에 비해 rnd 에 전체 목록을 확인합니다.

데 큰 목록을 확률을 줄일 것입니다 크게 다룹니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top