Random 및 OrderBy를 사용하는 것이 좋은 셔플 알고리즘입니까?


164

Coding Horror 에서 다양한 셔플 알고리즘에 대한 기사 를 읽었습니다 . 사람들이 목록을 섞기 위해이 작업을 수행 한 곳을 보았습니다.

var r = new Random();
var shuffled = ordered.OrderBy(x => r.Next());

이것이 좋은 셔플 알고리즘입니까? 정확히 어떻게 작동합니까? 이것이 허용되는 방법입니까?

답변:


205

내가 좋아하는 셔플 방법이 아닙니다. 주로 O (n log n)이기 때문에 O (n) 셔플을 쉽게 구현 할 수있는 좋은 이유가 없습니다. 문제의 코드는 기본적으로 각 요소에 임의의 (희망적으로 고유 한) 숫자를 부여한 다음 해당 숫자에 따라 요소를 정렬하여 "작동"합니다.

나는 Durstenfield의 Fisher-Yates 셔플 변형을 선호 합니다.

간단한 Shuffle확장 방법을 구현하는 것은 기본적으로 Fisher-Yates의 기존 구현을 사용하여 호출 ToList하거나 ToArray입력 하는 것으로 구성됩니다 . ( Random일반적으로 인생을 더 좋게하기 위해 매개 변수로 전달하십시오.) 주변에는 많은 구현이 있습니다 ... 아마도 어딘가에 답변이 있습니다.

그러한 확장 방법의 좋은 점은 독자가 실제로하려는 일을 독자에게 분명히 알 수 있다는 것입니다.

편집 : 다음은 간단한 구현입니다 (오류 검사 없음).

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length-1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        T tmp = elements[i];
        elements[i] = elements[swapIndex];
        elements[swapIndex] = tmp;
    }
    // Lazily yield (avoiding aliasing issues etc)
    foreach (T element in elements)
    {
        yield return element;
    }
}

편집 : 아래 성능에 대한 의견은 요소를 섞을 때 실제로 요소를 반환 할 수 있음을 상기시킵니다.

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    for (int i = elements.Length - 1; i >= 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        // ... except we don't really need to swap it fully, as we can
        // return it immediately, and afterwards it's irrelevant.
        int swapIndex = rng.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

이제 필요한만큼만 작업 할 수 있습니다.

두 경우 모두 Random다음과 같이 사용 하는 인스턴스에주의해야합니다 .

  • Random대략 같은 시간에 두 개의 인스턴스를 생성 하면 동일한 방식으로 사용될 때 동일한 난수 시퀀스가 ​​생성됩니다.
  • Random 스레드 안전하지 않습니다.

나는이 에 대한 기사Random 이러한 문제에 대한 자세한 내용이수록 및 솔루션을 제공하는합니다.


5
글쎄, 내가 말하고 싶은 작지만 중요한 구현은 항상 여기에서 찾을 수 있습니다. 예), 당신이 =)하고자한다면
— Svish

9
Jon-Fisher-Yates에 대한 설명은 질문에 제공된 구현 (순진한 버전)과 동일합니다. Durstenfeld / Knuth는 할당이 아니라 감소하는 세트와 교체에서 선택하여 O (n)을 달성합니다. 이 방법으로 선택한 난수가 반복 될 수 있으며 알고리즘은 O (n) 만 사용합니다.
— tvanfosson

8
당신은 아마 이것에 대해 나에게서 아프게 될 것이지만, 나는 당신이 알고 싶어하는 단위 테스트에서 약간의 문제에 부딪쳤다. ElementAt에는 매번 확장을 호출하여 신뢰할 수없는 결과를 제공하는 기발한 요소가 있습니다. 내 테스트에서는 이것을 피하기 전에 결과를 구체화하고 있습니다.
— tvanfosson

3
@ tvanfosson : 전혀 아프지 않습니다 :) 그렇습니다. 발신자는 그것이 게으르게 평가된다는 것을 알고 있어야합니다.
— Jon Skeet

4
조금 늦었지만 같은 파일 source.ToArray();에 있어야 using System.Linq;합니다. 그렇지 않으면 다음과 같은 오류가 발생합니다.'System.Collections.Generic.IEnumerable<T>' does not contain a definition for 'ToArray' and no extension method 'ToArray' accepting a first argument of type 'System.Collections.Generic.IEnumerable<T>' could be found (are you missing a using directive or an assembly reference?)
— Powerlord

70

이것은 Jon Skeet의 답변을 기반으로 합니다.

