2021-10-18

WARNING: This article may be obsolete
This post was published in 2021-10-18. Obviously, expired content is less useful to users if it has already pasted its expiration date.
This article is categorized as "Garbage" . It should NEVER be appeared in your search engine's results.



等差数列求和公式

🔗 [等差数列 - 维基百科,自由的百科全书] https://zh.wikipedia.org/zh/%E7%AD%89%E5%B7%AE%E6%95%B0%E5%88%97

有等差数列形式如下:

[mathjax-d]{\displaystyle \{a\,,\,\,a+d\,,\,\,a+2d\,,\,\cdots \,,\,\,a+(n-1)d\}}[/mathjax-d]

则求和公式为:

【一个等差数列的和,等于其首项与末项的和,乘以项数除以2。】,也就是:

[mathjax-d]S_n=a n+d \cdot \frac{n(n-1)}{2}[/mathjax-d]

等比数列求和公式

🔗 [等比数列 - 维基百科,自由的百科全书] https://zh.wikipedia.org/zh/%E7%AD%89%E6%AF%94%E6%95%B0%E5%88%97

有等比数列形式如下:

[mathjax-d]\left\{a, a r, a r^{2}, \cdots, a r^{n-1}\right\}[/mathjax-d]

则求和公式为:

[mathjax-d]S_{n}=\frac{a\left(1-r^{n}\right)}{1-r}[/mathjax-d]

python ++i和i++

🔗 [Python 为什么不支持 i++ 自增语法,不提供 ++ 操作符? - 知乎] https://zhuanlan.zhihu.com/p/149878104

https://zhuanlan.zhihu.com/p/149878104
https://zhuanlan.zhihu.com/p/149878104

二分查找/二分搜索

https://segmentfault.com/a/1190000008699980

算法:时间复杂度

● 渐进时间复杂度:

https://www.cnblogs.com/hellcats/p/4790586.html

● 表示时间复杂度的不同符号:

具体内容见中文算法导论p26开始

中文算法导论p27

● 关于[mathjax]\Theta[/mathjax]的例题:

中文算法导论p27

● 特别要注意这个例题带来的重要条件:

[mathjax]\Theta(g(n))[/mathjax]的定义需要每个成员[mathjax]f(n)\in \Theta(g(n))[/mathjax]都是渐进非负。

● 有关[mathjax]O[/mathjax]:

注意[mathjax]f(n)=\Theta (g(n))[/mathjax]隐含着[mathjax]f(n)=O(g(n))[/mathjax],因为[mathjax]\Theta[/mathjax]记号强于[mathjax]O[/mathjax]记号。

中文算法导论p29

● 有关[mathjax]O[/mathjax]记号和[mathjax]o[/mathjax]记号:

[mathjax]o[/mathjax]表示【非渐进紧确的上界】,详细内容见中文算法导论p30


取整和取模(mod)

详细内容见中文算法导论p32

● 下取整:[mathjax]\lfloor a \rfloor[/mathjax];上取整:[mathjax]\lceil a \rceil[/mathjax] .

● mod:

[mathjax]a\ mod\ n[/mathjax] 表示:[mathjax]a/n[/mathjax]的余数。

如果[mathjax](a\ mod \ n)=(b\ mod \ n)[/mathjax],那么这个表达式也可以写作:[mathjax]a\equiv b(mod\ n)[/mathjax].


算法:有关时间复杂度的计算(例题)

有关递归:(链接里还有其他复杂度计算例题)🔗 [算法的时间复杂度分析题笔记] https://www.shellcodes.org/Programming/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E7%AE%97%E6%B3%95%E7%9A%84%E6%97%B6%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6%E5%88%86%E6%9E%90%E9%A2%98%E7%AC%94%E8%AE%B0.html

对于某些形式的递归表达式,可以用更快速的方法解答:main theorem/master theorem/主定理,见下面


算法:使用 main theorem/master theorem/主定理 计算时间复杂度

(有定理推导和例题)🔗 [主定理 Master Theorem - 知乎] https://zhuanlan.zhihu.com/p/100531135

主定理公式:

https://zhuanlan.zhihu.com/p/100531135

例题:

https://blog.csdn.net/lanchunhui/article/details/52451362

应用:

https://zh.wikipedia.org/wiki/%E4%B8%BB%E5%AE%9A%E7%90%86

❗另一个应用(需要仔细思考为什么merge sort对应这个推导式):

