使用 Elasticsearch 和 AI 构建智能重复项检测

作者:来自 Elastic Dayananda Srinivas

探索组织如何利用 Elasticsearch 检测和处理贷款或保险申请中的重复项。

Elasticsearch 带来了大量新功能,帮助你为你的使用场景构建最佳搜索方案。深入了解我们的示例 notebooks,开始免费云试用,或立即在本地机器上尝试 Elastic。


想象这个场景:一位客户在周一以 “Katherine Johnson” 申请了一笔 $50,000 的贷款,然后在周三又以 “Kate Johnson” 提交了另一份申请。是同一个人,同一个地址,但你的系统将他们视为不同的申请人。结果?重复批准、违规操作、严重财务损失。

这个挑战远不止简单拼写错误,它涉及复杂的欺诈手段、真实用户失误,以及人们实际填写表单时的各种情况。当你的重复检测失效时,你不仅是在亏钱 —— 你还在违反监管规定,破坏客户信任,并引发运营混乱。

让我们构建一个可以在后端处理数千份申请时捕捉这些重复项的解决方案。借助 Elasticsearch 的音近搜索能力和 LLM,我们将打造一个既强大又实用的系统。

重复检测的隐藏复杂性

重复检测的挑战远比多数组织意识到的更深。以下是真实场景中传统系统难以应对的情况:

姓名变化误导系统:

  • 经典拼写错误:"John Smith" vs "Jon Smith" vs "Jonathon Smith" —— 是拼写错误,还是不同的人?

  • 发音混淆:"Shawn" vs "Shaun" vs "Sean" vs "Shon" —— 发音相同,拼写不同

  • 昵称与变体:"Alexander" vs "Alex" vs "Xander" —— 同一个人,多种称呼

地址差异带来盲区:

  • 街道缩写:"123 Maple Street" vs "123 Maple St" vs "123 Maple Avenue" —— 同一地址,不同写法

  • 公寓表示差异:"Unit 5, 42 Elm Road" vs "42 Elm Rd, Apt 5" —— 同一地址,不同结构

  • 城市名称混淆:"Los Angeles" vs "LA" —— 地理名称的不同表达

家庭关系使检测复杂化:

  • 名字相似、地址相同:"Bob Brown" 和 "Rob Brown",住在 "789 Pine Rd" —— 他们是双胞胎,不是重复

  • 世代后缀:"James Carter Sr." vs "James Carter Jr." —— 一个字母的差异却意义重大

如果没有可靠方式标记这些重复记录,组织可能无意中为同一客户批准多笔贷款或保险单,违反资格规定,增加违约风险,并带来收入损失。

解决方案

通过将 Elasticsearch 与现代 AI 模型结合,我们可以构建一个智能、可扩展且高性价比的重复记录识别与清除方案。

姓名的音近搜索:
Elasticsearch 支持音近算法,能识别发音相同但拼写不同的名字。例如,“Smith” 和 “Smyth” 被视为相同,因为发音一致。这使系统能够发现简单文本匹配无法识别的重复项。你可以将其理解为教搜索引擎像人一样  “听” 名字 —— 让 “John” 和 “Jon” 被认为是同一个人。

处理地址格式差异:
用户输入的地址格式往往各不相同。我们使用 AI 模型生成地址的不同形式或同义词 —— 如 “Syd.” 和 “Sydney”、 “Bengaluru” 和 “Bangalore” —— 并利用这些变体优化 Elasticsearch 查询。即使用户输入与系统存储内容不完全匹配,也能实现地址匹配。

使用 AI 进行去重判断:
当我们从 Elasticsearch 检索到可能重复的记录后,将其传递给 AI 模型进行去重判断。虽然我们也可以使用 Levenshtein 或 Jaro-Winkler 等算法,但当涉及更多字段(如出生日期、身份证号或电话号码)时,逻辑会变复杂。AI 模型提供了灵活性,能够整体分析数据,简化判断逻辑,更容易在多个字段间识别真正的重复项。

架构概览

以下是该解决方案的高级工作流程:

亲自体验一下!

先决条件与设置

在进入具体实现之前,我们先确认所需的全部内容。

所需基础设施

  • Elasticsearch 集群 —— 你需要访问一个 Elasticsearch 集群。本次设置中,我使用的是 Elastic Cloud 托管的 Elasticsearch 9.0.0 版本。如果你还没有集群,有以下两种选择:

    • Elastic Cloud —— 你可以在这里创建一个新集群,并选择 Elastic Cloud 托管或 Elasticsearch Serverless 选项。

    • 本地设置 —— 如果你更喜欢本地运行,可以使用这里提供的脚本快速启动一个集群。

  • Phonetic Analysis 插件 —— 为支持姓名音近匹配,请确保你的 Elasticsearch 安装已启用 Phonetic Analysis 插件。

  • Ollama LLM 服务器 —— 鉴于我们要处理姓名、地址、出生日期等敏感信息,建议使用本地 LLM。可以使用 Ollama 运行轻量模型如 LLaMa 3.2 8B。它运行快速、本地部署,适合此类数据处理。

    • 先从这里下载并安装与你操作系统兼容的 Ollama 版本。

    • 安装完成后,运行 ollama run llama3:8b 拉取并运行该模型。

  • 样例数据集 —— 为测试设置并模拟真实场景,我准备了一个包含姓名、地址等细微差异的小型数据集。你可以从此链接下载包含 101 条记录的样例数据集。

下方是样例数据集的截图:

开发环境:

!pip install elasticsearch==9.0.2
!pip install pandas==2.2.2
!pip install -U langchain-community==0.3.26
!pip install -U streamlit==1.46.1
!npm install -g localtunnel@2.0.2

这些安装提供以下功能:

  • elasticsearch:用于连接 Elasticsearch 的客户端库

  • pandas:用于数据处理和 CSV 操作

  • langchain-community:用于集成 OpenAI,进行 AI 分析

  • streamlit:用于构建交互式网页界面

  • localtunnel:用于将本地开发环境暴露到公网

第 1 步:连接到 Elasticsearch

我们需要 Elasticsearch 的 endpoint 和 API Key 进行身份验证。

获取你的 Elasticsearch endpoint:

  • 登录 Elastic Cloud
  • 进入你的部署
  • 从部署概览中复制 Elasticsearch endpoint

创建 API key:

  1. 从你的部署中打开 Kibana
  2. 进入 Stack Management → API Keys
  3. 创建一个新的 API key 并妥善保存

当你获取到凭证后,建立连接。

将下面的代码保存并执行,文件名为 “es_connect.py”。记得填写 ES_URL 和 API_KEY 的值。

from elasticsearch import Elasticsearch,helpers
import os# Set environment variables
os.environ["ES_URL"] = ""
os.environ["API_KEY"] = ""# Use them
es = Elasticsearch(hosts=[os.environ["ES_URL"]],api_key=os.environ["API_KEY"]
)
# Test the connection
if es.ping():print("✅ Connected to Elasticsearch!")
else:print("❌ Connection failed.")

第 2 步:创建索引模板

我们重复检测系统的核心在于索引配置,尤其是我们指示 Elasticsearch 根据姓名生成音近编码的部分。我们将创建一个索引模板,使用姓名的音近匹配以及地址的最佳匹配。

了解语音分析器:

我们的模板使用了两种音近算法:

  • Double Metaphone:处理复杂的发音变化,适用于多样化的姓名

  • Soundex:为发音相似的姓名提供一致的编码

以下是完整的索引模板。

将下面的代码保存并执行,文件名为 “create_index_template.py”

from es_connect import es
# Define the index template
index_template = {"index_patterns": ["names-*"],"settings": {"index": {"analysis": {"filter": {"my_dmetaphone_filter": {"replace": "false","type": "phonetic","encoder": "double_metaphone"},"my_soundex": {"type": "phonetic","encoder": "soundex"}},"analyzer": {"name_analyzer_soundex": {"filter": ["lowercase","my_soundex"],"tokenizer": "standard"},"name_analyzer_dmetaphone": {"filter": ["lowercase","my_dmetaphone_filter"],"tokenizer": "standard"}}},"number_of_shards": "1","number_of_replicas": "1"}},"mappings": {"properties": {"address": {"type": "text","fields": {"keyword": {"ignore_above": 256,"type": "keyword"}}},"name": {"analyzer": "name_analyzer_dmetaphone","type": "text","fields": {"soundex": {"analyzer": "name_analyzer_soundex","type": "text"}}}}}}# Create the template
try:response = es.indices.put_template(name="name-search",body=index_template)print("Index template created successfully:", response)
except Exception as e:print("Error creating index template:", e)

