常见算法汇总

常见算法

排序算法

排序方法时间复杂度(平均)时间复杂度(最坏)时间复杂度(最好)空间复杂度稳定性
冒泡排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)稳定
选择排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)不稳定
插入排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)稳定
希尔排序 O ( n 1.3 ) O(n^{1.3}) O(n1.3) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)不稳定
归并排序 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( n 2 ) O(n^2) O(n2) O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( n ) O(n) O(n)稳定
快速排序 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( n 2 ) O(n^2) O(n2) O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( l o g 2 n ) O(log_2n) O(log2n)不稳定
堆排序 O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( n l o g 2 n ) O(nlog_2n) O(nlog2n) O ( 1 ) O(1) O(1)不稳定
计数排序 O ( n + k ) O(n+k) O(n+k) O ( n + k ) O(n+k) O(n+k) O ( n + k ) O(n+k) O(n+k) O ( n + k ) O(n+k) O(n+k)稳定
桶排序 O ( n + k ) O(n+k) O(n+k) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( n + k ) O(n+k) O(n+k)稳定
基数排序 O ( n ∗ k ) O(n*k) O(nk) O ( n ∗ k ) O(n*k) O(nk) O ( n ∗ k ) O(n*k) O(nk) O ( n + k ) O(n+k) O(n+k)

冒泡排序

  • 算法描述

    • 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
    • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
    • 针对所有的元素重复以上的步骤,除了最后一个;
    • 重复步骤1~3,直到排序完成。
  • 代码实现

    for i in range(len(arr)-1):
        for j in range(len(arr)-1-i):
            if arr[j] > arr[j+1]:
                arr[j],arr[j+1] = arr[j+1], arr[j]
    

选择排序

  • 算法描述

    先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

插入排序

  • 算法描述

    • 从第一个元素开始,该元素可以认为已经被排序;
    • 取出下一个元素,在已经排序的元素序列中从后向前扫描;
    • 如果该元素(已排序)大于新元素,将该元素移到下一位置;
    • 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
    • 将新元素插入到该位置后;
    • 重复步骤2~5。
  • 代码实现

    for i in range(1,len(arr)):
        key = arr[i]
        j = i -1
        while j >=0 and key < arr[j]:
            arr[j+1]= arr[j]
            j -= 1
        a[j+1] = key
    

希尔排序

  • 算法描述

    先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。

  • 代码实现

    n = len(arr)
    gap = n // 2
    while gap > 0:
        for i in range(gap,n):
            temp = arr[i]
            j = i
            while j >= gap and arr[j-gap] > temp:
                arr[j] = arr[j-gap]
                j -= gap
            arr[j] = temp
        gap = gap // 2
    

归并排序

  • 算法描述

    • 把长度为n的输入序列分成两个长度为n/2的子序列;
    • 对这两个子序列分别采用归并排序;
    • 将两个排序好的子序列合并成一个最终的排序序列
  • 代码实现

    def merge(arr,l,m,r):
        n1 = m- l + 1
      	n2 = r - m
        
        # 拷贝数据到临时数组
        L = arr[l:l+n1]
        R = arr[m+1:m+1+n2]
        
        while i < n1 and j < n2 : 
        	if L[i] <= R[j]: 
            	arr[k] = L[i] 
            	i += 1
        	else: 
            	arr[k] = R[j] 
            	j += 1
        	k += 1
            
        # 拷贝 L[] 的保留元素
        while i < n1: 
            arr[k] = L[i] 
            i += 1
            k += 1
        # 拷贝 R[] 的保留元素
        while j < n2: 
            arr[k] = R[j] 
            j += 1
            k += 1
    
    def merge_sort(arr,l,r):
        if l < r:
            m = (l+(r-1))//2
            merge_sort(arr,l,m)
            merge_sort(arr,m+1,r)
            merge(arr,l,m,r)
    

