문제

첫째,내가 알지에 대한 피셔-Yates 셔플.하지만 말할 수 있습에 대한 인수 위해 원하는 사용자가 선택하는 일종 옵션에서 드롭다운 목록입니다.이 목록에 포함하고"무작위"옵션을 선택합니다.의 결과에 따라 자신의 선택은 내가 원하는 대신에 IComparer 인스턴스에 대한 내합니다.어떤 것 IComparer 처럼 보이나요?

Google 제공의 과다는 결함이 있는 결과 모두 이 양식은:

public class NaiveRandomizer<T> : IComparer<T>
{
    private static Random rand = new Random();

    public int Compare(T x, T y)
    {
        return (x.Equals(y))?0:rand.Next(-1, 2);
    }
}

그러나,그 구현의 편견입니다 심지어는 예외에서 어떤 상황이다.편견을 입증하실 수 있습으로 다음과 같은 코드:

void Test()
{
    Console.WriteLine("NaiveRandomizer Test:");
    var data = new List<int>() {1,2,3};
    var sortCounts = new Dictionary<string, int>(6);
    var randomly = new NaiveRandomizer<int>();

    for (int i=0;i<10000;i++)
    {   //always start with same list, in _the same order_.
        var dataCopy = new List<int>(data); 
        dataCopy.Sort(randomly);

        var key = WriteList(dataCopy);
        if (sortCounts.ContainsKey(key))
            sortCounts[key]++;
        else
            sortCounts.Add(key, 1);
    }

    foreach (KeyValuePair<string, int> item in sortCounts)
        Console.WriteLine(item.Key + "\t" + item.Value);
}

string WriteList<T>(List<T> list)
{
   string delim = "";
   string result = "";
   foreach(T item in list)
   {
       result += delim + item.ToString();
       delim = ", ";
   }
   return result;
}

그래서 어떻게 구현할 random IComparer<T> 는 그 문제를 해결?그것이 허용되는 필요한 각각 전화 .Sort() 을 사용하여 별도의 IComparer 인스턴스,으로 보이지 않는 다른 방법으로 이렇게하려면:항목 것에 비해 어떤 다른 사람을 사용하여,진정으로 임의의 값을 가지고 있지만,그 값 또한 일관된 품목에 대한 내에 주어진 정렬 작업입니다.

가 시작 , 지만,그것은에 게시,서둘러 가입 매우 느린,지 않도 반환 가능한 모든 종류(시험 보여주는 그 이상을 제거 바이어스지 않는 경우에,수 없는 옵션).난 기대하지 않는 O(n)성능과 같은 피셔-Yates,하지만 난 뭔가를 원하고 합리적인(로그 n n a small-ish n),그리고 나는 그것을 기대하는 표시 가능한 모든 종류.불행하게도,는 링크는 현재 수락 응답에 대한 그것의 질문에 그래서 내가 기대할 수 있으로 조금 더입니다.

면 아무 것도,내가 이것을 원하는 자석이 될 모든 사람들을 위해 구글 쿼리를 찾고 IComparable 솔루션-는 그들은 여기까지는 대신 다른 어딘가에 그들에게 말을 사용하여 잘못된 버전입니다.

도움이 되었습니까?

해결책 2

내가 다른 곳에서 얻은 한 가지 제안은 단일 작업을 설명하는 별도의 iArranger 인터페이스를 만드는 것이 었습니다. 마련하다 컬렉션. ICOMPARER/ICOMPALY가 개별 항목 대신 전체 컬렉션에서 작동하기 때문에 작동 할 수없는 경우에 작동 할 수 있습니다. 다음과 같이 보일 수 있습니다.

public interface IArranger<T>
{
    IEnumerable<T> Arrange(IEnumerable<T> items);
}

그런 다음 구현할 수있었습니다 Shuffle 적절한 Fisher-Yates 알고리즘을 사용하여 Iarranger 인터페이스에서 각각을 추가로 감싸는 구현도 있습니다. IEnumerable.Sort()/IComparable/IComparer 내가 관심있는 품종. 그것은 다음과 같이 보일 수 있습니다.

public class ComparerArranger<T> : IArranger<T>
{
    private IComparer<T> comparer;

    public ComparableArranger(IComparer<T> comparer)
    {
        this.comparer = comparer;
    }

    public IEnumerable<T> Arrange(IEnumerable<T> items)
    {
       return items.OrderBy(i => i, comparer);
    }
}

또는