这个模板的作用

  • 音近处理:在索引时,姓名会自动转换为音近编码

  • 多字段分析:每个姓名同时使用 Double Metaphone 和 Soundex 进行分析

  • 地址优化:地址同时支持全文匹配和精确匹配的索引方式

  • 灵活匹配:该模板支持多种搜索策略,适用于不同使用场景

第 3 步:加载并索引数据

现在,我们来加载样例数据集,并将其索引到 Elasticsearch 以便进行搜索。将以下代码保存并执行,文件名为 “index_csv_data.py”

import pandas as pd
from es_connect import es, helpers
file_path = input("Enter the path to your CSV file: ")
try:df = pd.read_csv(file_path)print("\nFirst 5 rows of the file:")print(df.head())
except Exception as e:print(f"Error reading the file: {e}")# Define the index name where the data will be ingested
INDEX_NAME = 'names-search'def generate_bulk_data(df):for index, row in df.iterrows():yield {"_index": INDEX_NAME,"_source": row.to_dict() }
# Delete if index already exists
if es.indices.exists(index=INDEX_NAME):es.indices.delete(index=INDEX_NAME)print(f"Index '{INDEX_NAME}' deleted.")try:response = helpers.bulk(es, generate_bulk_data(df))print("Data ingestion complete!")print(f"Indexed {response[0]} documents successfully.")
except Exception as e:print("Error during bulk ingestion:", e)

第 4 步:启动 Llama 模型并生成地址变体

获取用户输入的地址,并通过调用 LLM 生成该地址的多种变体,以处理一些细微差异。

例如,如果用户输入 "123 Maple St., Syd",模型将生成关键词,如 ["123 Maple St., Sydney", "Street", "Str", "Sydnei", "Syd"]。