快速排序

  • 算法描述

    • 从数列中挑出一个元素,称为 “基准”(pivot);
    • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
    • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
  • 代码实现

    def partition(arr, low, high):
        i = low - 1 # 最小元素索引
        pivot = arr[high]
        for j in range(low, high):
            # 当前元素小于等于pivot,放到pivot左边
            if arr[j] <= pivot:
                i += 1
                arr[i],arr[j] = arr[j],arr[i]
        # 将pivot放到中间
        arr[i+1],arr[high] = arr[high],arr[i+1]
        return i + 1
    
    def quickSort(arr,low,high):
        if low < high:
            pi = partition(arr,low,high)
        	quickSort(arr, low, pi-1) 
        	quickSort(arr, pi+1, high)  
                
    
    def quick_sort(array, left, right):
        if left >= right:
            return
        low = left
        high = right
        key = array[low]
        while left < right:
            while left < right and array[right] > key:
                right -= 1
            array[left] = array[right]
            while left < right and array[left] <= key:
                left += 1
            array[right] = array[left]
        array[right] = key
        quick_sort(array, low, left - 1)
        quick_sort(array, left + 1, high)
    

堆排序

  • 算法描述

    大顶堆:每个结点的值都大于或等于其左右孩子结点的值

    小顶堆:每个结点的值都小于或等于其左右孩子结点的值

    若对堆的二叉树依次编号,使用数组表示:

    大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]

    小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

    堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。

    一般升序采用大顶堆,降序采用小顶堆

  • 代码实现

    def heapify(arr, n , i):
        largest = i
        l = 2 * i + 1
        r = 2 * i + 2
        if l < n and arr[largest] < arr[l]:
            largest = l
        if r < n and arr[largest] < arr[r]:
            largets = r
        if largest != i:
        	arr[i],arr[largest] = arr[largest],arr[i]
    		heapify(arr, n, largest) 
    def heapSort(arr):
        n = len(arr)
       	# 构建一个大顶堆
        # 第一个非叶结点为 n/2-1
        for i in range(n//2, -1, -1):
            heapify(arr,n, i)
       	
        # 一个个交换元素
        for i in range(n-1, 0, -1):
        	arr[i], arr[0] = arr[0],arr[i]
            heapify(arr,i ,0)
        
    

    使用python堆的插入、删除、排序,上升,下沉

    class Heap:
        def __init__(self,  is_max=True):
            self.heap = []
            self.is_max = is_max
    
        def heap_up(self, idx):
            """
            实现节点的上浮操作
            :param idx:
            :return:
            """
            while idx > 0:
                parent = (idx - 1) // 2
    
                #  大顶堆
                if self.is_max and self.heap[parent] < self.heap[idx]:
                    self.heap[parent], self.heap[idx] = self.heap[idx], self.heap[parent]
                # 小顶堆
                elif not self.is_max and self.heap[parent] > self.heap[idx]:
                    self.heap[parent], self.heap[idx] = self.heap[idx], self.heap[parent]
                else:
                    break
                idx = parent
    
        def insert(self, num):
            """
            将元素插入堆,从堆最后一个叶节点开始上浮
            :param num:
            :return:
            """
            self.heap.append(num)
            self.heap_up(len(self.heap) - 1)
    
        def get_child(self, idx):
            """
            根据堆的类型,返回节点idx最小或者最大的子节点的idx,若idx没有子节点,则返回2*idx+1
            :param idx:
            :return:
            """
            left = 2 * idx + 1
            right = 2 * idx + 2
            res = left
            n = len(self.heap)
            if right < n:
                # 大顶堆,返回较大的子节点
                if self.is_max and self.heap[left] < self.heap[right]:
                    res = right
                elif not self.is_max and self.heap[right] < self.heap[left]:
                    res = right
            return res
    
        def heap_down(self, idx):
            """
            实现节点的下沉操作
            :param idx:
            :return:
            """
            n = len(self.heap)
            # 当该节点有子节点才下沉
            while 2 * idx + 1 < n:
                child_idx = self.get_child(idx)
                # 大顶堆 当前节点小于子节点,则与子节点替换
                if self.is_max and self.heap[idx] < self.heap[child_idx]:
                    self.heap[idx], self.heap[child_idx] = self.heap[child_idx], self.heap[idx]
                elif not self.is_max and self.heap[idx] > self.heap[child_idx]:
                    self.heap[idx], self.heap[child_idx] = self.heap[child_idx], self.heap[idx]
                else:
                    break
                idx = child_idx
    
        def del_top(self):
            """
            删除最顶端元素
            :return:
            """
            res = None
            if len(self.heap) > 1:
                res = self.heap[0]
                self.heap[0] = self.heap.pop()
                self.heap_down(0)
            else:
                if self.heap:
                    res = self.heap.pop()
            return res
    
        def build_heap(self):
            """
            将heap构建成一个堆
            :return:
            """
            n = len(self.heap)
            for i in range(n // 2, -1, -1):
                self.heap_down(i)
    

计数排序

  • 算法描述

    • 找出待排序的数组中最大和最小的元素;
    • 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
    • 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
    • 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
  • 代码实现

    def counting_sort(a, k):  # k = max(a)
        n = len(a)  # 计算a序列的长度
        b = [0 for i in range(n)]  # 设置输出序列并初始化为0
        c = [0 for i in range(k + 1)]  # 设置计数序列并初始化为0,
        for j in a:
            c[j] = c[j] + 1
        # 记录有多少数小于等于c[i]
        for i in range(1, len(c)):
            c[i] = c[i] + c[i-1]
        # 反向填充数组,保证算法稳定性
        for j in a[::-1]:
            b[c[j] - 1] = j
            c[j] = c[j] - 1
        return b
    

桶排序

  • 算法描述

    • 设置一个定量的数组当作空桶;
    • 遍历输入数据,并且把数据一个一个放到对应的桶里去;
    • 对每个不是空的桶进行排序;
    • 从不是空的桶里把排好序的数据拼接起来。
  • 代码实现

    def bucket_sort(arr,bucket_size):
        min_value = min(arr)
        max_value = max(arr)
        #桶数量
        bucketcount = (maxValue - minValue +1) // bucket_size
        #对应的桶
        bucket_lists = list([] for _ in range(bucketcount))
        # 数据放入对应的桶
        for i in arr:
            bucket_index = (i - minValue) // bucket_size
            bucket_lists[bucket_index].append(i)
        # 桶内排序
        for i in bucket_lists:
            sorted(i)
        # 合并数据
        result = []
        for j in bucket_lists:
            if len(j) != 0:
                result.extend(j)
        return result
        
        
    

基数排序

  • 算法描述

    • 取得数组中的最大数,并取得位数;
    • arr为原始数组,从最低位开始取每个位组成radix数组;
    • 对radix进行计数排序(利用计数排序适用于小范围数的特点)
  • 代码实现

    def radix_sort(s):
        """基数排序"""
        i = 0 # 记录当前正在排哪一位,最低位为1
        max_num = max(s)  # 最大值
        j = len(str(max_num))  # 记录最大值的位数
        while i < j:
            bucket_list =[[] for _ in range(10)] #初始化桶数组
            for x in s:
                bucket_list[int(x / (10**i)) % 10].append(x) # 找到位置放入桶数组
            s.clear()
            for x in bucket_list:   # 放回原序列
                for y in x:
                    s.append(y)
            i += 1
    

图算法

有向图中的环及元素

import sys
def main():
    def dfs(node, graph, visited, stack):
        # 当前点设为已经搜索
        visited[node] = True
        # 记录路径
        stack.append(node)
        if node in graph:
            # 遍历点的所有邻居
            for n in graph[node]:
                # 如果不在已经走过的路径里则继续搜索该点的路径
                if n not in stack:
                    # if not visited[n]:
                    dfs(n, graph, visited, stack)
                # 如果该点已经在走过的路径里
                else:
                    # 得到该点在路径中第一次出现的位置
                    index = stack.index(n)
                    # 将此段路径记入结果
                    res.append(stack[index:])
        # 每次尝试完后都要将该点删除
        stack.pop(-1)
    
    # 记录图的dict
    graph = {}
    # 记录是否遍历过点
    visited = {}
    # 存储当前路径上的点
    stack = []
    # 存储结果
    res = []
    # 根据输入生成图以及visited
    num = int(sys.stdin.readline())
    for i in range(num):
        n1, n2 = sys.stdin.readline().strip().split(' ')
        if n1 not in graph:
            graph[n1] = [n2]
        elif n2 not in graph[n1]:
            graph[n1].append(n2)
        if n1 not in visited:
            visited[n1] = False
        if n2 not in visited:
            visited[n2] = False
	# 遍历所有的点
    for node in visited.keys():
        # 如果当前点没有被搜到过,就搜索
        if not visited[node]:
            dfs(node, graph, visited, stack)
    print(res)

迪杰斯特拉算法

# dijkstra算法实现,有向图和路由的源点作为函数的输入,最短路径最为输出
def dijkstra(graph,src):
    # 判断图是否为空,如果为空直接退出
    if graph is None:
        return None
    nodes = [i for i in range(len(graph))]  # 获取图中所有节点
    visited=[]  # 表示已经路由到最短路径的节点集合
    if src in nodes:
        visited.append(src)
        nodes.remove(src)
    else:
        return None
    distance={src:0}  # 记录源节点到各个节点的距离
    for i in nodes:
        distance[i]=graph[src][i]  # 初始化
    # print(distance)
    path={src:{src:[]}}  # 记录源节点到每个节点的路径
    k=pre=src
    while nodes:
        mid_distance=float('inf')
        for v in visited:
            for d in nodes:
                new_distance = graph[src][v]+graph[v][d]
                if new_distance < mid_distance:
                    mid_distance=new_distance
                    graph[src][d]=new_distance  # 进行距离更新
                    k=d
                    pre=v
        distance[k]=mid_distance  # 最短路径
        path[src][k]=[i for i in path[src][pre]]
        path[src][k].append(k)
        # 更新两个节点集合
        visited.append(k)
        nodes.remove(k)
        print(visited,nodes)  # 输出节点的添加过程
    return distance,path
if __name__ == '__main__':
    graph_list = [ [0, 2, 1, 4, 5, 1],
            [1, 0, 4, 2, 3, 4],
            [2, 1, 0, 1, 2, 4],
            [3, 5, 2, 0, 3, 3],
            [2, 4, 3, 4, 0, 1],
            [3, 4, 7, 3, 1, 0]]

    distance,path= dijkstra(graph_list, 0)  # 查找从源点0开始带其他节点的最短路径
    print(distance,path)

字典形式

# dists定义了图,记录着从从起点出发到其他顶点的距离
dist={1:{2:1,3:12},
      2:{3:9,4:3},
      3:{5:5},
      4:{3:4,5:13,6:15},
      5:{6:4},
      6:{6:0}}
cost={1:0,2:1,3:12,4:999,5:999,6:999}  # 由起点(结点1)到其余顶点的最短距离,999代表无法到达
parents={1:None,2:1,3:2,4:2,5:3,6:5}   # parent代表到达这个结点的最短路径的前一个结点
visited=[1]   # 起始结点默认已经访问过


# 找到还没有访问的结点中路径最短的一个结点
def findShorestNode(cost):
    minDist=999
    node=None
    for i in dist.keys():
        if (cost[i]<minDist)&(i not in visited):
            minDist=cost[i]
            node=i
    return node


# 更新最短路径
node=findShorestNode(cost)
while node:
    for i in dist[node]:  # 所有node结点的邻居结点
        # 新的cost
        newcost=cost[node]+dist[node][i]
        # 判断是否替换旧cost
        if newcost<cost[i]:
            parents[i]=node
            cost[i]=newcost
    # 此节点遍历完毕
    visited.append(node)
    # 寻找下一个没有visited且距离最近的节点
    node=findShorestNode(cost)
    
# 打印出从1到6的最短路径
parent=parents[6]
while parent:
    print(parent)
    parent=parents[parent]

弗洛伊德算法

import copy
M=1000000
def Floyd(G):
    n=len(G)
    path=copy.deepcopy(G)
    for k in range(0,n):
        for i in range(0,n):
            for j in range(0,n):
                if path[i][j] > path[i][k] + path[k][j]:
                    path[i][j] = path[i][k] + path[k][j]
                
    return path

if __name__=='__main__':
    G=[
        [0,30,15,M,M,M],
        [5,0,M,M,20,30],
        [M,10,0,M,M,15],
        [M,M,M,0,M,M],
        [M,M,M,10,0,M],
        [M,M,M,30,10,0]
    ]
    path=Floyd(G)

二叉树遍历

前序遍历

非递归

def pre_order(root):
    s = []
    while root:
        print(root.val)
        if root.right:
            s.append(root.right)
        root = root.left
        if root is None  and s:
            root = s.pop(-1)

中序遍历

非递归

def in_order(root):
    p = root
    todo = []
    while p or todo:
        while p:
            todo.append(p)
            p = p.left
        if todo:
            p = todo.pop(-1)
            print(p.val)
            p = p.right

后序遍历

非递归

def post_order(root):
    todo = []

    pre = None
    todo.append(root)
    while todo:
        cur = todo[-1]
        # 如果当前节点没有左右子树则输出
        # 如果当前节点是上一个输出节点的父节点则输出,因为其子节点已经输出了表明已经遍历过其子树了
        if (cur.left is None and cur.right is None) or (pre is not None and (pre == cur.left or pre == cur.right)):
            print(cur.val)
            todo.pop(-1)
            pre = cur
        else:
            # 因为先输出左子树,所以先将右子树入栈
            if cur.right:
                todo.append(cur.right)
            if cur.left:
                todo.append(cur.left)

字符串匹配

Sunday算法

给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        
        def generate_dict(needle):
            # 计算每个字符最后出现的位置离末尾的距离,方面后续决定字符串后移多少位
            dic = {}
            l = len(needle)
            for i,c in enumerate(needle):
                dic[c] = l - i 
            return dic

        if not needle:
            return 0
        if not haystack:
            return -1 

        dic = generate_dict(needle)
        i  = 0
        l = len(needle)
        while i + l <= len(haystack):
            # 进行匹配
            if needle == haystack[i:i+l]:
                return i
            else:
                # 每次字符串失配时,看当前范围的后一个字符,是否在目标中,在的话将其与目标串对应位置对齐
                if i+l >= len(haystack):
                    return -1
                c = haystack[i+l]
                i = i + dic.get(c,l)
        
        return -1 

并查集

class Node:
    def __init__(self):
        self.val = 1
        self.father = self

# 并查集的查找
def find(node):
    r = node
    # 循环找到其父节点
    while r.father != r:
        r = r.father

    # 压缩路径
    # 将路径上所有点的father都设置为r
    while node.father != node:
        next_node =node.father
        node.father =r
        node = next_node
    return r

# 并查集的合并
def union(node1, node2, val, child_dict):
    # 祖先相同则不需要合并
    root1 = find(node1)
    root2 = find(node2)
    if root1 == root2:
        return
    
    # 否则将父节点1的父节点从自身变为父节点2
    # 此时可能根据题意要对val进行一些变换
	# 使用dict保存每个节点的子孙,方便查找
    root1.father = root2
    child_dict[root2] += child_dict[root1]
    child_dict.pop(root1)

    
class Solution:
    def main():
        # 存放节点的dict
        str_node_dict = {}
        # 存放父节点子孙的dict
        child_dict ={}
        for (x,y),v in zip(equations,values):
            # 找到或创建其对应节点
            if x in str_node_dict:
                node1 = str_node_dict[x]
            else:
                node1 = Node()
                str_node_dict[x] = node1
                child_dict[node1] = [node1]
            if y in str_node_dict:
                node2 = str_node_dict[y]
            else:
                node2 = Node()
                str_node_dict[y] = node2
                child_dict[node2] = [node2]
            
            union(node1,node2,v,child_dict)

手写代码

梯度下降

def cost_func(true,pred):
    
    return cost

def grad(theta,x ,y):
    return grad

lr = 0.01
epochs = 100
net =
loss = 
for epoch in range(epochs):
    for x,y in dataloader:
        pred = net(x)
        loss = cost_func(y,pred)
        grad = grad(theta,x,y)
        theta = theta - lr*grad
        if 不满足阈值条件(比如两次loss<):
            break

K-means

# -*- coding:utf-8 -*-
import numpy as np
from collections import defaultdict


class KMeans:
    """
    K-means的类,实现KMeans的训练与预测
    """

    def __init__(self, k, max_iter, delta):
        """

        :param k: 类别数
        :param max_iter: 最大迭代次数
        :param delta: 中心点误差阈值
        """
        self.k = k
        self.max_iter = max_iter
        self.delta = delta
        self.centers = {}  # 存储中心点的字典

    def fit(self, data):
        """
        根据输入数据训练模型的函数
        :param data: numpy,batch*node
        :return:
        """
        # 初始化中心点
        assert len(data) >= self.k, "The amount of data is less than K."
        for i in range(self.k):
            self.centers[i] = data[i]
        # 进行迭代
        for iter in range(self.max_iter):
            # 存储类别

            # 计算点到各个中心点的距离
            # TODO 可用numpy矩阵操作优化
            distance = []
            for center in self.centers:
                distance.append(np.linalg.norm(data - center, axis=1))
            distance = np.array(distance)
            clf_idx = np.argmin(distance, axis=0)

            # 根据各类别的点确定新的中心点
            new_centers = {}
            for clf in range(self.k):
                new_center = np.mean(data[clf_idx == clf])
                new_centers[clf] = new_center

            # 计算新旧中心点的误差,决定是否需要提前中止训练
            stop = True
            for clf in self.centers:
                new_center = new_centers[clf]
                old_center = self.centers[clf]
                if np.linalg.norm(new_center - old_center) > self.delta:
                    stop = False
                    break
            if stop:
                break

    def predict(self, p_data):
        """
        预测输入的数据所属的类别
        :param p_data: numpy,batch*node
        :return: 对应数据所属的类别index
        """
        assert self.centers, "please fit first"
        distance = []
        for center in self.centers:
            distance.append(np.linalg.norm(p_data - center, axis=1))
        distance = np.array(distance)
        res = np.argmin(distance, axis=0)
        return res

逻辑回归-numpy

def sigmoid(x):
    '''
    定义sigmoid函数
    :param x: 参数x
    :return: 返回计算后的值
    '''
    return 1.0 / (1 + np.exp(-x))

使用对数似然函数作为损失函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bt7429Z0-1617259286220)(常见算法汇总.assets/image-20200909101521366.png)]

