用于程序搜索的智能融合算法的设计与实现(C++,已用于程序中)

该程序搜索算法是我最近写的软件中使用到的算法,软件的项目地址如下:https://github.com/ghost-him/QuickLaunch/。建议打开源码,找到对应的代码后再阅读本文章。

该算法已经应用在软件中,并且取得了令我自己很满意的效果。

前言结束,以下是正文:


程序搜索算法介绍

该程序使用了融合算法来实现查找目标应用程序。搜索算法的实现位于model文件夹下的database类中。可以对着源码来查看本文档。

该搜索算法的特点是:

  • 可以用极少的字符匹配超长字符
  • 支持拼音(全拼与首字母拼音)搜索
  • 支持模糊查找

算法的总体流程

先对算法的运行流程熟悉后再对具体实现有所了解。

  1. 程序在初始化时,会根据配置读取电脑上安装的程序,并向database类中添加对应的记录。(通过insertProgramInfo函数添加)。
  2. 用户在按下alt + space后,会弹出来搜索栏,每当用户向其中输入一个字符(或删除一个字符),则会调用一次database类中的updateProgramInfo函数,而传入的参数则是当前搜索栏中的字符。
    1. updateProgramInfo会计算当前用户输入的字符串与存储的所有的记录的匹配度(programLevel
    2. 根据匹配度对所有的内容进行从小到大的排序
  3. uiController会选择指定的前几个记录作为显示项,将其添加到QListWidget中,显示给用户。

从以上的流程可以知道:

  • 每当用户输入一个字符,程序都会运行一次搜索算法
  • 算法的核心是根据用户输入的字符串计算每一个programNodeprogramLevel的值
  • 显示出来的匹配项为所有的programNodeprogramLevel最小的几个。

数据结构定义

对于一个程序,使用ProgramNode来维护,该结构体的定义如下:

struct ProgramNode {
    std::wstring programName;

    std::wstring compareName;
    std::wstring pinyinName;
    std::wstring firstLatterName;

    std::wstring programPath;
    double programLevel;
    int stableLevel;
    int launchTime;

    const bool operator<(const ProgramNode& other) const {
        if (programLevel != other.programLevel) {
            return programLevel < other.programLevel;
        } else if (launchTime != other.launchTime) {
            return launchTime > other.launchTime;
        } else {
            return compareName < other.compareName;
        }
    }
};

以下是字段的解释:

  • programName:该字段用于在界面中显示程序的名字,不参与搜索算法。
  • compareName:该字段用于精确匹配程序的名字。在插入数据时,会先进行预处理:
    • 将大字字母全部转换为小写字母(OneNote -> onenote
    • 将括号内的内容全部去除(qt creator 13.0.2 (community)->qt creator 13.0.2
  • pinyinName:该字段用于存储中文名字的程序的拼音(网易云音乐->wang yi yun yin le,因 字存在多音字的情况,所以无法正确匹配拼音)
  • firstLatterName:该字段用于存储中文名字的程序的拼音首字母(网易云音乐->wyyyl
  • programPath:该字段用于存储该程序在磁盘中的位置
  • programLevel:该字段用于存储该程序的总权重
  • stableLevel:该字段用于存储程序的固定权重
  • launchTime:该字段用于存储程序的启动次数

具体流程

programLevel的值取决于以下三个值:directValuepinyinValuefirstLatterValue,其意思分别是:精确匹配的匹配值,使用拼音匹配的匹配值,使用拼音首字母匹配的匹配值。而programLevel的值取这三个中最小的那个。

至于三个值的计算均由computeCombinedValue函数完成,该函数也是程序搜索算法的核心。

该函数有两个参数:storeNameinputNamestoreName表示存储的程序的名字,而inputName表示用户输入的程序的名字。融合算法由以下三种子算法构成:

  1. 最短编辑距离变体
  2. KMP算法变体
  3. 最长公共子序列变体

除了以上的子算法,还有其他的条件来确保匹配:

  • 判断输入的长度是否大于程序的名字的长度(精确名字与拼音名字的最大值),如果已经大于程序的长度了,则一定不是该程序(在运行时,如果不设置该条件,则会存在无法匹配到拥有长程序名的程序)。

最短编辑距离变体

最短编辑距离变体算法由editSubstrDistance函数实现,左边为内存中存储的字符串,而右边为用户输入的字符串。

该函数的计算流程是:当用户输入的字符串中,最少经过[添加一个字符|删除一个字符|修改一个字符]几次后可以变为左边的子字符串。并且计算修改的次数占总输入数据长度的比值 x x x(通过除法,将值映射为[0-1]区间内)。并使用 y ( x ) = 25 ∗ 3 3 ∗ x − 2 y(x)=25*3^{3*x-2} y(x)=2533x2函数将其映射到 [ 2.7 , 75 ] [2.7, 75] [2.7,75]之间。(该函数没有特别设计,关键是一个指数函数,且 y ( x ) y(x) y(x)足够小, y ( 1 ) y(1) y(1)足够大,凹函数)

至于为什么这样设计,可以这样理解:如果我输入了5个字符,比如steam,但是这5个字符,需要修改4次,才可以成为deepl的一个子字符串,那么有极大的概率查找的不是这个程序。相反,我输入的这5个字符无须修改就可以成为steam的一个子字符串,那么说明我查找的程序很有可能就是这个程序。综上所述,steamprogramLevel会比deeplprogramLevel小。

以下是计算的过程,输入的字符串为steam

  • 对于程序steam来说:修改0次(steam->steam),则比值min_operations 0 / 5 = 0 0/5=0 0/5=0。再经过函数的映射,可以得到directValue2.77
  • 对于程序deepl来说:修改4次(steam->deepl),则比值min_operations 4 / 5 = 0.8 4/5=0.8 4/5=0.8。再经过函数的映射,可以得到directValue38.79

为什么要使用指数函数将其扩大?

  • 为了方便后续的处理。当输入steam时,可能会匹配其他的选择,比如steam support center。此时,如果单纯的使用该算法作为判断依据,则无法处理这个情况(两者的值一样)

KMP算法变体

KMP算法变体由kmp函数实现,其具体的处理过程如下:

  1. 如果输入的字符串是一个程序的子字符串,则返回其输入长度的负值。
  2. 如果输入的字符串不但是程序的子字符串,同时程序还是以该字符串开头的,则返回2倍的负的输入长度

通过该算法可以实现:如果我输入command,那么command prompt的值会比developer command prompt for vs 2022小,可以实现前者排在后者的上面,优先被选中。

注意:该函数返回的值是一个负值。相加时可以减少programLevel的值,从而提高匹配程序的排名。

最长公共子序列变体

最长公共子序列算法求的是输入字符串与目标字符串的最长的公共子序列的长度。比如:

text1 = "abcde", text2 = "ace" 
输出:3  
解释:最长公共子序列是 "ace" ,它的长度为 3 。

以上数据来源:力扣

在本程序中,使用LCS函数实现。当该函数计算完子序列的长度previous[n]时,会计算该子序列相对于输入字符串与目标字符串的差值。即:m + n - 2 * previous[n]。这样可以防止输入steam时,steamsteam support center的值一样,可以优先匹配长度更短的字符串。同时,使用该算法可以弥补最短编辑距离变体的缺点。由于该算法对于结果的影响较大(比如以上例子,匹配steam support center时,该函数运行的结果为15,会对结果造成很大的影响,可能排在第二位的选项不是该程序而是其他完全不相关的程序),所以在融合算法中,差其权重设置为了0.2,从而来减小影响。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/744766.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java | Leetcode Java题解之第198题打家劫舍

题目&#xff1a; 题解&#xff1a; class Solution {public int rob(int[] nums) {if (nums null || nums.length 0) {return 0;}int length nums.length;if (length 1) {return nums[0];}int first nums[0], second Math.max(nums[0], nums[1]);for (int i 2; i <…

SpringBoot整合Mybatis并实现数据库增删改查

写在前面 Mybatis一个基于Java的持久层框架&#xff0c;它通过XML或注解的方式&#xff0c;将SQL语句和Java方法进行映射&#xff0c;使得开发者可以轻松地进行数据库操作。下面我会演示mybatis的配置与使用并实现数据库的增删改查。 1.准备测试数据 使用mybatis实现对数据库…

石油化工厂为什么要用专业防爆手机?

防爆手机之所以必须使用专业设计的产品&#xff0c;主要是出于安全考虑&#xff0c;以防止在易燃易爆环境中因手机使用不当引发爆炸事故。以下几点详细解释了使用专业化工防爆手机的必要性&#xff1a; 本质安全设计&#xff1a;顶坚专业防爆手机采用了本质安全&#xff08;本安…

权重衰退及代码

一、硬性限制 1、通常不限制偏移b&#xff0c;因为限制不会有区别&#xff1b;seta越小&#xff0c;意味着正则项强 2、优化的是最小化的损失函数 3、后部的限制条件&#xff0c;每个项的平方和小于一个值&#xff1b;极端情况下&#xff0c;当seta等于0&#xff0c;意味着所…

【node】深入探讨 class URL

【node】深入探讨 class URL &#x1f4cc; 浅说 fileURLToPath() 在vite.config.ts中有这么一段代码&#xff1a; import { fileURLToPath, URL } from node:url import { defineConfig } from vite export default defineConfig({resolve: {alias: {: fileURLToPath(new U…

github无法访问,下载慢的解决方法

GitHub是一个存储分享无数的开源项目和代码的宝库网站。然而&#xff0c;由于一些原因&#xff0c;国内用户在访问GitHub时常常遭遇无法访问或下载速度缓慢的问题。这不仅影响了开发者的工作效率&#xff0c;也使一些想要访问下载github文件的普通用户遇到困难。下面小编就来和…

线性代数、矩阵计算

一、线性代数 1、对于向量&#xff0c;若a是标量&#xff0c;为a的绝对值乘以b的向量长度。 2、点乘 3、范数&#xff1a;向量或者矩阵的长度 L1范数&#xff1a;&#xff08;对向量&#xff09;每个元素的绝对值求和 L2范数&#xff1a;&#xff08;对向量&#xff09;torch.…

Websocket在Java中的实践——最小可行案例

WebSocket是一种先进的网络通信协议&#xff0c;它允许在单个TCP连接上进行全双工通信&#xff0c;即数据可以在同一时间双向流动。WebSocket由IETF标准化为RFC 6455&#xff0c;并且已被W3C定义为JavaScript API的标准&#xff0c;成为现代浏览器的重要特性之一。 WebSocket的…

【嵌入式Linux】i.MX6ULL 外部中断服务函数的初始化

文章目录 1. Cortex-A7 中断系统1.1 分析1.2 具体处理流程 2. 外部中断服务函数的初始化2.1 基本流程分析2.2 具体代码分析2.2.1. 定义中断处理类型和结构体2.2.2. 初始化中断系统2.2.3. 注册中断处理函数2.2.4. 具体的中断处理逻辑2.2.5. 默认的中断处理函数 3. 完整代码 本文…

002_unsigned long数据比较的坑?

【背景】 unsigned long 类似数据的比较问题&#xff0c;先上一段代码&#xff0c;如下图所示&#xff1a; 就是图中框出的部分&#xff0c;眨眼一看&#xff0c;应该没啥问题&#xff0c;而且我也在本地的编译器vs2019上编译了&#xff0c;确实也没有报错&#xff0c;所以就修…

【Linux】静态库、动态库

动静态库里面包含的是源文件通过汇编阶段生成的后缀为.o的可重定位目标文件。我们在使用C语言&#xff0c;包含一个stdio.h头文件就可以使用scanf方法&#xff0c;其实都是系统调用了相应的头文件和库&#xff0c;库里面有开发者已经写好各种方法。也就是说我们在使用C语言时&a…

Java | Leetcode Java题解之第191题位1的个数

题目&#xff1a; 题解&#xff1a; public class Solution {public int hammingWeight(int n) {int ret 0;while (n ! 0) {n & n - 1;ret;}return ret;} }

【学习】软件测试中常见的文档类型及其作用

在软件开发的生命周期中&#xff0c;软件测试是确保产品质量的关键步骤。为了系统地进行测试活动&#xff0c;并保证测试结果的有效性和可追溯性&#xff0c;产生了一系列标准化的测试文档。这些文档不仅为测试人员提供了执行指南&#xff0c;而且为项目管理者和利益相关者提供…

【排序 队列】1585. 检查字符串是否可以通过排序子字符串得到另一个字符串

本文涉及知识点 排序 队列 LeetCode1585. 检查字符串是否可以通过排序子字符串得到另一个字符串 给你两个字符串 s 和 t &#xff0c;请你通过若干次以下操作将字符串 s 转化成字符串 t &#xff1a; 选择 s 中一个 非空 子字符串并将它包含的字符就地 升序 排序。 比方说&a…

Discourse 的 AI 内容分享

虽然 Discourse 的 AI 接口调用是需要比较高的用户权限或者管理员权限。 但是对已经生成的结果&#xff0c;Discourse 是可以保存并且分享的。 例如&#xff0c;我们搜索了一些美食的做法。 在页面的下面有一个分享 AI 对话的按钮。 在随后弹出的界面中&#xff0c;会又一个…

服务运营 | MS文章精选:线上点单,当真免排队?餐饮零售与医疗场景中的全渠道运营

编者按&#xff1a; 小A走进了一家奶茶店&#xff0c;准备向店员点单&#xff0c;但却在屏幕上看到还有98杯奶茶待制作&#xff08;因为线上订单突然暴增&#xff09;。因此&#xff0c;小A不满地嘟囔着离开了奶茶店。这个例子展示了线上渠道可能会对线下渠道造成一些负面影响…

链表数组遍历输出的辨析(二者都含指针的情况下)----PTA期末复习题

输入输出三位学生的学号和信息 一开始我认为是指针&#xff0c;直接背了指针输出的方式&#xff1b;p;p!NULL;pp->next 这个是错误的 下面这个输出是正确的方式 分析怎么区分这两个 举个例子来 数组遍历&#xff1a; 链表遍历&#xff1a; 输出的结果&#xff1a; 如果将…

第十次作业

1.登陆界面 2.导航页面 3.接口&#xff08;我负责的主要是管理员管理用户和密码的界面&#xff09; import request from /utils/request// 登录 export function login(data) {return request({url: /user/login,method: post,data}) }// 获取用户信息 export function getIn…

网关登录校验

如何在网关转发之前做登录校验&#xff1f; 网关请求处理流程 如何在网关转发之前做登录校验&#xff1f; 网关如何将用户信息传递给微服务&#xff1f; 如何在微服务之间传递用户信息&#xff1f; 自定义过滤器 网关过滤器有两种&#xff0c;分别是&#xff1a; GatewayFi…

春秋云境:CVE-2022-25411[漏洞复现]

根据题目提示和CNNVD优先寻找后台管理地址 靶机启动后&#xff0c;使用AWVS进行扫描查看网站结构 在这里可以看到后台管理的登录地址&#xff1a;/admin/&#xff0c;根据题目提示可知是弱口令 尝试admin、123456、admin666、admin123、admin888...等等常见弱口令 正确的账户…