# Import required libraries
import pandas as pd
import streamlit as st
import json
from es_connect import es
from langchain_community.llms.ollama import Ollama
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain@st.cache_resource
def get_ollama_model():return Ollama(model="llama3:8b",temperature=0.1,  # Low temperature for consistent resultsbase_url="http://localhost:11434"  # Default Ollama URL)def get_address_variations(model, input_address):prompt_template = PromptTemplate(template=("Given the address below, generate a concise JSON array of common synonyms, place name variations, abbreviations, misspellings, and variations people might use for this address.""Return ONLY a JSON array, with no explanation or extra text.""If no variations are found, return an array with only the original address.""Address: {address}"),input_variables=["address"],)chain = LLMChain(llm=model, prompt=prompt_template)try:llm_response = chain.run({"address": input_address})print("Raw LLM response:", llm_response)address_variations = json.loads(llm_response.strip())if not isinstance(address_variations, list):address_variations = [input_address]except Exception as e:print(f"Error parsing LLM response: {e}")address_variations = [input_address]return address_variations

第 5 步:构建最终搜索查询

使用从上述姓名和地址变体生成的值来构建搜索查询。

def query_elasticsearch(index, input_name, input_address, size=5):model = get_ollama_model()address_variations = get_address_variations(model, input_address)print(f"Address variations used for search: {address_variations}")# Use multi_match to search for all variations in the address fieldbody = {"query": {"bool": {"must": [{"match_phrase": {"name": input_name}},{"multi_match": {"query": " ".join(address_variations),"fields": ["address"],"type": "best_fields"}}]}},"size": size}print("Elasticsearch query body:", json.dumps(body, indent=2))response = es.search(index=index, body=body)return response['hits']['hits']

第 6 步:检查重复项

上述搜索查询会找到潜在匹配项。随后,这些姓名和地址将作为上下文提供给模型。通过以下函数,我们将提示模型计算它们是重复项的概率。

def check_duplicates(search_name, input_address, response):# Create prompt template optimized for local modelsprompt_template = PromptTemplate(template="""
You are a data deduplication expert. Analyze the following information and provide a structured comparison.Input Name: {search_name}
Input Address: {input_address}Potential matches from database:
{response}Instructions:
1. Compare each potential match against the input name and address
2. Calculate a match percentage (0-100%) for each record
3. Consider name variations, spelling differences, and address similarities
4. Classification Rules:- 80%+ match = Duplicate- 60-79% match = Review Required- Below 60% = Not Duplicate
5. Use the Jaro-Winkler algorithm principles.Format your response as a markdown table. Sort results based on match percentage. Include the columns:
| Name | Address | Match % | Status | Explanation |Example format:
| John Smith | 123 Main St | 95% | DUPLICATE | Exact name match, address variation |
| Jon Smyth | 123 Main Street | 85% | DUPLICATE | Name spelling variation, same address |""",input_variables=["search_name", "input_address", "response"],)# Prepare prompt inputprompt_input = {"search_name": search_name,"input_address": input_address,"response": response}# Create the LLM chainmodel = get_ollama_model()chain = LLMChain(llm=model, prompt=prompt_template)# Get the response from Ollamaresponse = chain.run(prompt_input)return response

第 7 步:创建 Streamlit 界面

现在,使用下面的 Streamlit 代码来创建一个简洁、直观的界面。

# Main function for Streamlit app
def main():st.set_page_config(page_title="Duplicate Detection (Local LLM)", layout="centered")# Custom CSS for stylingst.markdown("""<style>body { font-family: 'Arial', sans-serif; color: #333; }.stTextInput input { background-color: #f0f8ff; padding: 10px; border-radius: 5px; }.stButton button { background-color: #4CAF50; color: white; border-radius: 5px; }.stButton button:hover { background-color: #45a049; }.response-table th, .response-table td { padding: 10px; border: 1px solid #ddd; }.response-table th { background-color: #f4f4f4; }.response-table td { text-align: center; }</style>""", unsafe_allow_html=True)st.title("🔍 Duplicate Detection (Local LLM)")st.write("Enter the name and address to search for potential duplicates in the database.")# Input fieldsinput_name = st.text_input("Search Name", placeholder="Enter the name you want to search for...")input_address = st.text_input("Enter Address", placeholder="Enter the address")if st.button("Search 🔍"):if input_name and input_address:index_name = "names-search"with st.spinner("Searching for duplicates..."):# Query Elasticsearch for potential duplicateses_response = query_elasticsearch(index_name, input_name, input_address)# Build context from resultsif es_response:# Format the response data for the LLMformatted_response = []for hit in es_response:source = hit['_source']formatted_response.append(f"Name: {source['name']}, Address: {source['address']}")response = "\n".join(formatted_response)# Send the details to Ollama for analysisresponse = check_duplicates(input_name, input_address, response)# Display the response on the UIst.write("### Results Comparison from Dataset")st.markdown(response)# Show raw Elasticsearch results for referencewith st.expander("Raw Elasticsearch Results"):for hit in es_response:st.json(hit['_source'])else:st.write("❌ No potential duplicates found.")else:st.write("⚠️ Please enter both the name and address to search.")if __name__ == "__main__":main() 

第 8 步:执行并测试系统

为优化性能,避免重复加载模型和过多连接打开,将第 4、5、6、7 步的代码整合到一个名为 app.py 的文件中。然后用该文件启动 Streamlit 界面。

streamlit run app.py

执行后,会生成一个界面,允许输入姓名和地址。结果会以表格形式显示,按匹配百分比排序,并包含潜在重复项的解释,如下图所示。

贷款和保险申请之外的应用场景

去重技术在多个行业和领域有广泛应用。以下是一些主要例子:

  • 政府和公共服务 —— 标记重复的选民登记、税务记录、社会保障申请或公共福利项目注册。

  • 客户关系管理(CRM) —— 识别 CRM 数据库中的重复客户记录,提升数据质量,避免冗余。

  • 医疗系统 —— 发现医院管理系统中的重复病人记录,确保医疗历史和账单准确。

  • 电子商务平台 —— 识别重复的产品列表或卖家档案,维护干净的目录,提升用户体验。

  • 房地产与物业管理 —— 发现物业管理系统中重复的房源或租户信息。

总结:使用 Elasticsearch 构建去重流程

我们展示了如何结合 Elasticsearch 的音近搜索功能和本地 LLM 处理,创建一个应对真实复杂情况的强大去重流程。

首先,准备好集群和所需数据集,并部署本地模型。然后,利用姓名、地址及其变体等关键实体查询 Elasticsearch 找到相似匹配。接着,将 Elasticsearch 返回结果作为上下文传给模型进行重复分析。模型根据指令判断哪些记录可能是重复。

请记住,重复检测不是一次性项目,而是持续改进的过程。AI 组件会根据反馈学习,搜索算法也能基于结果不断优化,系统的准确性随时间提升。

通过在这些场景中实施 Elasticsearch,企业能够领先一步,确保数据准确、合规,并在快速变化的市场中保持竞争优势。

原文:Building intelligent duplicate detection with Elasticsearch and AI - Elasticsearch Labs

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在不依赖 Office 的情况下转换 PDF 为可编辑文档

在日常工作里&#xff0c;我们经常需要处理各种文件格式的转换问题&#xff0c;像Word转PDF或者PDF转Excel这样的需求屡见不鲜。它是一款功能全面的PDF转换工具&#xff0c;能够帮助你轻松应对多种文档处理任务。不仅能够实现PDF与其他格式之间的转换&#xff0c;如Word、Excel…

嵌入式学习笔记-MCU阶段--DAY09

1. oled屏幕的接口IIC应用场合&#xff1a;2.IIC通信原理概念&#xff1a;IIC&#xff08;Inter-Integrated Circuit&#xff09;其实是IICBus简称&#xff0c;所以中文应该叫集成电路总线&#xff0c;它是一种串行通信总线&#xff0c;使用多主从架构&#xff0c;由飞利浦公司…

解决 Node.js 托管 React 静态资源的跨域问题

在 Node.js 项目中托管 React 打包后的静态资源时&#xff0c;可能会遇到跨域问题&#xff08;CORS&#xff09;。以下是几种解决方案&#xff1a; 1. 使用 Express 中间件设置 CORS 头 const express require(express); const path require(path); const app express();// …

【Linux】多路转接之epoll

优化poll进行拷贝的开销poll开销过大将整个 pollfd 数组拷贝到内核态&#xff0c;以便内核检查 fd 是否就绪&#xff08;从用户态 → 内核态&#xff09;。内核检查 fd 状态&#xff0c;并填充 revents。将 pollfd 数组从内核态拷贝回用户态&#xff0c;让应用程序可以读取 rev…

下载一个JeecgBoot-master项目 导入idea需要什么操作启动项目

官网&#xff1a;开发环境搭建 | JEECG 文档中心 一般做开发的电脑里都是有的&#xff0c;没有的只能下载了 前端安装 node官网:https://nodejs.org/zh-cnpnpm安装:通过命令 后端安装: jdk17 :https://www.oracle.com/cn/java/technologies/downloads/#java17maven :https://m…

解决 InputStream 只能读取一次问题

是的&#xff0c;InputStream 的一个重要特性是它通常只能被读取一次。这是因为&#xff1a;输入流通常是单向的、顺序访问的数据源很多流&#xff08;如网络流、文件流&#xff09;读取后指针就移动了&#xff0c;无法回退有些流&#xff08;如Socket流&#xff09;甚至读取后…

数据分析面试题

技都测试 1、请列举5个 Excel 中常用的函数及写法。[ if ] IF(A1>60, "及格", "不及格") —— 若 A1 单元格数值≥60&#xff0c;返回 “及格”&#xff0c;否则返回 “不及格”。IF(B2>100, B2*0.8, B2) —— 若 B2 数值 > 100&#xff0c…

【07】VisionMaster入门到精通——Blob分折

文章目录0 视屏讲解与演示1 案例演示2 参数详解1 运行参数0 视屏讲解与演示 1 案例演示 周长使能查找U型槽 短轴使能查找U型槽 面积筛选区域 当条件不符合是&#xff0c;该模块显示红色&#xff0c;状态为NG 显示二值图像 显示Blob图像 2 参数详解 Blob分折&#xff0c;…

解释 MySQL 中的 EXPLAIN 命令的作用和使用场景

解释 MySQL 中的 EXPLAIN 命令的作用和使用场景 总结性回答 EXPLAIN 是 MySQL 中用于分析 SQL 查询执行计划的命令&#xff0c;它能展示 MySQL 如何执行一个查询&#xff0c;包括使用的索引、表连接顺序、扫描行数等关键信息。主要用于查询性能优化&#xff0c;帮助开发者识别潜…

.env 文件

.env 文件其实就是一个纯文本文件&#xff0c;用来写“环境变量”键值对&#xff0c;格式非常简单 &#x1f447;✅ .env 文件写法格式&#xff1a;每一行就是一个变量名 值&#xff0c;不要加引号&#xff0c;不要加空格DEEPSEEK_API_KEYsk-xxxxxxxxxxxxxxxxxxxx完整例子&…

机器学习——K 折交叉验证(K-Fold Cross Validation),案例:逻辑回归 交叉寻找最佳惩罚因子C

什么是交叉验证&#xff1f; 交叉验证是一种将原始数据集划分为若干个子集&#xff0c;反复训练和验证模型的策略。 交叉验证&#xff08;Cross-Validation&#xff09;适用于你在模型调参&#xff08;如逻辑回归中的 C&#xff09; 最常用的&#xff1a;K 折交叉验证&#…

蓝桥杯----串口

&#xff08;五&#xff09;、串口1、串口通信简介制定通信的规则&#xff0c;通信双方按照协议规则进行数据收发&#xff0c;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统&#xff0c;串口USART有两根通信线Tx、Rx&#xff0c;可同时双向通信&#xff0c;称之为…

错误: 找不到或无法加载主类 原因: java.lang.ClassNotFoundException

背景&#xff1a; 代码没有更改&#xff0c;主类位置也没有移动&#xff0c;运行时突然报找不到或无法加载主类的错误 错误: 找不到或无法加载主类 原因: java.lang.ClassNotFoundException编译器上方显示 Java file is located outside of the module source root so it wont …

Lock 接口及实现类详解:从 ReentrantLock 到并发场景实践

在 Java 并发编程中&#xff0c;除了synchronized关键字&#xff0c;java.util.concurrent.locks.Lock接口及其实现类是另一种重要的同步机制。自 JDK 5 引入以来&#xff0c;Lock接口凭借灵活的 API 设计、可中断的锁获取、公平性控制等特性&#xff0c;成为复杂并发场景的首选…

「iOS」————SideTable

iOS学习前言sideTableSlideTablesSideTableBufSideTable前言 我们在上一篇中&#xff0c;简单的介绍了weak的实现原理。其中弱引用表就是存储在SideTable中的&#xff0c;这里我们来学习了解一下SideTable sideTable sideTable主要用于存储和管理对象的额外信息&#xff0c;…

【PHP】CURL请求第三方API接口

当我们需要调用第三方接口时&#xff0c;就需要使用CURL&#xff0c;通过CURL操作去请求第三方API接口&#xff0c;有的是通过POST方式&#xff0c;有的是通过GET方式&#xff0c;下面介绍一个通用的使用CURL调用API接口的方法。一、CURL操作共两个方法&#xff0c;分别是CURL操…

对于考研数学的理解

文章目录高等数学总结补充说明1. 微分方程与无穷级数的特殊性2. 隐藏的逻辑链条3. 向量代数的桥梁作用核心框架为什么这样设计&#xff1f;结论线性代数核心逻辑框架各讲之间的本质联系1. 行列式 → 矩阵2. 矩阵 → 向量组3. 矩阵 向量组 → 线性方程组4. 矩阵 → 特征值与特征…

基于 Hadoop 生态圈的数据仓库实践 —— OLAP 与数据可视化(四)

目录 四、数据可视化与 Hue 简介 1. 数据可视化简介 &#xff08;1&#xff09;数据可视化的重要性 &#xff08;2&#xff09;数据可视化的用途 &#xff08;3&#xff09;实施数据可视化需要考虑的问题 &#xff08;4&#xff09;几种主要的数据可视化工具 2. Hue 简介…

HarmonyOS 开发:基于 ArkUI 实现复杂表单验证的最佳实践

摘要 在现代应用开发中&#xff0c;表单是最常见的交互方式之一。不管是用户注册、信息录入&#xff0c;还是登录验证&#xff0c;表单的可靠性直接影响用户体验。而在鸿蒙 ArkUI 开发中&#xff0c;虽然表单结构清晰&#xff0c;但要实现 复杂验证&#xff08;比如&#xff1a…

高效游戏状态管理:使用双模式位运算与数学运算

在游戏开发中&#xff0c;状态管理是一个核心问题。无论是任务系统、成就系统还是玩家进度跟踪&#xff0c;我们都需要高效地存储和查询大量状态。本文将深入分析一个创新的游戏状态管理工具类 GameStateUtil&#xff0c;它巧妙结合了位运算和数学运算两种模式&#xff0c;在存…