이 답변에서 배열은 뒤섞이고를 사용하여 반환됩니다 yield. 결과적으로 배열은 foreach 기간 동안 반복적으로 필요한 객체뿐만 아니라 메모리에 유지되지만 비용은 모두 처음에 있습니다. 수율은 기본적으로 빈 루프입니다.

이 알고리즘은 게임에서 많이 사용되며 처음 세 항목을 선택하고 다른 항목은 나중에 필요한 경우에만 필요합니다. 내 제안은 yield교환되는 즉시 숫자 에 대한 것입니다. 이는 반복 비용을 O (1) (기본적으로 반복 당 5 개의 작업)으로 유지하면서 시작 비용을 줄입니다. 총 비용은 동일하게 유지되지만 셔플 링 자체는 더 빠릅니다. 이것이 collection.Shuffle().ToArray()이론적으로 아무런 차이가 없기 때문에 이것이 호출되는 경우, 위에서 언급 한 사용 사례에서는 시작 속도가 빨라집니다. 또한 이렇게하면 고유 한 항목이 몇 개만 필요한 경우에도 알고리즘이 유용합니다. 예를 들어, 52의 데크에서 3 장의 카드를 뽑아야하는 경우 전화를 걸 수 deck.Shuffle().Take(3)있으며 3 개의 스왑 만 발생합니다 (전체 어레이를 먼저 복사해야 함).

