2.3.29

2.3.29 #

解答 #

在快排类内部添加一个随机数发生器,每次随机取枢轴并交换至第一位进行切分。

private int Partition<T>(T[] a, int lo, int hi) where T : IComparable<T>
{
    int i = lo, j = hi + 1;
    var pivot = _randomGenerator.Next(hi - lo) + lo;
    Exch(a, pivot, lo);
    var v = a[lo];
    while (true)
    {
        while (Less(a[++i], v))
            if (i == hi)
                break;
        while (Less(v, a[--j]))
            if (j == lo)
                break;
        if (i >= j)
            break;
        Exch(a, i, j);
    }
    Exch(a, lo, j);
    return j;
}

测试结果:

代码 #

使用随机枢轴的快排

public class QuickSortRandomPivot : BaseSort
{
    /// <summary>
    /// 切换到插入排序的阈值。
    /// </summary>
    public int M { get; set; }

    /// <summary>
    /// 随机数发生器。
    /// </summary>
    private readonly Random _randomGenerator = new();

    /// <summary>
    /// 默认构造函数。
    /// </summary>
    public QuickSortRandomPivot()
    {
        M = 10;
    }

    /// <summary>
    /// 用快速排序对数组 a 进行升序排序。
    /// </summary>
    /// <typeparam name="T">需要排序的类型。</typeparam>
    /// <param name="a">需要排序的数组。</param>
    public override void Sort<T>(T[] a)
    {
        Sort(a, 0, a.Length - 1);
        Debug.Assert(IsSorted(a));
    }

    /// <summary>
    /// 用快速排序对数组 a 的 lo ~ hi 范围排序。
    /// </summary>
    /// <typeparam name="T">需要排序的数组类型。</typeparam>
    /// <param name="a">需要排序的数组。</param>
    /// <param name="lo">排序范围的起始下标。</param>
    /// <param name="hi">排序范围的结束下标。</param>
    protected void Sort<T>(T[] a, int lo, int hi) where T: IComparable<T>
    {
        if (hi <= lo)                   // 别越界
            return;
        if (hi - lo <= M)
        {
            // 调用插入排序
            for (var i = lo; i <= hi; i++)
            for (var k = i; k > lo && Less(a[k], a[k - 1]); k--)
                Exch(a, k, k - 1);
            return;
        }
        var j = Partition(a, lo, hi);
        Sort(a, lo, j - 1);
        Sort(a, j + 1, hi);
    }

    /// <summary>
    /// 对数组进行切分,返回枢轴位置。
    /// </summary>
    /// <typeparam name="T">需要切分的数组类型。</typeparam>
    /// <param name="a">需要切分的数组。</param>
    /// <param name="lo">切分的起始点。</param>
    /// <param name="hi">切分的末尾点。</param>
    /// <returns>枢轴下标。</returns>
    private int Partition<T>(T[] a, int lo, int hi) where T : IComparable<T>
    {
        int i = lo, j = hi + 1;
        var pivot = _randomGenerator.Next(hi - lo) + lo;
        Exch(a, pivot, lo);
        var v = a[lo];
        while (true)
        {
            while (Less(a[++i], v))
                if (i == hi)
                    break;
            while (Less(v, a[--j]))
                if (j == lo)
                    break;
            if (i >= j)
                break;
            Exch(a, i, j);
        }
        Exch(a, lo, j);
        return j;
    }
}

测试用例

Console.WriteLine("M\tN\tshuffle\trandom\tshuffle/random");
Trial(10);
Trial(20);
Trial(50);

// 进行一次测试。
static void Trial(int m)
{
    var withShuffle = new QuickSortInsertion();
    var randomPivot = new QuickSortRandomPivot();
    var trialTime = 5;

    // M=10
    withShuffle.M = m;
    randomPivot.M = m;
    double timeShuffle = 0;
    double timeRandomPivot = 0;
    for (var n = 1000; n < 10000000; n *= 10)
    {
        for (var i = 0; i < trialTime; i++)
        {
            var a = new int[n];
            var b = new int[n];
            for (var j = 0; j < n; j++)
            {
                a[j] = j;
            }

            Shuffle(a);
            a.CopyTo(b, 0);
            timeShuffle += SortCompare.Time(withShuffle, a);
            timeRandomPivot += SortCompare.Time(randomPivot, b);
        }

        timeShuffle /= trialTime;
        timeRandomPivot /= trialTime;
        Console.WriteLine(
            withShuffle.M
            + "\t"
            + n
            + "\t"
            + timeShuffle
            + "\t"
            + timeRandomPivot
            + "\t"
            + timeShuffle / timeRandomPivot);
    }
}

// 打乱数组。
static void Shuffle<T>(T[] a)
{
    var random = new Random();
    for (var i = 0; i < a.Length; i++)
    {
        var r = i + random.Next(a.Length - i);
        var temp = a[i];
        a[i] = a[r];
        a[r] = temp;
    }
}

另请参阅 #

Quick 库