金沙国际官网_金沙国际平台登录

因为这个金沙国际官网_金沙国际平台登录网站与很多的大型澳门赌场都有合作,金沙国际官网_金沙国际平台登录尽职尽责,高效执行,保持好奇心,不断学习,追求卓越,点击进入金沙国际官网_金沙国际平台登录马上体验吧,所以现在也正式地开始了营业。

您的位置:金沙国际官网 > 编程 > 自定义日期的时分秒

自定义日期的时分秒

发布时间:2019-10-28 22:02编辑:编程浏览(95)

    1 DateTime beginTime = DateTime.Now.Date;
    2 Console.WriteLine(beginTime);
    3 DateTime endTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 59, 59);
    4 Console.WriteLine(endTime);
    5 DateTime customTime = new DateTime(2018,09,03,14,30,20);
    6 Console.WriteLine(customTime);
    

    定义函数:

    写在前面

    整个项目都托管在了 Github 上:
    查找更为方便的版本见:
    这一节内容可能会用到的库文件有 Quick,同样在 Github 上可以找到。
    善用 Ctrl + F 查找题目。

    图片 1

     

    习题&题解

    #语法
    def 函数名(参数1,参数2,参数3,...):
        '''注释'''
        函数体
        return 返回的值
    
    #函数名要能反映其意义
    

    2.3.1

    过程:就是没有返回值的函数

    题目

    按照 Partition() 方法的轨迹的格式给出该方法是如何切分数组 E A S Y Q U E S T I O N 的。

     

    解答

    图片 2

     

    2.3.2

    函数的参数:

    题目

    按照本节中快速排序所示轨迹的格式给出快速排序是如何将数组 E A S Y Q U E S T I O N 排序的(出于练习的目的,可以忽略开头打乱数组的部分)。

    • 形参:只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只在函数内部有效。
    • 实参:可以是常量、变量、表达式、函数等,在进行函数调用时,都必须有确定的值,以便把这些值传给形参。 
    解答

    图片 3

     

    2.3.3

    图片 4

    题目

    对于长度为 N 的数组,在 Quick.sort() 执行时,其最大元素最多会被交换多少次?

     

    解答

    N / 2
    在快速排序中,一个元素要被交换,有以下两种情况
    1.该元素是枢轴,在切分的最后一步被交换
    2.该元素位于枢轴错误的一侧,需要被交换到另一侧去
    注意,以上两种情况在一次切分中只会出现一次

    首先来看第一种情况,如果一个元素变成了枢轴
    那么在之后的切分中该元素会被排除,不存在后续的交换。
    因此我们的目标应该是:
    最大的元素总是出现在错误的一侧,同时切分的次数尽可能多。

    接下来我们来思考如何构造这样的数组
    由于我们针对的是最大的元素,因此「错误的一侧」就是枢轴的左侧。
    为了使切分的次数尽可能多,我们需要保持最大值移动的距离尽量短。
    但如果每次只移动一位的话,下一次切分时最大值就会变成枢轴
    例如 4 10 3 5 6,枢轴为 4,交换后数组变为:
    4 3 10 5 6
    随后 4 和 3 交换
    3 4 10 5 6
    下一次切分时 10 会变成枢轴,不再参与后续的切分。
    因此我们需要让最大值每次移动两个元素。

    考虑下面的数组:
    2 10 4 1 6 3 8 5 7 9
    第一次切分的时候,枢轴为 2,10 和 1 进行交换
    数组变为:
    2 1 4 10 6 3 8 5 7 9
    随后枢轴交换,数组变为:
    1 2 4 10 6 3 8 5 7 9
    第二次切分,枢轴为 4,10 和 3 进行交换。
    1 2 4 3 6 10 8 5 7 9
    随后枢轴交换 数组变为:
    1 2 3 4 6 10 8 5 7 9
    第三次切分,枢轴为 6,10 和 5 交换
    1 2 3 4 6 5 8 10 7 9
    随后枢轴交换,数组变为:
    1 2 3 4 5 6 8 10 7 9
    第四次切分,枢轴为 8,10 和 7 交换
    1 2 3 4 5 6 8 7 10 9
    枢轴交换,数组变为
    1 2 3 4 5 6 7 8 10 9
    最后一次切分,枢轴为 10,直接交换
    1 2 3 4 5 6 7 8 9 10

    我们可以总结出要构造这样的数组的模板
    a2 max a3 a1
    其中 a1 < a2 < a3 < max
    max 每轮切分移动两格,总共切分 N/ 2 次。

    def test(x,y,z): #x=1,y=2,z=3
        print(x)
        print(y)
        print(z)
    
    #位置参数,必须一一对应,缺一不行多一也不行
    test(1,2,3)
    
    #关键字参数,无须一一对应,缺一不行多一也不行
    test(y=1,x=2,z=3)
    
    #输出结果:
    1
    2
    3
    2
    1
    3
    
    另请参阅

    Number of largest element exchanges for quicksort-Stack Overflow

     

    2.3.4

     

    题目

    假如跳过开头打乱数组的操作,
    给出六个含有 10 个元素的数组,
    使得 Quick.sort() 所需的比较次数达到最坏情况。

    #参数组:**字典 *列表
    def test(x,*args,**kwargs):
        print(x)
        print(args,args[-1])
        print(kwargs,kwargs.get('s'))
    test(1,*[1,2,3],**{'s':1})
    
    #输出结果:
    1
    (1, 2, 3) 3
    {'s': 1} 1
    
    解答

    每次只让枢轴变为已排序,这就是最坏情况。
    这种时候枢轴是当前子数组的最大值 / 最小值。
    由于在我们的实现中总是取子数组的第一个元素作为枢轴。
    因此一个已排序的数组可以达到最坏情况,比较次数达到 O(n^ 2)。
    如果换作取最后一个元素,最坏情况会变成逆序数组。

    我们的实现中如果碰到与枢轴相等的元素也会停止循环,
    因此如果数组中有重复的元素会减少比较次数。

    例如:

    1 2 3 4 5 6 7 8 9 10
    2 3 4 5 6 7 8 9 10 11
    3 4 5 6 7 8 9 10 11 12
    4 5 6 7 8 9 10 11 12 13
    5 6 7 8 9 10 11 12 13 14
    6 7 8 9 10 11 12 13 14 15
    

     

    另请参阅

    Analysis of Quicksort-khanacademy
    Worst case for QuickSort - When can it occur?-Stack Overflow

    全局变量和局部变量:

    在子程序中定义的变量称为局部变量,在程序的一开始定义的变量称为全局变量。

    全局变量作用域是整个程序,局部变量作用域是定义该变量的子程序。

    当全局变量与局部变量同名时:在定义局部变量的子程序内,局部变量起作用;在其它地方全局变量起作用。

     

    name='reese'
    def change_name():
        print('我的名字',name)
    change_name()
    
    
    def change_name():
        name='帅'
        print('我的名字',name)
    change_name()
    print(name)
    
    
    def change_name():
        global name
        name='帅'
        print('我的名字',name)
    change_name()
    print(name)
    
    #输出结果:
    我的名字 reese
    我的名字 帅
    reese
    我的名字 帅
    帅
    

     # 如果函数内部无global关键字,优先读取局部变量,能读取全局变量,无法对全局变量重新赋值;

        但对于可变类型(除数字、字符串、元祖,其他都可变),可对内部元素进行操作。

    # 如果函数内部有global关键字,变量本质上是全局变量,可读取可赋值。

    # 一般全局变量名用大写,局部变量名用小写。

     

    2.3.5

     

    题目

    给出一段代码将已知只有两种主键值的数组排序。

    递归 

    #递归调用
    
    def calc(n):
        print(n)
        if int(n / 2) == 0:
            return n
        s = calc(int(n / 2))
        return s
    
    
    calc(10)
    
    #输出:
    10
    5
    2
    1
    

     

    递归特性:

    1. 必须有一个明确的结束条件

    2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

    1. 递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,

    每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)

     

    #问路
    
    import time
    
    person_list=['林克','士官长','奎爷','但丁']
    def ask_way(person_list):
        print('-'*60)
        if len(person_list) == 0:
            return '没人知道'
        person=person_list.pop(0)
        if person == '但丁':
            return '%s说:我知道,路在脚下,走多了,也就知道了' %person
        print('hi 美男[%s],敢问路在何方' %person)
        print('%s回答道:我不知道,但念你慧眼识猪,你等着,我帮你问问%s...' %(person,person_list))
        time.sleep(3)
        res=ask_way(person_list)
        # print('%s问的结果是: %res' %(person,res))
        return res
    
    
    
    res=ask_way(person_list)
    
    print(res)
    

     

    解答

    官方实现:

    算法 gif 动图
    图片 5

    函数作用域

     

    作用域在定义函数时就已经固定住了,不会随着调用位置的改变而改变

    name = "reese"
    def s():
        name = "neo"
        def n():
            print(name)
        return n
    
    func = s()
    func()
    
    #输出:
    neo
    

     

    代码
    namespace Quick
    {
        /// <summary>
        /// 用于将只有两种元素的数组排序。
        /// </summary>
        public class Sort2Distinct : BaseSort
        {
            /// <summary>
            /// 默认构造函数。
            /// </summary>
            public Sort2Distinct() { }
    
            /// <summary>
            /// 对数组 a 进行排序。
            /// </summary>
            /// <typeparam name="T">数组 a 的元素类型。</typeparam>
            /// <param name="a"></param>
            public override void Sort<T>(T[] a)
            {
                int lt = 0, gt = a.Length - 1;
                int i = 0;
                while (i <= gt)
                {
                    int cmp = a[i].CompareTo(a[lt]);
                    if (cmp < 0)
                        Exch(a, lt++, i++);
                    else if (cmp > 0)
                        Exch(a, i, gt--);
                    else
                        i++;
                }
            }
        }
    }
    

     

    另请参阅

    Quick 库

    匿名函数:

    不需要显示的指定函数

    def calc(x):
        return x + 10
    
    
    res = calc(10)
    print(res)
    
    #输出:
    20
    
    
    #用匿名函数:
    func = lambda x: x + 10
    print(func(10))
    
    #输出:
    20
    

     

    func = lambda x, y, z: x + y + z
    print(func(1, 2, 3))
    
    #输出:
    6
    

     

     

    2.3.6

     

    题目

    编写一段代码来计算 $ C_N $ 的准确值,
    在 $ N=100、1000 $ 和 $10 000 $ 的情况下比较准确值和估计值 $ 2NlnN $ 的差距。

    map函数:

     

    num = [3, 4, 5, 6, 11, 7, 54]
    #lambda x:x+1
    def add_one(x):   #列表元素自增一
        return x + 1
    
    #lambda x:x-1
    def minus_one(x):  #列表元素自减一
        return x - 1
    
    
    def map_test(func, array):
        ret = []
        for i in num:
            res = func(i)
            ret.append(res)
        return ret
    print(map_test(add_one,num))
    # print(map_test(lambda x:x+1,num)) #可用匿名函数
    print(map_test(minus_one,num))
    # print(map_test(lambda x:x-1,num))
    
    
    #终极版本
    def map_test(func,array):
        ret = []
        for i in array:
            res = func(i)
            ret.append(res)
        return ret
    
    print(map_test(lambda x:x+1,num))
    
    #输出结果:
    [4, 5, 6, 7, 12, 8, 55]
    [2, 3, 4, 5, 10, 6, 53]
    [4, 5, 6, 7, 12, 8, 55]
    

     

     map:

    处理序列中的每个元素,得到的结果是一个列表,该列表元素个数及位置与原来一样

    num = [3, 4, 5, 6, 11, 7, 54]
    res=map(lambda x:x+1,num)
    print('内置函数map,处理结果',list(res))
    
    print(list(map(minus_one,num)))
    
    msg = "reese"
    print(list(map(lambda x:x.upper(),msg)))
    
    #输出结果:
    内置函数map,处理结果 [4, 5, 6, 7, 12, 8, 55]
    [2, 3, 4, 5, 10, 6, 53]
    ['R', 'E', 'E', 'S', 'E']
    

     

    解答

    运行结果如下:
    图片 6

    filter函数:

    便利序列中的每个元素,判断每个元素得到布尔值,如果是True则留下来

    people = ['reese', 'neo_s', '林克']
    print(filter(lambda n: not n.endswith('s'), people))
    
    res = filter(lambda n: not n.endswith('s'), people)
    print(list(res))
    
    print(list(filter(lambda n: not n.endswith('s'), people)))
    
    #输出:
    <filter object at 0x04E612B0>
    ['reese', '林克']
    ['reese', '林克']
    

     

     

    代码

    新建一个 QuickSortAnalyze 类,在 QuickSort 的基础上添加一个 CompareCount 属性,用于记录比较次数。重写 Less 方法,每调用一次就让 CompareCount 增加 1 。

    using System;
    using System.Diagnostics;
    
    namespace Quick
    {
        /// <summary>
        /// 自动记录比较次数以及子数组数量的快速排序类。
        /// </summary>
        public class QuickSortAnalyze : BaseSort
        {
            /// <summary>
            /// 比较次数。
            /// </summary>
            public int CompareCount { get; set; }
    
            /// <summary>
            /// 是否启用打乱。
            /// </summary>
            public bool NeedShuffle { get; set; }
    
            /// <summary>
            /// 是否显示轨迹。
            /// </summary>
            public bool NeedPath { get; set; }
    
            /// <summary>
            /// 大小为 0 的子数组数量。
            /// </summary>
            public int Array0Num { get; set; }
    
            /// <summary>
            /// 大小为 1 的子数组数量。
            /// </summary>
            public int Array1Num { get; set; }
    
            /// <summary>
            /// 大小为 2 的子数组数量。
            /// </summary>
            public int Array2Num { get; set; }
    
            /// <summary>
            /// 默认构造函数。
            /// </summary>
            public QuickSortAnalyze()
            {
                this.CompareCount = 0;
                this.NeedShuffle = true;
                this.NeedPath = false;
                this.Array0Num = 0;
                this.Array1Num = 0;
                this.Array2Num = 0;
            }
    
            /// <summary>
            /// 用快速排序对数组 a 进行升序排序。
            /// </summary>
            /// <typeparam name="T">需要排序的类型。</typeparam>
            /// <param name="a">需要排序的数组。</param>
            public override void Sort<T>(T[] a)
            {
                this.Array0Num = 0;
                this.Array1Num = 0;
                this.Array2Num = 0;
                this.CompareCount = 0;
                if (this.NeedShuffle)
                    Shuffle(a);
                if (this.NeedPath)
                {
                    for (int i = 0; i < a.Length; i++)
                    {
                        Console.Write("  ");
                    }
                    Console.WriteLine("tlotjthi");
                }
                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>
            private void Sort<T>(T[] a, int lo, int hi) where T : IComparable<T>
            {
                if (hi - lo == 1)
                    this.Array2Num++;
                else if (hi == lo)
                    this.Array1Num++;
                else if (hi < lo)
                    this.Array0Num++;
    
                if (hi <= lo)                   // 别越界
                    return;
                int j = Partition(a, lo, hi);
                if (this.NeedPath)
                {
                    for (int i = 0; i < a.Length; i++)
                    {
                        Console.Write(a[i] + " ");
                    }
                    Console.WriteLine("t" + lo + "t" + j + "t" + 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;
                T 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;
            }
    
            /// <summary>
            /// 打乱数组。
            /// </summary>
            /// <typeparam name="T">需要打乱的数组类型。</typeparam>
            /// <param name="a">需要打乱的数组。</param>
            private void Shuffle<T>(T[] a)
            {
                Random random = new Random();
                for (int i = 0; i < a.Length; i++)
                {
                    int r = i + random.Next(a.Length - i);
                    T temp = a[i];
                    a[i] = a[r];
                    a[r] = temp;
                }
            }
    
            /// <summary>
            /// 比较第一个元素是否小于第二个元素。
            /// </summary>
            /// <typeparam name="T">要比较的元素类型。</typeparam>
            /// <param name="a">第一个元素。</param>
            /// <param name="b">第二个元素。</param>
            /// <returns></returns>
            new protected bool Less<T>(T a, T b) where T : IComparable<T>
            {
                this.CompareCount++;
                return a.CompareTo(b) < 0;
            }
        }
    }
    

    主方法

    using System;
    using Quick;
    
    namespace _2._3._6
    {
        /*
         * 2.3.6
         * 
         * 编写一段代码来计算 C_N 的准确值,
         * 在 N=100、1000 和 10 000 的情况下比较准确值和估计值 2NlnN 的差距。
         * 
         */
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Nt准确值t估计值t比值");
                QuickSortAnalyze sort = new QuickSortAnalyze();
                int N = 100;
                int trialTime = 500;
                for (int i = 0; i < 3; i++)
                {
                    int sumOfCompare = 0;
                    int[] a = new int[N];
                    for (int j = 0; j < trialTime; j++)
                    {
                        for (int k = 0; k < N; k++)
                        {
                            a[k] = k;
                        }
                        SortCompare.Shuffle(a);
                        sort.Sort(a);
                        sumOfCompare += sort.CompareCount;
                    }
                    int averageCompare = sumOfCompare / trialTime;
                    double estimatedCompare = 2 * N * Math.Log(N);
                    Console.WriteLine(N + "t" + averageCompare + "t" + (int)estimatedCompare + "t" + averageCompare / estimatedCompare);
                    N *= 10;
                }
            }
        }
    }
    

     

    另请参阅

    Quick 库

    reduce函数:

    处理一个序列,然后把序列进行合并操作

    #  reduce函数
    from functools import reduce
    
    num = [1, 2, 3, 4, 5]
    print(reduce(lambda x, y: x + y, num, ))
    
    #输出:
    15
    

     

    2.3.7

    内置函数:

    图片 7

     

     

     

     

    题目

    在使用快速排序将 N 个不重复的元素排序时,
    计算大小为 0、1 和 2 的子数组的数量。如果你喜欢数学,请推导;
    如果你不喜欢,请做一些实验并提出猜想。

    解答

    我讨厌数学= =

    证明:
    我们设 $ C_0(n) $ 代表将 $ n $ 个不重复元素排序时大小为 0 的数组的数量。
    同理有 $ C_1(n) $ 和 $ C_2(n) $ 代表大小为 1 的数组的数量以及大小为 2 的数组的数量。
    设 k 代表切分位置,显然切分位置随机且概率相等,在 1~n 之间均匀分布。
    根据条件,$ C_0(n), C_1(n),C_2(n) $ 都满足下式:
    [ C(n)= frac{sum_{k=1}^{n}(C(k-1)+C(n-k))}{n} ]
    根据快速排序算法, $ sum_{k=1}^{n}C(k-1)=sum_{k=1}^{n}C(n-k) $ ,因此
    [ C(n)=frac{2sum_{k=1}^{n}C(k-1)}{n}\ nC(n)=2sum_{k-1}^{n}C(k-1) ]
    同理代入 $ n-1 $ 有
    [ (n-1)C(n-1)=2sum_{k-1}^{n-1}C(k-1) ]
    相减
    [ nC(n)-(n-1)C(n-1)=2C(n-1)\ C(n)=frac{n+1}{n}C(n-1) ]
    利用累乘法求到通项公式
    [ frac{C(n)}{C(n-1)}=frac{n+1}{n} \ frac{C(n)}{C(n-1)}timesfrac{C(n-1)}{C(n-2)}timesdotstimesfrac{C(m+1)}{C(m)}= frac{n+1}{n}timesfrac{n}{n-1}timesdotstimesfrac{m+2}{m+1}\ frac{C(n)}{C(m)}=frac{n+1}{m+1}\ C(n)=C(m)frac{n+1}{m+1},n>m ]
    对于 $ C_0(n) $ ,我们有初始条件 $ C_0(0)=1, C_0(1)=0,C_0(2)=C_0(0)+C_0(1)=1 $
    [ C_0(n)=frac{n+1}{3}, n>2 ]
    对于 $ C_1(n) $ ,我们有初始条件 $ C_1(0)=0,C_1(1)=1,C_1(2)=C_1(0)+C_1(1)=1 $
    [ C_1(n)=frac{n+1}{3},n>2 ]
    对于 $ C_2(n) $ ,我们有初始条件 $ C_2(0)=C_2(1)=0,C_2(2)=1,C_2(3)=frac{2times(C_2(0)+C_2(1)+C_2(2))}{3}=frac{2}{3} $
    [ C_2(n)=frac{n+1}{6},n>3 ]
    结论
    [ C_0(n)=C_1(n)=frac{n+1}{3},n>2 \ C_2(n)=frac{n+1}{6},n>3 ]
    实验结果:
    图片 8

    代码

    QuickSortAnalyze 类,添加了三个属性用于计算数组数量。

    using System;
    using System.Diagnostics;
    
    namespace Quick
    {
        /// <summary>
        /// 自动记录比较次数以及子数组数量的快速排序类。
        /// </summary>
        public class QuickSortAnalyze : BaseSort
        {
            /// <summary>
            /// 比较次数。
            /// </summary>
            public int CompareCount { get; set; }
    
            /// <summary>
            /// 是否启用打乱。
            /// </summary>
            public bool NeedShuffle { get; set; }
    
            /// <summary>
            /// 是否显示轨迹。
            /// </summary>
            public bool NeedPath { get; set; }
    
            /// <summary>
            /// 大小为 0 的子数组数量。
            /// </summary>
            public int Array0Num { get; set; }
    
            /// <summary>
            /// 大小为 1 的子数组数量。
            /// </summary>
            public int Array1Num { get; set; }
    
            /// <summary>
            /// 大小为 2 的子数组数量。
            /// </summary>
            public int Array2Num { get; set; }
    
            /// <summary>
            /// 默认构造函数。
            /// </summary>
            public QuickSortAnalyze()
            {
                this.CompareCount = 0;
                this.NeedShuffle = true;
                this.NeedPath = false;
                this.Array0Num = 0;
                this.Array1Num = 0;
                this.Array2Num = 0;
            }
    
            /// <summary>
            /// 用快速排序对数组 a 进行升序排序。
            /// </summary>
            /// <typeparam name="T">需要排序的类型。</typeparam>
            /// <param name="a">需要排序的数组。</param>
            public override void Sort<T>(T[] a)
            {
                this.Array0Num = 0;
                this.Array1Num = 0;
                this.Array2Num = 0;
                this.CompareCount = 0;
                if (this.NeedShuffle)
                    Shuffle(a);
                if (this.NeedPath)
                {
                    for (int i = 0; i < a.Length; i++)
                    {
                        Console.Write("  ");
                    }
                    Console.WriteLine("tlotjthi");
                }
                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>
            private void Sort<T>(T[] a, int lo, int hi) where T : IComparable<T>
            {
                if (hi - lo == 1)
                    this.Array2Num++;
                else if (hi == lo)
                    this.Array1Num++;
                else if (hi < lo)
                    this.Array0Num++;
    
                if (hi <= lo)                   // 别越界
                    return;
                int j = Partition(a, lo, hi);
                if (this.NeedPath)
                {
                    for (int i = 0; i < a.Length; i++)
                    {
                        Console.Write(a[i] + " ");
                    }
                    Console.WriteLine("t" + lo + "t" + j + "t" + 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;
                T 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;
            }
    
            /// <summary>
            /// 打乱数组。
            /// </summary>
            /// <typeparam name="T">需要打乱的数组类型。</typeparam>
            /// <param name="a">需要打乱的数组。</param>
            private void Shuffle<T>(T[] a)
            {
                Random random = new Random();
                for (int i = 0; i < a.Length; i++)
                {
                    int r = i + random.Next(a.Length - i);
                    T temp = a[i];
                    a[i] = a[r];
                    a[r] = temp;
                }
            }
    
            /// <summary>
            /// 比较第一个元素是否小于第二个元素。
            /// </summary>
            /// <typeparam name="T">要比较的元素类型。</typeparam>
            /// <param name="a">第一个元素。</param>
            /// <param name="b">第二个元素。</param>
            /// <returns></returns>
            new protected bool Less<T>(T a, T b) where T : IComparable<T>
            {
                this.CompareCount++;
                return a.CompareTo(b) < 0;
            }
        }
    }
    

    主方法

    using System;
    using Quick;
    
    namespace _2._3._7
    {
        /*
         * 2.3.7
         * 
         * 在使用快速排序将 N 个不重复的元素排序时,
         * 计算大小为 0、1 和 2 的子数组的数量。
         * 如果你喜欢数学,请推导;
         * 如果你不喜欢,请做一些实验并提出猜想。
         * 
         */
        class Program
        {
            static void Main(string[] args)
            {
                // 证明
                // 我们设 C0(n) 代表将 n 个不重复元素排序时大小为 0 的数组的数量。
                // 同理有 C1(n) 和 C2(n) 代表大小为 1 的数组的数量和大小为 2 的数组的数量。
                // 设 k 代表切分位置,显然切分位置随机且概率相等,在 1~n 之间均匀分布。
                // 根据条件,三者都满足下式。
                // C(n) = 1/n sum(C(k - 1) + C(n - k)), k=1,2,...,n
                // 显然 sum(C(k - 1)) = sum(C(n - k)), k=1,2,...,n
                // 于是可以化简为
                // C(n) = 2/n sum(C(k - 1)), k=1,2,...,n
                // nC(n) = 2 * sum(C(k-1)), k=1,2,...,n
                // 同理有
                // (n-1)C(n-1) = 2 * sum(C(k-1)), k = 1,2,...,n-1
                // 相减得到递推式
                // nC(n) - (n-1)C(n-1) = 2*C(n-1)
                // C(n) = (n+1)/n * C(n-1)
                // 利用累乘法可以求得通项公式
                // C(n)=C(k)*(n+1)/(k+1), n>k
                // 对于 C0 有 C0(0)=1, C0(1)=0
                // C0(2)=C(0)+C(1)=1
                // C0(n)=(n+1)/3, n>2
                // 对于 C1 有 C1(0)=0, C1(1)=1
                // C1(2)=C1(0)+C1(1)=1
                // C1(n)=(n+1)/3, n>2
                // 对于 C2 有 C2(0)=C2(1)=0, C2(2)=1
                // C2(3)=1/3*2*(C2(0)+C2(1)+C2(2))=2/3
                // C2(n)=C2(3)*(n+1)/4=(n+1)/6, n>3
                // 结论
                // C0(n)=C1(n)=(n+1)/3, C2(n)=(n+1)/6
                int n = 1000;
                QuickSortAnalyze sort = new QuickSortAnalyze();
                Console.WriteLine("nt0t1t2");
                for (int i = 0; i < 5; i++)
                {
                    int[] a = new int[n];
                    for (int j = 0; j < n; j++)
                    {
                        a[j] = j;
                    }
                    SortCompare.Shuffle(a);
                    sort.Sort(a);
                    Console.WriteLine(n + "t" + sort.Array0Num + "t" + sort.Array1Num + "t" + sort.Array2Num);
                    n *= 2;
                }
            }
        }
    }
    
    另请参阅

    Quick 库
    What is the expected number of subarrays of size 0, 1 and 2 when quicksort is used to sort an array of N items with distinct keys?-Stack Overflow

    2.3.8

    题目

    Quick.sort() 在处理 N 个全部重复的元素时大约需要多少次比较?

    解答

    每次切分都会把数组平分,共切分 logN 次(二分法),每次切分比较 N 次(i 和 j 会一位一位地从两边向中间靠拢)。
    共比较 NlogN 次。

    2.3.9

    题目

    请说明 Quick.sort() 在处理只有两种主键值时的行为,以及在处理只有三种主键值的数组时的行为。

    解答

    切分时,枢轴左侧都是小于(或等于)枢轴的,
    右侧都是大于(或等于)枢轴的
    只有两种主键值时,
    第一次切分之后,某一侧的元素将全部相同
    (如果枢轴选了较大的,那么右侧将全部相同,反之则左侧全部相同)

    只有三种主键值时,和一般快速排序并无不同。
    但如果第一次切分时选择了中间值作为枢轴,且中间值只有一个
    那么只需要一次切分数组便会有序。

    2.3.10

    题目

    Chebyshev 不等式表明,一个随机变量的标准差距离均值大于 k 的概率小于 1/k^2 。
    对于 N=100 万,用 Chebyshev 不等式计算快速排序所使用的比较次数大于 1000 亿次的概率(0.1N^2)。

    解答

    切比雪夫不等式(Chebyshev’s inequality)
    [ P(|X-mu|geq ksigma)leq frac{1}{k^2} ]
    其中,$ mu $ 代表期望,$ sigma $ 代表标准差。
    对于快速排序的比较次数来说,$ mu = 2Nln N $ ,$ sigma=0.65N $。
    (这两个结论见 2.3 节的命题 K 和命题 L)
    题目中要求比较次数大于 $ 0.1N^2 $ ,可以求得 $ k $ 的值。
    [ 0.65kN=0.1N^2 \ k=frac{2N}{13} ]
    将 $ N=1,000,000 $ 代入
    [ P(|X-27,631,021|geq 100,000,000,000)leq 0.00000000004225 ]

    另请参阅

    切比雪夫不等式到底是个什么概念? - 马同学的回答 - 知乎

    2.3.11

    题目

    假如在遇到和切分元素重复的元素时我们继续扫描数组而不是停下来,
    证明使用这种方法的快速排序在处理只有若干种元素值的数组时运行时间是平方级别的。

    解答

    只有若干种元素值意味着大量的连续重复。
    (由于存在打乱这一步骤,不存在连续重复的可能性是很低的)
    接下来我们考虑这样的连续重复在修改后的快排下的性能。
    1 1 1 1 1 1 1
    对于这样的数组,枢轴选为 1,j 将会在 j = lo 处终止。
    因此最后的结果将是每次只有数组的第一个元素被排序
    已知每次切分都是 O(k - 1) 的(i 和 j 都将走完整个子数组)
    因此这样的快速排序所需时间 = $ 2 (N - 1 + N - 2 + cdots + 1) = (N - 1)N $
    因此对于值相同的子数组,这样的快排运行时间是平方级别的
    那么当数组中这样的连续重复内容越多,运行时间就越接近平方级别。

    2.3.12

    题目

    按照代码所示轨迹的格式给出信息量最佳的快速排序第一次是如何切分
    数组 B A B A B A B A C A D A B R A 的。

    解答

    图片 9

    本文由金沙国际官网发布于编程,转载请注明出处:自定义日期的时分秒

    关键词:

上一篇:使用任务并行库,构造函数赋值

下一篇:没有了