public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng)
{
    T[] elements = source.ToArray();
    // Note i > 0 to avoid final pointless iteration
    for (int i = elements.Length - 1; i > 0; i--)
    {
        // Swap element "i" with a random earlier element it (or itself)
        int swapIndex = rng.Next(i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
        // we don't actually perform the swap, we can forget about the
        // swapped element because we already returned it.
    }

    // there is one item remaining that was not returned - we return it now
    yield return elements[0]; 
}

아야! 소스의 모든 항목을 반환하지는 않습니다. N 반복에 고유 한 난수에 의존 할 수 없습니다.
— P Daddy

2
영리한! (그리고 나는이 15 문자 물건을 싫어 ...)
— Svish

@P 아빠 : 응? 정교하게 관리?
— Svish

1
또는> 0을> = 0으로 바꿀 수 있으며 추가 RNG 적중과 중복 할당이 필요하지 않습니다.
— FryGuy

4
시작 비용은 source.ToArray ()의 비용으로 O (N)입니다.
— Dave Hillier

8

이 Skeet 인용에서 시작 :

내가 좋아하는 셔플 방법이 아닙니다. 주로 O (n log n)이기 때문에 O (n) 셔플을 쉽게 구현 할 수있는 좋은 이유가 없습니다. 문제의 코드는 기본적으로 각 요소에 임의의 ( 희망적으로 고유 한 ) 숫자를 부여한 다음 해당 숫자에 따라 요소를 정렬하여 "작동"합니다 .

희망적으로 독특한 이유를 조금 설명 하겠습니다!

이제 Enumerable.OrderBy에서 :

이 방법은 안정적인 정렬을 수행합니다. 즉, 두 요소의 키가 동일하면 요소의 순서가 유지됩니다.

이건 매우 중요합니다! 두 요소가 동일한 난수를 "수신"하면 어떻게됩니까? 배열에서와 동일한 순서로 유지됩니다. 자, 이것이 일어날 가능성은 무엇입니까? 정확하게 계산하기는 어렵지만 생일 문제 는 바로이 문제입니다.

자, 진짜입니까? 사실인가요?

항상 그렇듯이 의심스러운 경우 몇 줄의 프로그램을 작성하십시오. http://pastebin.com/5CDnUxPG

이 작은 코드 블록은 Fisher-Yates 알고리즘을 뒤로 사용하고 Fisher-Yates 알고리즘을 앞으로 사용하여 3 번의 요소 배열을 특정 횟수만큼 섞습니다 ( wiki 페이지에는 두 개의 의사 코드 알고리즘이 있습니다 ... 결과는하지만, 하나가 다른 하나는 제 마지막 요소에서 수행되는 동안), 마지막 요소로부터 제의 순 잘못된 알고리즘 수행 http://blog.codinghorror.com/the-danger-of-naivete/를 상기 사용 .OrderBy(x => r.Next())그리고 .OrderBy(x => r.Next(someValue)).

이제 Random.Next 는

0보다 크거나 MaxValue보다 작은 32 비트 부호있는 정수입니다.

그래서 그것은

OrderBy(x => r.Next(int.MaxValue))

이 문제가 존재하는지 테스트하기 위해 배열을 확대하거나 (매우 느린 것) 난수 생성기의 최대 값을 간단히 줄일 수 있습니다 ( int.MaxValue"특별한"숫자가 아닙니다. 단순히 매우 큰 숫자입니다). 결국 알고리즘이의 안정성에 의해 편향되지 않으면 OrderBy모든 값 범위가 동일한 결과를 제공해야합니다.

그런 다음 프로그램은 1 ... 4096 범위의 일부 값을 테스트합니다. 결과를 보면, 낮은 값 (<128)의 경우 알고리즘이 매우 편향되어 있음 (4-8 %)이 분명합니다. 3 개 이상의 값이 필요합니다 r.Next(1024). 배열을 더 크게 만들면 (4 또는 5) r.Next(1024)충분하지 않습니다. 나는 셔플 링과 수학의 전문가는 아니지만 배열 길이의 각 여분의 비트마다 최대 값의 2 비트가 더 필요하다고 생각합니다 (생일 역설이 sqrt (numvalues)에 연결되어 있기 때문에) 최대 값이 2 ^ 31이면 최대 2 ^ 12 / 2 ^ 13 비트 (4096-8192 요소)까지 배열을 정렬 할 수 있어야한다고 말할 것입니다.


잘 설명하고 원래 질문에 대한 문제를 완벽하게 표시합니다. 이것은 Jon의 대답과 병합되어야합니다.
— TheSoftwareJedi

6

대부분의 경우 probablly ok이며 거의 항상 진정한 임의 분포를 생성합니다 (Random.Next ()가 두 개의 동일한 임의 정수를 생성하는 경우 제외).

시리즈의 각 요소에 임의의 정수를 할당 한 다음이 정수로 순서를 정렬하여 작동합니다.

응용 프로그램의 99.9 %에 대해 완전히 허용됩니다 (위의 경우를 처리해야하는 경우 제외). 또한 런타임에 대한 skeet의 이의 제기가 유효하므로 긴 목록을 섞을 경우 사용하지 않을 수 있습니다.


4

이것은 여러 번 전에 나타났습니다. StackOverflow에서 Fisher-Yates를 검색하십시오.

이 알고리즘을 위해 작성한 C # 코드 샘플 은 다음과 같습니다 . 원하는 경우 다른 유형으로 매개 변수화 할 수 있습니다.

static public class FisherYates
{
        //      Based on Java code from wikipedia:
        //      http://en.wikipedia.org/wiki/Fisher-Yates_shuffle
        static public void Shuffle(int[] deck)
        {
                Random r = new Random();
                for (int n = deck.Length - 1; n > 0; --n)
                {
                        int k = r.Next(n+1);
                        int temp = deck[n];
                        deck[n] = deck[k];
                        deck[k] = temp;
                }
        }
}

2
Random이와 같은 정적 변수로 사용해서는 안됩니다 - Random스레드 안전하지 않습니다. csharpindepth.com/Articles/Chapter12/Random.aspx
— Jon Skeet

@ Jon Skeet : 물론 이것은 합법적 인 주장입니다. OTOH, OP는 평평하지 않은 알고리즘에 대해 묻고 있지만 이것이 맞습니다 (멀티 스레드 카드 셔플 링 유스 케이스 제외).
— hughdbrown

1
그것은 이것이 OP의 접근 방식보다 "잘못 잘못되었다"는 것을 의미합니다. 그것은 그것이 다중 스레드 컨텍스트에서 안전하게 사용할 수 없다는 것을 이해하지 않고 사용해야하는 코드를 의미하지는 않습니다 ... 이것은 언급하지 않은 것입니다. 정적 멤버를 여러 스레드에서 안전하게 사용할 수 있다는 합리적인 기대 가 있습니다.
— Jon Skeet 2012 년

@ Jon Skeet : 물론, 바꿀 수 있습니다. 끝난. OP가 알고리즘보다 더 많은 것을 요구하지 않으면 "멀티 스레드 유스 케이스를 처리하지 않기 때문에 잘못되었습니다."라고 대답하는 경향이 있습니다. 몇 년 동안 내 답변을 검토하십시오. 종종 명시된 요구 사항을 초과하는 OP 답변을 받았습니다. 나는 그것을 비난했습니다. 그러나 OP가 가능한 모든 용도에 맞는 답변을 얻을 것으로는 기대하지 않습니다.
— hughdbrown

다른 사람이 채팅에서 나를 지적했기 때문에 나는이 답변을 전혀 방문하지 않았습니다. OP는 스레딩을 구체적으로 언급 하지 않았지만 정적 메소드 가 스레드 안전 하지 않은 경우를 언급 할 가치가 있다고 생각합니다. 정상적이지 않으며 코드를 수정하지 않고 많은 상황에 적합하지 않기 때문입니다. 새로운 코드는 스레드에 안전하지만 같은 크기의 두 컬렉션을 섞기 위해 "거의"동시에 여러 스레드에서 호출하는 것처럼 이상적이지는 않습니다. 셔플은 동일합니다. 기본적으로 Random내 기사에서 언급했듯이 사용하기가 어렵습니다.
— Jon Skeet

3

성능에 너무 걱정하지 않으면 좋은 셔플 링 알고리즘처럼 보입니다. 내가 지적한 유일한 문제는 그 동작을 제어 할 수 없다는 것이므로 테스트하기가 어려울 수 있습니다.

하나의 가능한 옵션은 시드를 난수 생성기 (또는 난수 생성기를 매개 변수)에 매개 변수로 전달하는 것이므로보다 쉽게 ​​제어하고 테스트 할 수 있습니다.


3

Jon Skeet의 답변이 전적으로 만족스러운 것으로 나타 났지만 고객의 로보 스캐너는 Random보안 결함으로 모든 사례를보고합니다 . 그래서 나는 그것을 교환했다 System.Security.Cryptography.RNGCryptoServiceProvider. 보너스로 언급 된 스레드 안전성 문제를 해결합니다. 반면을 사용하는 RNGCryptoServiceProvider것보다 300 배 느리게 측정되었습니다 Random.

용법:

using (var rng = new RNGCryptoServiceProvider())
{
    var data = new byte[4];
    yourCollection = yourCollection.Shuffle(rng, data);
}

방법:

/// <summary>
/// Shuffles the elements of a sequence randomly.
/// </summary>
/// <param name="source">A sequence of values to shuffle.</param>
/// <param name="rng">An instance of a random number generator.</param>
/// <param name="data">A placeholder to generate random bytes into.</param>
/// <returns>A sequence whose elements are shuffled randomly.</returns>
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, RNGCryptoServiceProvider rng, byte[] data)
{
    var elements = source.ToArray();
    for (int i = elements.Length - 1; i >= 0; i--)
    {
        rng.GetBytes(data);
        var swapIndex = BitConverter.ToUInt32(data, 0) % (i + 1);
        yield return elements[swapIndex];
        elements[swapIndex] = elements[i];
    }
}