(可能需要再次回顾merge sort: 🔗 [2021-10-17 - Truxton's blog] https://truxton2blog.com/2021-10-17/ , 或:🔗[python归并排序--递归实现 - 简书] https://www.jianshu.com/p/3ad5373465fd )

https://zh.wikipedia.org/wiki/%E4%B8%BB%E5%AE%9A%E7%90%86

python 优先队列/priority queue

这个其实就是已经接触过的最小堆min heap/最大堆max heap:

https://en.wikipedia.org/wiki/Binary_heap

在python中的实现(python heapq用的是最小堆,见:https://docs.python.org/3/library/heapq.html(网页内搜索“max heap”)):

from heapq import *

heap = []
insert = [10, 9, 8, 7, 6, 5, 4]
for item in insert:
    heappush(heap, item)
while len(heap) != 0:
    print(heap)
    print(heappop(heap))

结果:

[4, 7, 5, 10, 8, 9, 6]
4
[5, 7, 6, 10, 8, 9]
5
[6, 7, 9, 10, 8]
6
[7, 8, 9, 10]
7
[8, 10, 9]
8
[9, 10]
9
[10]
10

python 快速排序/quicksort

📚 具体内容见中文算法导论p85开始

(❗注意:这个例子里的细节算法和算法导论并不一样)入门例子:🔗 [深入理解快速排序(quciksort) - 知乎] https://zhuanlan.zhihu.com/p/63202860

但是上面的链接配图的步骤有点少,需要自己用一个更长的数组进行演算,加深印象。

参考算法导论写的代码:

def quicksort(A, l, r):
    if l < r:
        middle = partition(A, l, r)
        quicksort(A, l, middle - 1)
        quicksort(A, middle + 1, r)
        return A


def partition(A, l, r):
    base = A[r]
    i = l - 1
    j = l
    while j < r:
        if A[j] < base:
            A[i + 1], A[j] = A[j], A[i + 1]
            i += 1
            j += 1
        else:
            j += 1
    A[i + 1], A[j] = A[j], A[i + 1]
    return i + 1


if __name__ == '__main__':
    A = [2, 8, 7, 1, 3, 5, 6, 4]
    print(quicksort(A, 0, len(A) - 1))
结果:
[1, 2, 3, 4, 5, 6, 7, 8]

● Randomized quicksort

简单的说就是在quicksort算法里,把原本的【固定选择A[r]作为pivot】修改为【随机挑选A[i]作为pivot, i 为随机数】。这样做能够让worst case time complexity从原来的[mathjax]O(n^2)[/mathjax]变为[mathjax]O(n\log(n))[/mathjax].

https://blog.csdn.net/haelang/article/details/44496387

即:

Worst caseBest caseAverage
quicksort[mathjax]O(n^2)[/mathjax][mathjax]O(n\log(n))[/mathjax][mathjax]O(n\log(n))[/mathjax]
randomized quicksort[mathjax]O(n\log(n))[/mathjax][mathjax]O(n\log(n))[/mathjax][mathjax]O(n\log(n))[/mathjax]

参考资料:

🔗 [Quicksort - Wikipedia] https://en.wikipedia.org/wiki/Quicksort#Analysis_of_randomized_quicksort

解释了为什么是[mathjax]O(n\log(n))[/mathjax]: 🔗 [algorithm analysis - Why does Randomized Quicksort have O(n log n) worst-case runtime cost - Computer Science Stack Exchange] https://cs.stackexchange.com/questions/35994/why-does-randomized-quicksort-have-on-log-n-worst-case-runtime-cost

用更容易理解的表达方式讲述了为什么是[mathjax]O(n\log(n))[/mathjax],同时附带了quicksort+randomized quicksort+代码:🔗 [算法导论(一):快速排序与随机化快排_Hamster-CSDN博客_随机快速排序] https://blog.csdn.net/haelang/article/details/44496387

验证代码:

import random
import time
import matplotlib.pyplot as plt
import sys

sys.setrecursionlimit(10000000)


def quicksort(A, l, r):
    if l < r:
        middle = partition(A, l, r)
        quicksort(A, l, middle - 1)
        quicksort(A, middle + 1, r)


def partition(A, l, r):
    base = A[r]
    i = l - 1
    j = l
    while j < r:
        if A[j] < base:
            A[i + 1], A[j] = A[j], A[i + 1]
            i += 1
            j += 1
        else:
            j += 1
    A[i + 1], A[j] = A[j], A[i + 1]
    return i + 1


def r_quicksort(A, l, r):
    if l < r:
        middle = r_partition(A, l, r)
        r_quicksort(A, l, middle - 1)
        r_quicksort(A, middle + 1, r)


def r_partition(A, l, r):
    base = A[random.randint(l, r)]
    i = l - 1
    j = l
    while j < r:
        if A[j] < base:
            A[i + 1], A[j] = A[j], A[i + 1]
            i += 1
            j += 1
        else:
            j += 1
    A[i + 1], A[j] = A[j], A[i + 1]
    return i + 1


def run(r_command):
    quicksort_t = []
    r_quicksort_t = []
    for i in range(10, 1000, 1):
        A = []
        for j in range(0, i):
            if r_command == 1:
                A.append(random.randint(0, i))
            else:
                A.append(j)
        start = time.time()
        quicksort(A, 0, len(A) - 1)
        quicksort_t.append(time.time() - start)

        A = []
        for j in range(0, i):
            A.append(j)
        start = time.time()
        r_quicksort(A, 0, len(A) - 1)
        r_quicksort_t.append(time.time() - start)
    plt.figure()
    axis = [i for i in range(10, 1000, 1)]
    plt.scatter(axis, quicksort_t, label='quicksort')
    plt.scatter(axis, r_quicksort_t, label='randomized quicksort')
    plt.legend(loc='upper left')
    if r_command == 1:
        plt.title('Average')
    else:
        plt.title('Worst Case')
    plt.show()


if __name__ == '__main__':
    run(0)
    run(1)

结果:

因为randomized还要花时间生成随机数

python 对象与递归

● 例子 (可以参考【python ++i和i++】):

def fun1(A):
    A[-1] = 999
    fun2(A)
    print(A)


def fun2(A):
    A[-2] = 888
    A.append(1000)
    A = A[:len(A) // 2]


if __name__ == '__main__':
    A = [1, 2, 3, 4]
    fun1(A)

结果:

[1, 2, 888, 999, 1000]

对上面的程序进行一点修改:

def fun1(A):
    A[-1] = 999
    A = fun2(A)
    print(A)


def fun2(A):
    A[-2] = 888
    A.append(1000)
    A = A[:len(A) // 2]
    return A


if __name__ == '__main__':
    A = [1, 2, 3, 4]
    fun1(A)
    print(A)

运行结果:

[1, 2]
[1, 2, 888, 999, 1000]

所以,本文之前的quicksort代码也可以这样写(区别在第6, 25, 26 行):

def quicksort(A, l, r):
    if l < r:
        middle = partition(A, l, r)
        quicksort(A, l, middle - 1)
        quicksort(A, middle + 1, r)


def partition(A, l, r):
    base = A[r]
    i = l - 1
    j = l
    while j < r:
        if A[j] < base:
            A[i + 1], A[j] = A[j], A[i + 1]
            i += 1
            j += 1
        else:
            j += 1
    A[i + 1], A[j] = A[j], A[i + 1]
    return i + 1


if __name__ == '__main__':
    A = [2, 8, 7, 1, 3, 5, 6, 4]
    quicksort(A, 0, len(A) - 1)
    print(A)

python 堆排序/heap sort(继续)

继续之前的内容:🔗 [2021-10-15 - Truxton's blog] https://truxton2blog.com/2021-10-15/

(从之前的内容里直接拿)heap sort using max heap代码如下:

❗注意!这段代码没有做到就地排序,使用了额外空间,这是对max heap性质不熟练导致的。事实上,整个程序都有一些问题:max_heapify()的功能并不完整。

import random


def max_heapify(arr, i):
    left = 2 * i + 1
    right = 2 * i + 2
    if left < len(arr) and arr[left] > arr[i]:
        largest = left
    else:
        largest = i
    if right < len(arr) and arr[right] > arr[largest]:
        largest = right
    if largest != i:
        temp = arr[largest]
        arr[largest] = arr[i]
        arr[i] = temp
        max_heapify(arr, largest)
    else:
        return arr


def build_max_heap(arr):
    for i in range(len(arr) // 2 - 1, -1, -1):
        max_heapify(arr, i)
    return arr


def heap_sort(arr):
    arr = build_max_heap(arr)
    sorted = []
    for i in range(len(arr) - 1, -1, -1):
        temp = arr[i]
        arr[i] = arr[0]
        arr[0] = temp
        sorted = [arr.pop()] + sorted
        max_heapify(arr, 0)
    return sorted


if __name__ == '__main__':
    A = []
    for i in range(0, 15):
        A.append(random.randint(0, 50))
    print(A)
    C = heap_sort(A)
    print(C)

● 有关写法:

注意上面代码的第35行,我平时非常习惯于使用 A.append(a) ,而现在需要使用 sorted = [arr.pop()] + sorted ,这段代码无法让我很好的回忆起“从一个最大堆导出排序数组”的细节。(因为上面的代码写的有问题)

在查找相关资料的时候发现了一些补充知识:🔗 [Python concatenation vs append speed on lists - Stack Overflow] https://stackoverflow.com/questions/25216962/python-concatenation-vs-append-speed-on-lists , 这段内容可以和【🔗 [2021-10-18 - Truxton's blog] https://truxton2blog.com/2021-10-18/ -> python 对象与递归】, 【🔗 [2021-10-15 - Truxton's blog] https://truxton2blog.com/2021-10-15/ -> python 初始化数组】相结合理解。

● 修改上面的heap sort using max heap(输入数据和中文算法导论p78一样,便于调试):

def max_heapify(arr, m, i):
    # arr: input array;
    # n: max index of arr (! important);
    # i: build max heap starting from node i(index)
    # does not need to return anything
    largest = i
    left = i * 2 + 1
    right = i * 2 + 2
    # find largest in : root, left, right
    # important: check if left <= m (left node exists in THE CURRENT SCOPE max index m)
    if left <= m and arr[i] < arr[left]:
        largest = left
    # important: check if right <= m (left node exists in THE CURRENT SCOPE max index m)
    if right <= m and arr[largest] < arr[right]:
        largest = right
    if largest != i:
        arr[largest], arr[i] = arr[i], arr[largest]
        max_heapify(arr, m, largest)


def heap_sort(A):
    # init a max heap
    for i in range(len(A) // 2 - 1, -1, -1):
        max_heapify(A, len(A) - 1, i)
    for j in range(len(A) - 1, -1, -1):
        # swap root and last node
        A[0], A[j] = A[j], A[0]
        max_heapify(A, j - 1, 0)


if __name__ == '__main__':
    A = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
    heap_sort(A)
    print(A)

结果:

[1, 2, 3, 4, 7, 8, 9, 10, 14, 16]

最小堆/min heap

● 类比上面的max heap - heap sort,现在使用最小堆来对一个数组从大到小排序:

def min_heap(A, m, i):
    minimum = i
    left = i * 2 + 1
    right = i * 2 + 2
    if left <= m and A[minimum] > A[left]:
        minimum = left
    if right <= m and A[minimum] > A[right]:
        minimum = right
    if minimum != i:
        A[minimum], A[i] = A[i], A[minimum]
        min_heap(A, m, minimum)


def heap_sort(A):
    for i in range(len(A) // 2 - 1, -1, -1):
        min_heap(A, len(A) - 1, i)
    for j in range(len(A) - 1, -1, -1):
        A[0], A[j] = A[j], A[0]
        min_heap(A, j - 1, 0)


if __name__ == '__main__':
    A = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
    heap_sort(A)
    print(A)

结果:

[16, 14, 10, 9, 8, 7, 4, 3, 2, 1]

● 修改为priority queue

def min_heap(A, m, i):
    minimum = i
    left = i * 2 + 1
    right = i * 2 + 2
    if left <= m and A[minimum] > A[left]:
        minimum = left
    if right <= m and A[minimum] > A[right]:
        minimum = right
    if minimum != i:
        A[minimum], A[i] = A[i], A[minimum]
        min_heap(A, m, minimum)


def build_heap(A):
    for i in range(len(A) // 2 - 1, -1, -1):
        min_heap(A, len(A) - 1, i)


def pqueue_pop(A):
    A[0], A[-1] = A[-1], A[0]
    pop = A[-1]
    # 这里可能还有改进空间,也许可以不创建新对象
    B = A[:-1]
    min_heap(B, len(B) - 1, 0)
    return B, pop


if __name__ == '__main__':
    A = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
    build_heap(A)
    while len(A) > 0:
        A, pop = pqueue_pop(A)
        print(pop, end='  ')

结果:

1  2  3  4  7  8  9  10  14  16 


 Last Modified in 2021-12-13 

Leave a Comment Anonymous comment is allowed / 允许匿名评论