def errorRate(pre, label):
    '''
    损失函数
    :param pre: 预测值
    :param label: 实际值
    :return: 错误率
    '''
    m = np.shape(pre)[0]
    errSum = 0.0
    for i in range(m):
        if pre[i, 0] > 0 and (1 - pre[i, 0]) > 0:
            errSum -= (label[i, 0] * np.log(pre[i, 0]) + (1 - label[i, 0]) * np.log(1 - pre[i, 0]))
        else:
            errSum -= 0.0
    return errSum / m

实现梯度下降(极大对数似然,其实为梯度上升)

def LRGradientDescent(feature, label, maxIteration, alpha):
    '''
    使用梯度下降法训练逻辑回归模型
    :param feature: 特征
    :param label: 标签
    :param maxIteration: 最大迭代次数
    :param alpha: 学习率α
    :return: 返回权重矩阵
    '''
    n = np.shape(feature)[1]  # 特征的个数
    w = np.mat(np.ones((n, 1)))  # 初始化权重矩阵
    i = 0 # 定义指标, 用于与最大迭代次数进行比较
    while i <= maxIteration:  # 当指标小于最大迭代次数时
        i += 1
        # 下面三行为对w求导推导出的梯度下降公式
        sig = sigmoid(feature * w)  # 调用sigmoid函数计算sigmoid的值
        error = label - sig
        w = w + alpha * feature.T * error  # 权重修正
        if i % 100 == 0:
            print("迭代", str(i), "时的错误率为:", str(errorRate(sig, label)))
    return w