3

알고리즘을 찾고 계십니까? 내 ShuffleList수업을 사용할 수 있습니다 .

class ShuffleList<T> : List<T>
{
    public void Shuffle()
    {
        Random random = new Random();
        for (int count = Count; count > 0; count--)
        {
            int i = random.Next(count);
            Add(this[i]);
            RemoveAt(i);
        }
    }
}

그런 다음 다음과 같이 사용하십시오.

ShuffleList<int> list = new ShuffleList<int>();
// Add elements to your list.
list.Shuffle();

어떻게 작동합니까?

5 개의 첫 정수의 초기 정렬 목록을 보자 : { 0, 1, 2, 3, 4 }.

이 메소드는 요소의 숫자를 세면서 시작하여 호출합니다 count. 그 다음으로 count, 각 단계에서 감소 그 사이의 난수를 얻어 0및 count상기리스트의 끝으로 이동한다.

다음 단계별 예제에서 이동할 수있는 항목은 기울임 꼴 이며 선택한 항목은 굵게 표시됩니다 .

0 1 2 3 4
0 1 2 3 4
0 1 2 4 3
0 1 2 4 3
1 2 4 3 0
1 2 4 3 0
1 2 3 0 4
1 2 3 0 4
2 3 0 4 1
2 3 0 4 1
3 0 4 1 2


그것은 O (n)이 아닙니다. 단독으로 O (n)입니다.
— paparazzo

흠, 당신이 옳은 것 같아, 내 나쁜! 그 부분을 제거하겠습니다.
— SteeveDroz

1

이 알고리즘은 목록의 각 값에 대해 새로운 임의의 값을 생성 한 다음 임의의 값으로 목록을 정렬하여 섞습니다. 메모리 내 테이블에 새 열을 추가 한 다음 GUID로 채우고 해당 열을 기준으로 정렬하는 것으로 생각하십시오. 나에게 효율적인 방법처럼 보입니다 (특히 람다 설탕으로!)


