zoukankan      html  css  js  c++  java
  • 算法进阶


    贪心算法

    贪心算法是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。

    贪心算法并不保证会得到最优解,但是在某些问题上贪心算法的解就是最优解。要会判断一个问题能否用贪心算法来计算。

    1.找零问题

    假设商店老板需要找零n元钱,钱币的面额有:100元,50元,20元,5元,1元,如何找零是的所需钱币的数量最少?

    t = [100, 50, 20, 5, 1]
    
    def change(t, n):
        m = [0 for i in range(len(t))]
        for i, money in enumerate(t):
            m[i] = n // money
            n = n % money
        return m, n
    
    print(change(t, 376))
    View Code

    2、背包问题

    一个小偷在某个商店发现有n个商品,第i个商品价值vi元,重wi千克。他希望拿走的价值尽量高,但他的背包最多只能容纳W千克的东西。他应该该走那些商品?

    0-1背包:对于一个商品,小偷要么把它完整拿走,要么留下。不能只拿走一部分,或则把一个商品拿走多次。

    分数背包:对于一个商品,小偷可以拿走其中任意一部分。

    举例: 商品1:v1=60 w1=10

        商品2:v2=100 w2=20

                商品3:v3=120 w3=30

                背包容量量:W=50

    goods = [(60, 10), (100, 20), (120, 30)]  # 每个商品元组表示(价格,重量)
    goods.sort(key=lambda x: x[0] / x[1], reverse=True)
    
    
    def fractional_backpack(goods, w):
        m = [0 for i in range(len(goods))]
        total_v = 0
        for i, (price, weight) in enumerate(goods):
            if w >= weight:
                m[i] = 1
                total_v += price
                w -= weight
            else:
                m[i] = w / weight
                total_v += m[i] * price
                w = 0
                break
        return total_v, m
    
    print(fractional_backpack(goods, 50))
    分数背包代码实现

    3、拼接最大数字问题

    有n个非负整数,将其按照字符串拼接的方式拼接为一个整数。如何拼接可以使得得到的整数最大?

    例例:32,94,128,1286,6,71可以拼接除的最⼤大整数为
    94716321286128

    li = [32, 94, 128, 1286, 6, 71]
    
    
    def number_join(li):
        li = list(map(str, li))
    
        for i in range(len(li) - 1):
    
            for j in range(len(li) - i - 1):
                if li[j] + li[j + 1] < li[j + 1] + li[j]:
                    li[j], li[j + 1] = li[j + 1], li[j]
    
        return "".join(li)
    
    print(number_join(li))
    View Code

    4、活动选择问题

    假设有n个活动,这些活动要占⽤用同⼀一⽚片场地,⽽而场地在某时刻只能供⼀一个活动使⽤用。

    每个活动都有⼀一个开始时间si和结束时间fi(题⽬目中时间以整数表示),表示活动在[si, fi)区间占⽤用场地。

    贪心讨论:最先结束的活动一定是最优解的一部分。

    证明:假设a是所有活动中最先结束的活动,b是最优解重最先结束的活动

    如果a=b,结论成⽴立。
    如果a≠b,则b的结束时间⼀一定晚于a的结束时间,则此时⽤用a替换掉最优解中
    的b,a⼀一定不不与最优解中的其他活动时间重叠,因此替换后的解也是最优解。

    activities = [(1, 4), (3, 5), (0, 6), (5, 7), (3, 9), (5, 9), (6, 10), (8, 11), (8, 12), (2, 14), (12, 16)]
    # 保证活动时按照结束时间排好序的
    activities.sort(key=lambda x: x[1])
    
    def activity_selection(a):
        res = [a[0]]
        for i in range(1, len(a)):
            if a[i][0] >= res[-1][1]:   # 当前活动的开始时间小于等于最后一个入选活动的结束时间
                res.append(a[i])
    
        return res
    
    print(activity_selection(activities))
    View Code

    动态规划

    1、从斐波那契数列看动态规划

    斐波那契数列列:Fn = Fn−1 + Fn−2
    练习:使⽤用递归和⾮非递归的⽅方法来求解斐波那契数列列的第n项

    # 子问题的重复计算
    def fibnacci(n):
        if n > 0:
            if n == 1 or n == 2:
                return 1
            else:
                return fibnacci(n-1) + fibnacci(n-2)
    
    
    # 动态规划(DP)的思想 = 递归式 + 重复子问题
    def fibnacci_no_recurision(n):
        f = [0, 1, 1]
        if n > 2:
            for i in range(n-2):
                num = f[-1] + f[-2]
                f.append(num)
        return f[n]

    4、钢条切割问题

    某公司出售钢条,出售价格与钢条⻓长度之间的关系如下表:

    问题:现有⼀一段⻓长度为n的钢条和上⾯面的价格表,求切割钢条⽅方案,使得总收益最⼤大。

     

    钢条切割问题 --递推式

    设⻓长度为n的钢条切割后最优收益值为rn,可以得出递推式:rn = max(pn,r1+rn-1,r2+rn-2,…,rn-1+r1),

    第⼀个参数pn表示不不切割
    其他n-1个参数分别表示另外n-1种不不同切割⽅方案,对方案i=1,2,...,n-1
    将钢条切割为长度为i和n-i两段
    方案i的收益为切割两段的最优收益之和
    考察所有的i,选择其中收益最大的⽅案

    钢条切割问题 --最优子结构

    可以将求解规模为n的原问题,划分为规模更更小的子问题:完成⼀一次切割后,可以将产⽣生的两段钢条看成两个独⽴立的钢条切个问题。

    组合两个子问题的最优解,并在所有可能的两段切割方案中选取组合收益最大的,构成原问题的最优解。

    钢条切割满足最优子结构:问题的最优解由相关⼦问题的最优解组合而成,这些子问题可以独立求解。

    钢条切割问题 --自顶向下递归实现

    def cut_rod_recurision_2(p, n):
        if n == 0:
            return 0
        else:
            res = 0
            for i in range(1, n+1):
                res = max(res, p[i] + cut_rod_recurision_2(p, n-i))
            return res

    ## 实现复杂度O(2n) 2的n次方, 效率指数型增长,效率非常慢

    钢条切割问题 --动态规划解法

    递归算法由于重复求解相同子问题,效率极低

    动态规划的思想:

      1. 每个子问题之求解一次,保存求解结果

      2. 之后需要此问题时,只需要查找保存的结果

    def cut_rod_dp(p, n):
        r = [0]
        for i in range(1, n+1):
            res = 0
            for j in range(1, i+1):
                res = max(res, p[j] + r[i - j])
            r.append(res)
        return r[n]

    ## 时间复杂度为O(n的平方)

    钢条切割问题 --重构解

    如何修改动态规划算法,使其不不仅输出最优解,还输出最优切割⽅方案?

    对于每个子问题,保存切割一次时左边切下的长度

     

    def cut_rod_extend(p, n):
        r = [0]
        s = [0]
        for i in range(1, n+1):
            res_r = 0 # 价格的最大值
            res_s = 0 # 价格最大值对应方案的左边不切割部分的长度
            for j in range(1, i + 1):
                if p[j] + r[i - j] > res_r:
                    res_r = p[j] + r[i - j]
                    res_s = j
            r.append(res_r)
            s.append(res_s)
        return r[n], s

    动态规划问题关键特征

    • 最优子结构
      • 原问题的最优解中涉及多少个子问题
      • 在确定最优解使用哪些自问题时,需要考虑多少种选择
    • 重叠子问题

    5、最长公共子序列

    一个序列的子序列是在该序列中删去若干元素后得 到的序列。
    例:“ABCD”和“BDF”都是“ABCDEFG”的⼦序列
    最长公共⼦子序列(LCS)问题:给定两个序列X和Y,求X和Y⻓度最⼤的公共⼦序列。
    例:X="ABBCBDE" Y="DBBCDB" LCS(X,Y)="BBCD"
    应⽤场景:字符串相似度⽐对

    例如:要求a="ABCBDAB"与b="BDCABA"的LCS:
    由于最后⼀位"B"≠"A":
    因此LCS(a,b)应该来源于LCS(a[:-1],b)与LCS(a,b[:-1])中更大的那⼀个

    def lcs_length(x, y):
        m = len(x)
        n = len(y)
        c = [[0 for _ in range(n+1)] for _ in range(m+1)]
        for i in range(1, m+1):
            for j in range(1, n+1):
                if x[i-1] == y[j-1]:    # i j 位置上的字符匹配的时候,来自于左上方+1
                    c[i][j] = c[i-1][j-1] + 1
                else:
                    c[i][j] = max(c[i-1][j], c[i][j-1])
        return c[m][n]
    
    def lcs(x, y):
        m = len(x)
        n = len(y)
        c = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
        b = [[0 for _ in range(n + 1)] for _ in range(m + 1)] # 1 左上方 2 上方 3 左方
        for i in range(1, m+1):
            for j in range(1, n+1):
                if x[i-1] == y[j-1]:    # i j 位置上的字符匹配的时候,来自于左上方+1
                    c[i][j] = c[i-1][j-1] + 1
                    b[i][j] = 1
                elif c[i-1][j] > c[i][j-1]: # 来自于上方
                    c[i][j] = c[i-1][j]
                    b[i][j] = 2
                else:
                    c[i][j] = c[i][j-1]
                    b[i][j] = 3
        return c[m][n], b
    
    
    def lcs_trackback(x, y):
        c, b = lcs(x, y)
        i = len(x)
        j = len(y)
        res = []
        while i > 0 and j > 0:
            if b[i][j] == 1:    # 来自左上方=>匹配
                res.append(x[i-1])
                i -= 1
                j -= 1
            elif b[i][j] == 2:  # 来自于上方=>不匹配
                i -= 1
            else: # ==3 来自于左方=>不匹配
                j -= 1
        return "".join(reversed(res))
    
    
    print(lcs_trackback("ABCBDAB", "BDCABA"))

     

    欧几里得算法

    最大公约数

    约数:如果整数a能被整数b整除,那么a叫做b的倍数,b叫做a的约数。

    给定两个整数a、b,两个数的所有公共约数中的最大值即为最大公约数

    例:12与16的最大公约数是4

    如何计算两个数的最大公约数:

    欧几里得:辗转相除法(欧几里得算法)

    九章算术:更相减损术

    # 欧几里得算法:gcd(a,b) = gcd(b, a mod b)
    例:gcd(60,21) = gcd(21,18) = gcd(18,3) = gcd(3,0) = 3

    RSA加密算法介绍

    传统加密:加密算法是秘密的

    现代密码系统:加密算法是公开的,密钥是秘密的

    • 对称加密
    • 非对称加密

    RSA非对称加密系统:

    公钥:用来加密,是公开的

    私钥:用来解密,是私有的

    ## RSA加密算法过程
    1、随机选取两个质数p和q
    2、计算n=pq
    3、选取⼀个与φ(n)互质的小奇数e,φ(n)=(p-1)(q-1)
    4、对模φ(n),计算e的乘法逆元d,即满足 (e*d) mod φ(n) = 1
    5、公钥(e, n) 私钥(d, n)

    加密过程:c = (m^e) mod n

    解密过程:m = (c^d) mod n

  • 相关阅读:
    jquery的下拉选择框
    jquery动态导航三
    jquery--动态导航二
    jquery--动态导航
    jquery--图片轮番效果
    jquery方式的价格随数量增加、删除当前行与所有行
    解决sese9 安装时多个屏幕
    利用PowerCLI不重启系统更新VMware Tools
    VMware: Deploy multiple VM’s from template with PowerCLI
    vmware converter linux p2v lvm
  • 原文地址:https://www.cnblogs.com/Xuuuuuu/p/10853152.html
Copyright © 2011-2022 走看看