训练

def predict(feature, w):
    '''
    对测试数据进行预测
    :param feature: 测试数据的特征
    :param w: 权重
    :return: 预测结果
    '''
    sig = sigmoid(feature * w.T)
    n = np.shape(sig)[0]
    for i in range(n):
        if sig[i, 0] < 0.5:
            sig[i, 0] = 0.0
        else:
            sig[i, 0] = 1.0
    return sig

fearture = read_data()
# 这里要对feature进行处理,加一列1, 构造常数项x0,相当于加了一个特征
n = feature.shape[0] # 样本数量
x0 = np.ones(n)
# np.insert(feature,n,values=x0,axis=1)
# np.column_stack((feature, x0))
fearture = np.c_[feature, x0] # 填加列

w = LRGradientDescent(feature, label, maxIteration, alpha)
res = predict(feature, w)

逻辑回归-pytorch

import torch
from torch import nn
from torch.autograd import Variable
import matplotlib.pyplot as plt
import numpy as np

# 构造假数据
n_data = torch.ones(50, 2)  # 数据的基本形态
x0 = torch.normal(2 * n_data, 1)  # 类型0 x data (tensor), shape=(100, 2)
y0 = torch.zeros(50)  # 类型0 y data (tensor), shape=(100, 1)
x1 = torch.normal(-2 * n_data, 1)  # 类型1 x data (tensor), shape=(100, 1)
y1 = torch.ones(50)  # 类型1 y data (tensor), shape=(100, 1)