1

약간 관련이 없지만 여기에 실제로 임의의 주사위 롤을 생성하는 흥미로운 방법이 있습니다 (실제로는 과도하지만 실제로 구현되었습니다)!

주사위 O- 매틱

내가 이것을 여기에 게시하는 이유는 그가 실제 주사위보다 셔플하기 위해 알고리즘을 사용한다는 아이디어에 사용자가 어떻게 반응했는지에 대해 흥미로운 점을 제시하기 때문입니다. 물론, 실제 세계에서, 그러한 솔루션은 무작위성이 큰 영향을 미치고 그 영향이 돈에 영향을 미치는 스펙트럼의 극한 끝에 만 적용됩니다.


1

"이 알고리즘은 목록의 각 값에 대해 새로운 임의의 값을 생성 한 다음 임의의 값을 기준으로 목록을 정렬하여 셔플"과 같은 많은 답변이 매우 잘못되었다고 말할 것입니다!

나는 이것이 소스 컬렉션의 각 요소에 임의의 값을 할당하지 않는다고 생각합니다. 대신 Quicksort처럼 실행되는 정렬 알고리즘이있을 수 있습니다.이 알고리즘은 대략 n log n 번 비교 함수를 호출합니다. 어떤 종류의 알고리즘은 실제로이 비교 함수가 안정적이며 항상 동일한 결과를 반환한다고 기대합니다!

IEnumerableSorter가 예를 들어 quicksort의 각 알고리즘 단계에 대해 비교 함수를 호출하고 매번 x => r.Next()이 매개 변수를 캐싱하지 않고 두 매개 변수에 대한 함수 를 호출 할 수 는 없습니다!

이 경우 정렬 알고리즘을 엉망으로 만들고 알고리즘이 기대하는 것보다 훨씬 나빠질 수 있습니다. 물론 결국에는 안정되어 무언가를 돌려줍니다.

나중에 새로운 "다음"함수에 디버깅 출력을 넣어서 확인할 수 있으므로 어떻게되는지 확인하십시오. Reflector에서 나는 그것이 어떻게 작동하는지 즉시 알 수 없었습니다.


1
그렇지 않은 경우 : internal override void ComputeKeys (TElement [] elements, int count); 선언 형식 : System.Linq.EnumerableSorter <TElement, TKEY이> 조립 : 퀵 그들을 정렬하기 전에 System.Core는, 버전 = 3.5.0.0이 기능은, 메모리를 소모하는 모든 키를 먼저 배열을 생성
— 기독교

알아두면 좋습니다. 여전히 구현 세부 사항 일 뿐이며, 이후 버전에서는 변경 될 수 있습니다!
— Blorgbeard는

-5

모든 스레드를 지우고 모든 새로운 테스트를 캐시하여 코드에서 시작하는 시작 시간

첫 번째 실패 코드. LINQPad에서 실행됩니다. 이 코드를 테스트하기 위해 따르는 경우.

Stopwatch st = new Stopwatch();
st.Start();
var r = new Random();
List<string[]> list = new List<string[]>();
list.Add(new String[] {"1","X"});
list.Add(new String[] {"2","A"});
list.Add(new String[] {"3","B"});
list.Add(new String[] {"4","C"});
list.Add(new String[] {"5","D"});
list.Add(new String[] {"6","E"});

//list.OrderBy (l => r.Next()).Dump();
list.OrderBy (l => Guid.NewGuid()).Dump();
st.Stop();
Console.WriteLine(st.Elapsed.TotalMilliseconds);

OrderBy (x => r.Next ())는 38.6528ms를 사용합니다.

list.OrderBy (x => Guid.NewGuid ())는 36.7634ms를 사용합니다 (MSDN에서 권장).

두 번째 이후에는 둘 다 동시에 사용됩니다.

편집 : Intel Core i7 4@2.1GHz의 테스트 코드, Ram 8GB DDR3 @ 1600, HDD SATA 5200rpm, [데이터 : www.dropbox.com/s/pbtmh5s9lw285kp/data]

using System;
using System.Runtime;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Threading;

namespace Algorithm
{
    class Program
    {
        public static void Main(string[] args)
        {
            try {
                int i = 0;
                int limit = 10;
                var result = GetTestRandomSort(limit);
                foreach (var element in result) {
                    Console.WriteLine();
                    Console.WriteLine("time {0}: {1} ms", ++i, element);
                }
            } catch (Exception e) {
                Console.WriteLine(e.Message);
            } finally {
                Console.Write("Press any key to continue . . . ");
                Console.ReadKey(true);
            }
        }

