Перемешать с помощью IComparer

Если вы застряли с Python 2.5 или более ранними версиями: трюк обезьяны-патча, похоже, не работает с исходным модулем simplejson, если установлены ускорения C:

$ python
Python 2.5.4 (r254:67916, Jan 20 2009, 11:06:13) 
[GCC 4.2.1 (SUSE Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import simplejson
>>> simplejson.__version__
'2.0.9'
>>> simplejson._speedups
<module 'simplejson._speedups' from '/home/carlos/.python-eggs/simplejson-2.0.9-py2.5-linux-i686.egg-tmp/simplejson/_speedups.so'>
>>> simplejson.encoder.FLOAT_REPR = lambda f: ("%.2f" % f)
>>> simplejson.dumps([23.67, 23.97, 23.87])
'[23.670000000000002, 23.969999999999999, 23.870000000000001]'
>>> simplejson.encoder.c_make_encoder = None
>>> simplejson.dumps([23.67, 23.97, 23.87])
'[23.67, 23.97, 23.87]'
>>> 
11
задан Community 23 May 2017 в 12:31
поделиться

7 ответов

Одно предложение, которое я получил в другом месте, состояло в том, чтобы создать отдельный интерфейс IArranger, который описывает единственную операцию для Расположения набора. Это может работать, где IComparer/IComparable не может, потому что он воздействует на весь набор вместо отдельных объектов. Это могло бы выглядеть примерно так:

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

Затем я мог реализовать a Shuffle от интерфейса 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;
    }
}

Для заключительного шага я добавляю поддержку этого к любому IEnumerable с помощью дополнительного метода. Затем Вы все еще получаете простой свопинг алгоритма во время выполнения, у Вас есть лучшая реализация алгоритма перестановки, и код для использования его чувствует себя естественным:

public static IEnumerable<T> Arrange(this IEnumerable<T> items, IArranger<T> arranger)
{
    return arranger.Arrange(items);
}
3
ответ дан 3 December 2019 в 08:05
поделиться

Я был несколько удивлен в этом потоке, сколько неправильных ответов было отправлено. Только ради других, которые предлагают решение, подобное тому, отправленному 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 к себе и получил ненулевой результат. Очевидным решением кода была бы запись:

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

Но даже это не работает, потому что существуют случаи, где.NET сравнивает 3 числа друг с другом, который возвращает непоследовательные результаты, такие как A> B, B> C, и C> (ой!). Неважно, при использовании Гуида, GetHashCode, или какого-либо другого случайным образом сгенерированного входа, решения как, один показанный выше является все еще неправильным.


Так как это сказан, Фишер-Йетс является стандартным способом переставить массивы, таким образом, нет никакой настоящей причины для использования IComparer во-первых. Фишер-Йетс является O (n), тогда как любая реализация с помощью IComparer использует quicksort behinds сцены, который имеет временную сложность 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;
11
ответ дан 3 December 2019 в 08:05
поделиться

IComparer требование нулевого возврата в какой-то момент (для равных экземпляров T), делает математически невозможным создать универсальный IComparer, который будет подражать Перестановке Фишера-Йетса статистически. Всегда будет предвзятость. Для реальной перестановки Вы никогда не хотели бы вынуждать это возвратить какое-то конкретное значение.

1
ответ дан 3 December 2019 в 08:05
поделиться

Как насчет того, чтобы сортировать на основе скрытого поля, которое предписано случайное значение?

0
ответ дан 3 December 2019 в 08:05
поделиться

Следовать идее James Curran: позвольте IComparer поддержать "отсортированные" значения как список; если новое значение происходит, вставьте его в список в случайном положении; сравните индексом списка. Оптимизируйте путем ведения списка как сбалансированного дерева или чего-то. Каждый экземпляр такого IComparer поддержит последовательный и случайный порядок сортировки, таким образом, у Вас будет выбор разрешения Вашей Случайной сортировке последовательно быть тем же случайным упорядочиванием или другим каждый раз. Незначительная модификация даже позволит равным элементам быть "отсортированными" в различные положения упорядочивания, если Вы предпочтете читать "случайный" тот путь.

0
ответ дан 3 December 2019 в 08:05
поделиться

Интересное усилие. Скорее всего, неправильное употребление/злоупотребление IComparer.

Вы пытаетесь сделать случайный взвешенный вид при помощи механизма, который не был создан с этой целью.

Почему бы не реализовать Вашу собственную программу сортировки и Ваш собственный компаратор? У меня есть чувство, что даже, который был бы недостаточен.

0
ответ дан 3 December 2019 в 08:05
поделиться

Не делайте этого.

Все алгоритмы, предложенные к настоящему времени, вводят своего рода предвзятость в вывод (некоторые больше, чем другие).

@Princess и @Luke предлагают хранить случайное число вместе с данными. Однако, потому что существует возможность, что любые два из этих случайных чисел могли иметь то же значение, как другой, порядок сортировки между теми двумя объектами будет детерминировано смещен

Худший случай для этого был бы то, если программа сортировки "стабильна" (который является, который возражает, что считаются равными, всегда производятся в том же порядке, они были введены в). Массив. Вид, оказывается, не стабилен (он использует QuickSort внутренне), но существует все еще предвзятость, которая происходит каждый раз, когда два объекта имеют то же значение, которое зависит от того, где они находятся во входе (и конкретно где они относительно центра QuickSort).

Когда ключевое пространство для этого случайного числа увеличивается, вероятность коллизии понижается (с хорошим источником случайности), но имейте в виду, что, поскольку количество значений, которые Вы сортируете, повышается, парадокс дня рождения диктует, что вероятность по крайней мере одной пары среди них столкновение повышается очень быстро.

Для целочисленного ключа, существуют 2^32 уникальные значения для ключа и даже предполагая, что существует совершенно ровное распределение случайных значений с 75 000 строк, существует 50%-я вероятность, что будет коллизия. Википедия.

Криптографический подход хеша, который Вы предложили потенциально, имеет достаточно большое ключевое пространство (160) биты для создания шанса коллизии незначительным, но алгоритм разлагается, вся та случайность отступает к единственному интервалу прежде на самом деле сделать сравнивание, которое инвертирует преимущество того большего ключевого пространства.

Ваш лучший подход должен связаться, отличное значение "порядка сортировки" с каждым из Ваших элементов данных переставляют эти значения с помощью проверенного алгоритма и затем заказывают результаты тем значением.

Если Вы используете Массив. Вид, существует перегрузка, которая берет массив "ключей" и массив "значений". Массив ключей обычно сортируется, но каждый раз, когда значение в массиве ключей перемещено, соответствующая запись в массиве значений также перемещена.

Что-то как:


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);
0
ответ дан 3 December 2019 в 08:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: