SciTech-BigDataAIML-Algorithm-Heuristic启发式 最优化算法: KMP字符串匹配 + A*(star)+ Prim + Dynamic Planning动态规划 + Floyd(弗落伊得)最短路线

SciTech-BigDataAIML-Algorithm-Heuristic启发式 最优化算法

所有的Heuristic(启发式)算法 的 “精髓” 在于对“Context(上下文)”的精准“计算、估计和预测”;
每一种算法都有其最适用的场景,算法的设计、选取 应当适配 场景;

非常好的“斯坦福大学”计算机科学毕业生的网站

Amit Patelamitp@cs.stanford.edu
很好的游戏算法+编程网:
https://www.redblobgames.com/pathfinding/a-star/introduction.html

  • KMP字符串匹配算法
  • Dynamic Planning动态规划
  • Dijkstra迪杰斯特拉算法
  • A*算法
  • Floyd(弗落伊得)最短路线算法
  • Prim算法

KMP字符串匹配算法

def kmp_search(string, patt):
  """signature: 字符串匹配算法: 在<string>,搜索<pattern>"""

  patt_len = len(patt)
  cmp_len = len(string) - patt_len + 1
  next = build_next(patt) # next数组的数值,代表"当前字符"匹配"失败"时,"可跳过"的"匹配字符个数".
  i = 0 # string  的指针: 只增不减,同时 pattern 的指针还可以预先偏移比较,是KMP的精华
  j = 0 # pattern 的指针

  while i < cmp_len : # 注意: i 只增不减, KMP算法的精华 
    if string[i] == patt[j]:
      # 字符匹配成功:
      i+= 1 # string 指针前移一个
      j+= 1 # pattern 指针前移一个
    elif j > 0:
      # 字符匹配失败: 之前有匹配成功的不只一个字符,next匹配时, 可跳过"上一次"匹配过的“那一部分”
      j=next[j-1] # pattern 指针退一字符, 停在最长匹配的字符上; 跳过pattern的前面一些子串. KMP精华
    else:
      # 字符匹配失败: 第一个字符就失配,只将"string"指针前移一个字符
      i+= 1 # string 指针前移一个字符.

    if j== patt_len: # 匹配成功: 当指针 j 等于 pattern 长度
      print(string)
      print("%s%s" % (' '*(i-j), patt))
      return i-j # 退出. return (i-j)


def build_next(patt):
  """
    预先计算构造出Next数组: PMT(Partial Match Table, 部分匹配表)
    用pdb 的step debug调试 build_next("ababcdabak"),  就对PMT的构造有最深刻的认知.
  """
  # next数组的数值,代表"当前字符"匹配"失败"时,"可跳过"的匹配字符个数".
  prefix_len = 0 #当前共同"前后缀"的长度
  next = [0] #next数组,初始时第一个元素的0
  k=1
  while k< len(patt):
    if patt[prefix_len] == patt[k]:
      prefix_len+=1
      next.append(prefix_len)
      k+=1 # k 前移一个字符
    else:
      if prefix_len==0:
        # 前一个字符不同;
        next.append(0)
        k+=1 # k 前移一个字符
      else:
        # **k不变**,**只改变 prefix_len**, 可能经过好几次的iteration, 才能得出要插入next数组的最终数值.
        # 当: k 由增加态, 首次转为0时,总之, 分两类情况: 
        # prefix_len=1, 例如 pattern = "ABCBAK", 
        #    当k=5时, 先prefix_len=1, 当前k位"反溯 prefix_len 个字符"的 "AK", 与 头部的 "AB"的"偏移"同;
               执行next step后, 有prefix_len=next[0]=0, 
        # prefix_len>1, 例如 pattern = "AACAAK",
        #    当k=5时, 先prefix_len=2, 当前k位"反溯 prefix_len 个字符"的 "AAK", 与 头部的 "AAB"的"偏移"同;
               执行next step后, 有prefix_len=next[1]=1
        prefix_len=next[prefix_len-1] # KMP的精华;
  return next



s = "ABABABCAA"
p = "ABABC"

def print_next(patt):
    print(build_next(patt))

# 用pdb 的step debug调试 build_next("ababcdabak"),  
# 就对 next数组(PMT)的构造有最深刻的认知.
# In [9]: print_next("ababcdabak")
# [0, 0, 1, 2, 0, 0, 1, 2, 3, 0]



KMP 字符串匹配算法:

  • KMP算法的时间复杂度为O(n+m)
  • 源于 Knuth, Morris, Pratt 三位大神的一篇论文"FAST PATTERN MATCHING IN STRINGS".




BFM(Brute Force Matching)

先引入BFM(Brute Force Matching, 暴力匹配):

  • BFM算法的时间复杂度为O(nm)
  • 即在<str>搜索<pattern>,
    由第一个字符开始一个个字符的与<pattern>比较,完全匹配上则return;
    如果不完全匹配,则跳到下一个字符,重新一个个字符与<pattern>比较。
    直到比较完成<str>的所有可能。


KMP( KMP Fast Pattern Matching)

KMP 快速匹配算法的思路是,不用BFM算法的"backup(回退)":
在匹配某一个字符时,
既然字符串在比对失败的时候,我们已经知道都读取比对过哪些字符序列,
有没有可能避兔backup(回退)到下一个字符,重新匹配”的步骤?


KMP 匹配实例
假设"string"为"ABABABCAA", 查找的"pattern"为"ABABC":

  • KMP匹配可能的最大长度字符串时: (i, j)指针对, 历经匹配的(0,0), (1,1), (2,2), (3,3),


  • KMP 匹配某一个字符, 字符串在比对失败时:

    • 到(i, j) 为 (4,4) 时,"pattern"出现第一个“不匹配字符”, 此时 j = 4,

    • 设置pattern头部"偏移": j=next[j-1], 偏移量可能值的情况是

      • j = 0, 当 都可能将"j"设置为0,即对 string 的 i 处的字符与 pattern 的首个字符开始,一一比较.
      • 0 < j < n, 当 pattern为一个字符重复出现 n次时, j 取得最大值:n-1

      本例是 j=next[j-1]=next[3]=2
      此时 j = 2, 同时 i=4(i不变)

    • 注意: 整个查找过程, i都是只向前移动,而pattern头部的“偏移量”根据"匹配情况"动态设置为[0, n)
      这是 KMP算法的两个最大特点

  • KMP 匹配某一个字符, 可以只 "Partial matching"(上一步比较过, 并且有Partial matched)跳过matched的部分:

    在本例:

    • 即"AB"已经在上一次时匹配过的LCS(Longest Common Substring, 最长匹配substr),本次可以跳过Partial;
    • 跳过"上一次匹配"过的"Partial matched", 是通过设置"本次匹配"的"pattern头部"的"偏移量"实现(string指针不变).
    • 实现上: 由pattern预先build的next数组, 取出"本次匹配"可"预先跳过比较"的"最长pattern字符数".
    • 源码:此时 string 指针 i 不变, 只要 pattern 指针设置成 j=next[j-1]( 因为j>0 ).

  • Papers: 下载 Knuth/Morris/Pratt 的Paper论文:
    ACM(Association for Computing Machinery) of the USA.
    https://dl.acm.org/doi/abs/10.1137/0206024
    多读和写论文绝对有好处:


Dynamic Planning动态规划

Dynamic Planning/Programming:
动态规划是计算机解决最优化问题的一种方法; 他给我们的印象是效率高、速度快。

  • 首先我们来看一个经典的“动态规划问题”:

    • 给一个无序数组,找出所有"最长递增子序列":
      举例: 无序数组nums=[1,5,2,4,3] 的 最长递增子序列: 一个是$ [1,2,4] , [1,2,3] $

    • 我们可以进一步简化这个问题:
      只要算法求出"最长递增子序列"的长度就好;

    • 枚举(普通解法):

      • 试验+探索:枚举代表性的一组数据(cases), 画出其"Recursion Tree":
      • 理论设计: 写出其"Recursion Function", 用"科学方法"在"理论上"评估算法性能(时间复杂度, BigO Notion)":


    • 理论优化:

      • Pruning(剪枝)


      • 记忆化搜索:






      • 工程优化,将Recursion Method改写成 Iteration Method方式:
        Iteration Method可以 释放对 Stack memory的占用,将"动态的内存需求"调整到用Heap memory满足:

    • 最终评估: 用"科学方法"在"理论上"评估算法性能(时间复杂度, BigO Notion)"

  • 扩展:



A *(Star) Algorithm(A星算法):

amitp@cs.stanford.edu
很好的游戏算法+编程网:
https://www.redblobgames.com/pathfinding/a-star/implementation.html

Loss Function: TotalCost="NewCost + PredicativeCost"

计算"总体的Cost": 包括一部分的"NewCost(已用成本)",和另一部分的"Predicative(预测成本)";
在对"predicative future cost(预测的将来成本)"的估计,和评估 TotalCost(总体成本)这些方面,
A *(Star) Algorithm(A星算法) 要优于 只计算 best next cost的Dijkstra(迪杰斯特拉算法)。

这是 A *(Star) Algorithm 多数时间 优于 Dijkstra Algorithm 的 root cause;
但是 Dijkstra Algorithm 的计算量小、算法精简。

1解决游戏的自动路线规划 2算法图示 3对比多种

主体实现

def heuristic(a, b):
   # Manhattan distance on a square grid
   return abs(a.x - b.x) + abs(a.y - b.y)

def a_star_search(graph,start,goal):
    frontier=PriorityQueue()
    frontier.put(start,0)
    came_from = {start:None}
    cost_so_far ={start:0}

    while not frontier.empty():
        # 由"优先队列"抽取出"总代价最低"(new_cost+heuristic(goal,next))的node
        current=frontier.get()
        if current= goal:
            break

        for next in graph.neighbors(current):
            new_cost = cost_so_far[current] + graph.cost(current,next)
            if next not in cost_so_far  and  new_cost < cost_so_far[next]:
                came_from[next]=current
                cost_so_far[next] = new_cost
                # 将next的node及其"总代价"插入"优先级队列"
                frontier.put(next, new_cost+heuristic(goal,next))

    return came_from, cost_so_far







Floyd(弗落伊得)最短路线算法


Prim算法


posted @   abaelhe  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
点击右上角即可分享
微信分享提示