        public static IEnumerable<double> GetTestRandomSort(int limit)
        {
            for (int i = 0; i < 5; i++) {
                string path = null, temp = null;
                Stopwatch st = null;
                StreamReader sr = null;
                int? count = null;
                List<string> list = null;
                Random r = null;

                GC.Collect();
                GC.WaitForPendingFinalizers();
                Thread.Sleep(5000);

                st = Stopwatch.StartNew();
                #region Import Input Data
                path = Environment.CurrentDirectory + "\\data";
                list = new List<string>();
                sr = new StreamReader(path);
                count = 0;
                while (count < limit && (temp = sr.ReadLine()) != null) {
//                  Console.WriteLine(temp);
                    list.Add(temp);
                    count++;
                }
                sr.Close();
                #endregion

//              Console.WriteLine("--------------Random--------------");
//              #region Sort by Random with OrderBy(random.Next())
//              r = new Random();
//              list = list.OrderBy(l => r.Next()).ToList();
//              #endregion

//              #region Sort by Random with OrderBy(Guid)
//              list = list.OrderBy(l => Guid.NewGuid()).ToList();
//              #endregion

//              #region Sort by Random with Parallel and OrderBy(random.Next())
//              r = new Random();
//              list = list.AsParallel().OrderBy(l => r.Next()).ToList();
//              #endregion

//              #region Sort by Random with Parallel OrderBy(Guid)
//              list = list.AsParallel().OrderBy(l => Guid.NewGuid()).ToList();
//              #endregion

//              #region Sort by Random with User-Defined Shuffle Method
//              r = new Random();
//              list = list.Shuffle(r).ToList();
//              #endregion

//              #region Sort by Random with Parallel User-Defined Shuffle Method
//              r = new Random();
//              list = list.AsParallel().Shuffle(r).ToList();
//              #endregion

                // Result
//              
                st.Stop();
                yield return st.Elapsed.TotalMilliseconds;
                foreach (var element in list) {
                Console.WriteLine(element);
            }
            }

        }
    }
}

결과 설명 : https://www.dropbox.com/s/9dw9wl259dfs04g/ResultDescription.PNG
결과 통계 : https://www.dropbox.com/s/ewq5ybtsvesme4d/ResultStat.PNG

결론 :
LINQ OrderBy (r.Next ()) 및 OrderBy (Guid.NewGuid ())는 첫 번째 솔루션의 사용자 정의 셔플 방법보다 나쁘지 않습니다.

답 : 모순입니다.


1
두 번째 옵션은 정확 하지 않으므로 성능은 관련 이 없습니다 . 또한 난수로 주문하는 것이 수용 가능한지, 효율적인지 또는 작동 방식에 대한 질문에 대답하지 않습니다. 첫 번째 솔루션은 정확성에 문제가있다,하지만 그들은 아니에요 으로 거래의 큰.
— Servy

죄송합니다. Linq OrderBy의 Quicksort에서 더 좋은 종류의 매개 변수가 무엇인지 알고 싶습니다. 성능을 테스트해야합니다. 그러나 int 유형은 Guid 문자열보다 속도가 빠르다고 생각하지만 그렇지 않습니다. MSDN이 권장하는 이유를 이해했습니다. 첫 번째 솔루션 편집 성능은 Random 인스턴스가있는 OrderBy와 동일합니다.
— GMzo

문제를 해결하지 못하는 코드의 성능을 측정하는 요점은 무엇입니까? 성능은 둘 다 작동하는 두 솔루션간에 만 고려할 사항 입니다. 당신이 솔루션을 일하고있을 때, 다음 을 수행 할 수 있습니다 시작 을 비교.
— Servy

더 많은 데이터를 테스트 할 시간이 있어야합니다. 완료되면 다시 게시 할 것을 약속드립니다. Linq OrderBy가 첫 번째 솔루션보다 나쁘지 않다고 생각합니다. 의견 : 사용하고 이해하기 쉽습니다.
— GMzo

매우 간단한 간단한 셔플 링 알고리즘보다 눈에 띄게 덜 효과적이지만 다시 한 번 성능은 관련 이 없습니다 . 성능이 떨어질뿐만 아니라 데이터를 안정적으로 섞지 않습니다.
— Servy
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.