# 注意 x, y 数据的数据形式是一定要像下面一样 (torch.cat 是在合并数据)
x = torch.cat((x0, x1), 0).type(torch.FloatTensor)  # FloatTensor = 32-bit floating
y = torch.cat((y0, y1), 0).type(torch.FloatTensor)  # LongTensor = 64-bit integer


# 初始化模型参数
num_inputs = 2
num_outputs = 1

W = torch.tensor(np.random.normal(0, 0.01, (num_inputs, num_outputs)), dtype=torch.float)
b = torch.zeros(num_outputs, dtype=torch.float)

W.requires_grad_(requires_grad=True),b.requires_grad_(requires_grad=True) 

# 定义模型
def net(X):
    return (torch.mm(X.view((-1, num_inputs)), W) + b).sigmoid()

# 定义优化算法
def sgd(params, lr, batch_size):  
    for param in params:
        param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data
        
# 定义损失函数
criterion = nn.BCELoss()

# 训练
num_epochs, lr = 1000, 1e-3

def train(net, x, y, lossFunction, num_epochs, batch_size,
              params=None, lr=None):
    for epoch in range(num_epochs):

        x_data = Variable(x)
        y_data = Variable(y)

        out = net(x_data)
        loss = lossFunction(out, y_data).sum()
        print_loss = loss.data.item()
        mask = out.ge(0.5).float().view(-1,100)  # 以0.5为阈值进行分类
        correct = (mask == y_data).sum()  # 计算正确预测的样本个数
        acc = correct.item() / x_data.size(0)  # 计算精度
        if params is not None and params[0].grad is not None:
            for param in params:
                param.grad.data.zero_()
        loss.backward()
        sgd(params, lr, batch_size)
        # 每隔20轮打印一下当前的误差和精度
        if (epoch + 1) % 20 == 0:
            print('*' * 10)
            print('epoch {}'.format(epoch + 1))  # 训练轮数
            print('loss is {:.4f}'.format(print_loss))  # 误差
            print('acc is {:.4f}'.format(acc))  # 精度
            
            
train(net, x, y, criterion, num_epochs, 100, params=[W, b], lr=lr)

训练

num_epochs, lr = 1000, 1e-3

def train(net, x, y, lossFunction, num_epochs, batch_size,
params=None, lr=None):
for epoch in range(num_epochs):

    x_data = Variable(x)
    y_data = Variable(y)

    out = net(x_data)
    loss = lossFunction(out, y_data).sum()
    print_loss = loss.data.item()
    mask = out.ge(0.5).float().view(-1,100)  # 以0.5为阈值进行分类
    correct = (mask == y_data).sum()  # 计算正确预测的样本个数
    acc = correct.item() / x_data.size(0)  # 计算精度
    if params is not None and params[0].grad is not None:
        for param in params:
            param.grad.data.zero_()
    loss.backward()
    sgd(params, lr, batch_size)
    # 每隔20轮打印一下当前的误差和精度
    if (epoch + 1) % 20 == 0:
        print('*' * 10)
        print('epoch {}'.format(epoch + 1))  # 训练轮数
        print('loss is {:.4f}'.format(print_loss))  # 误差
        print('acc is {:.4f}'.format(acc))  # 精度

train(net, x, y, criterion, num_epochs, 100, params=[W, b], lr=lr)


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值