//uses the default Comparer for the type (Comparer<T>.Default)
public class TypeArranger<T> : IArranger<T> 
{
    public IEnumerable<T> Arrange(IEnumerable<T> items)
    {
       return items.OrderBy(i => i);
    }
}

또는

public class ShuffleArranger<T> : IArranger<T>
{
    //naive implementation for demonstration
    // if I ever develop this more completely I would try to
    // avoid needing to call .ToArray() in here
    // and use a better prng
    private Random r = new Random();

    public IEnumerable<T> Arrange(IEnumerable<T> items)
    {
        var values = items.ToArray();

        //valid Fisher-Yates shuffle on the values array
        for (int i = values.Length; i > 1; i--)
        {
            int j = r.Next(i);
            T tmp = values[j];
            values[j] = values[i - 1];
            values[i - 1] = tmp;
        }
        foreach (var item in values) yield return item;
    }
}

마지막 단계를 위해 확장 방법을 통해 이에 대한 지원을 추가합니다. 그런 다음 여전히 간단한 런타임 알고리즘 스왑을 받고 셔플 알고리즘을 더 잘 구현할 수 있으며 사용하는 코드는 자연 스럽습니다.

public static IEnumerable<T> Arrange(this IEnumerable<T> items, IArranger<T> arranger)
{
    return arranger.Arrange(items);
}

다른 팁

내가 다소 놀라에서 이 스레드 얼마나 많은 잘못된 응답이 게시한다.을 위해서 그냥 다른 사람의 해결책에 게시된 것과 유사한 의 OP,다음 코드는 정:

int[] nums = new int[1000];
for (int i = 0; i < nums.Length; i++)
{
    nums[i] = i;
}

Random r = new Random();
Array.Sort<int>(nums, (x, y) => r.Next(-1, 2));

foreach(var num in nums)
{
    Console.Write("{0} ", num);
}

그러나,코드 예외가 발생 때때로,항상은 아니지만.그게 무슨 재미를 디버그:)실행하는 경우 그것이 충분한 시간,또는 실행하는 절차를 정렬 루프에서 50 또는 그래서 시간이,당신은 오류 메시지가 나타납:

IComparer (or the IComparable methods it relies upon) did not return zero when Array.Sort called x. CompareTo(x). x: '0' x's type: 'Int32' The IComparer: ''.

다시 말해서,빠른 종류에 비해 몇 번호 x 자체에 있어 non-zero 결과입니다.분명한 솔루션 코드가 쓰기:

Array.Sort<int>(nums, (x, y) =>
    {
        if (x == y) return 0;
        else return r.NextDouble() < 0.5 ? 1 : -1;
    });

그러나 심지어이기 때문에 작동하지 않는 경우가 있다.순교 3 번호에 대하여 다른 하나는 반환하지 않는 등 A>B B>C,C>A(oops!).상관없이 사용하는 경우 Guid GetHashCode,또는 다른 임의로 생성 된 입력을 솔루션을 위와 같은 여전히 잘못입니다.


으로 말해서,피셔-예이츠는 표준 방법 셔플의 배열이 없다,그래서 진짜 이유를 사용하 IComparer 에서 첫 번째 장소입니다.Fisher-Yates O(n)면 어떤 사용하여 구현 IComparer 사용하는 퀵 비하인드 장면에서 시간 복잡도 O(n 로그 n).이 없이 좋은 이유로 사용하지 않는 잘 알려진,효율,표준를 해결하는 알고리즘의 이런 종류의 문제입니다.

그러나,당신이 정말로 주장을 사용하여 IComparer 고 랜드,다음을 적용하여 임의의 데이터 을 정렬합니다.이 필요로 투영의 데이터에 다른 객체를 잃게 하지 않습니다.임의의 데이터:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Pair<T, U>
    {
        public T Item1 { get; private set; }
        public U Item2 { get; private set; }
        public Pair(T item1, U item2)
        {
            this.Item1 = item1;
            this.Item2 = item2;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Pair<int, double>[] nums = new Pair<int, double>[1000];
            Random r = new Random();
            for (int i = 0; i < nums.Length; i++)
            {
                nums[i] = new Pair<int, double>(i, r.NextDouble());
            }

            Array.Sort<Pair<int, double>>(nums, (x, y) => x.Item2.CompareTo(y.Item2));

            foreach (var item in nums)
            {
                Console.Write("{0} ", item.Item1);
            }

            Console.ReadKey(true);
        }
    }
}

거나 LINQy 으로 사용:

Random r = new Random();
var nums = from x in Enumerable.Range(0, 1000)
           orderby r.NextDouble()
           select x;

Icomparer 요구 어느 시점에서 0 리턴 (t의 동일한 인스턴스). 수학적으로 Fisher-Yates를 통계적으로 흉내낼 수있는 일반적인 Icomparer를 만들 수 없습니다. 항상 편견이있을 것입니다. 실제 셔플의 경우 특정 값을 반환하도록 강요하고 싶지 않습니다.

숨겨진 필드를 기반으로 한 파운드 정렬은 임의의 값을 사전 할당하는 방법은 무엇입니까?

James Curran의 아이디어에 대한 후속 조치 : Icomparer가 "정렬 된"값을 목록으로 유지하도록하십시오. 새 값이 발생하면 임의의 위치에서 목록에 삽입하십시오. 목록 색인으로 비교하십시오. 목록을 균형 잡힌 트리 또는 무언가로 유지하여 최적화하십시오. 이러한 ICOMPARER의 모든 인스턴스는 일관되고 무작위 정렬 순서를 유지하므로 임의의 분류가 매번 동일한 임의의 순서 또는 다른 순서를 지속적으로 선택할 수 있도록 선택할 수 있습니다. 사소한 수정을 통해 "무작위"를 읽는 것을 선호하는 경우 동일한 요소가 다른 순서 위치로 "정렬"될 수 있습니다.

흥미로운 노력. 아마도 Icomparer의 오용/남용 일 가능성이 높습니다.

당신은 그 목적을 위해 만들어지지 않은 메커니즘을 사용하여 임의의 가중치를 만들려고 노력하고 있습니다.

자신의 정렬 루틴과 자신의 비교를 구현하지 않겠습니까? 나는 그것이 충분하지 않을 것이라고 생각합니다.

하지 마십시오.

지금까지 제안 된 모든 알고리즘은 출력에 대한 일종의 편견을 소개합니다 (일부는 다른 것보다 더 큽니다).

@princess와 @luke는 데이터와 함께 임의의 숫자를 저장할 것을 제안합니다. 그러나이 임의 숫자 중 두 가지가 다른 값과 동일한 값을 가질 가능성이 있기 때문에이 두 항목 사이의 정렬 순서는 결정적으로 편향됩니다.

정렬 루틴이 "안정적"인 경우 이에 대한 최악의 경우가 될 것입니다 (즉, 동등한 것으로 간주되는 객체는 항상 입력 한 순서로 출력됩니다). Array.Sort는 안정적이지 않지만 (내부적으로 QuickSort를 사용) 두 항목이 입력의 위치 (특히 QuickSort의 위치에 따라 위치에 따라 동일한 값을 가질 때마다 발생하는 바이어스가 있습니다. 피벗).

이 임의의 숫자에 대한 열쇠 공간이 증가함에 따라 충돌 가능성은 낮아 지지만 (무작위로 좋은 소스와 함께), 분류하는 값의 수가 증가함에 따라 생일 역설은 충돌하는 최소 한 쌍은 매우 빨리 올라갑니다.

정수 키의 경우 키에는 2^32 고유 한 값이 있으며, 75,000 행으로 임의 값이 완벽하게 분포되어 있다고 가정 할 때 충돌이있을 확률이 50%입니다. 위키 백과.

당신이 제안한 암호화 해시 접근법은 충돌 가능성이 높을 수있는 충분한 키 공간 (160) 비트를 가지고 있지만, 알고리즘은 모든 무작위성을 단일 INT로 다시 분해하기 전에 비교를 수행하는 이점을 부정하는 이점을 부정합니다. 그 큰 열쇠 공간.

가장 좋은 방법은 각각의 데이터 항목과 뚜렷한 "Sortorder"값을 연관시키는 것입니다. 각 데이터 항목은 입증 된 알고리즘을 사용하여 이러한 값을 셔플 한 다음 해당 값으로 결과를 주문하는 것입니다.

Array.Sort를 사용하는 경우 "키"배열과 "값"배열을 사용하는 오버로드가 있습니다. 키 어레이는 정상적으로 정렬되지만 키 어레이의 값이 이동할 때마다 값 배열의 해당 항목도 이동합니다.

같은 것 :


Something[] data;//populated somewhere
int[] keys = new int[data.Length];//or long if you might have lots of data
for(int i=0;i<keys.Length;++i) {
 keys[i] = i;
}

Shuffle(keys);

Array.Sort(keys, data);
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top