IT技术博客大学习 共学习 共进步

算法

共 606 篇文章

IT 2022-07-24 20:47:15 / 累计浏览 4,280

一个 VLA (可变长度数组)的实现

这篇讲的是作者如何用C语言实现一个更实用、更安全的可变长度数组库。C99引入的VLA特性因安全问题已被MSVC和Linux内核相继放弃,但在日常开发中,变长数组的需求依然存在。现有的通用方案,如C++的std::vector或简单的void*实现,在类型安全、性能(堆内存分配)和与特定运行时(如Lua)的集成方面各有不足。 作者的方案核心是将VLA拆分为抽象的“句柄”和类型化的“访问器”。通过`vla_using`宏,在栈上创建一个指向实际数据的原生指针作为访问器,同时关联句柄,从而在保证类型安全和原生数组访问性能的同时,提供了清晰的API。为兼顾临时使用与持久引用的不同场景,方案统一了栈上缓存与堆分配的切换逻辑。 更巧妙的一点是,作者针对与Lua交互的场景,实现了利用Lua GC管理内存的第三种模式:小块内存直接分配在C栈上,大块内存则转为Lua临时userdata,随函数退出自动回收,省去了手动清理的麻烦。整个实现展示了如何在C语言的限制下,通过宏技巧和分层设计,构建出一个既高效又贴合实际工程需求的通用数据结构。

IT 2022-07-24 20:46:45 / 累计浏览 3,916

用邻接表实现无向图

这篇讲的是作者在游戏管道系统扩展中,如何用邻接表实现一个无向图数据结构。作者从原有的液体管道系统(采用类似树的固定数组存储)出发,面对电线网络等新需求——节点无方向且可多邻接,决定重新设计。核心思路是将节点id与边关系分离,便于模块化复用。具体实现上,每条边用32位紧凑表示(16位端点id对,小id在低位确保唯一性),并设计了一个巧妙的结构:在边数据中加入两个链表指针,分别指向两端点所属的边集合,从而支持高效遍历。 更精巧的是,作者进一步压缩了存储:通过将其中一端点的边集合连续排列,另一端以链表链接,并用“反转端点id”来标志链表结束(通常小id在前),最终每条边只需48位。文章以九宫格网格图为例,完整演示了如何从节点索引开始,逐步追踪并找出顶点5的全部四条邻接边。作者坦言这种无向图实现并非日常工作所需,但经过半天编码,能写出这样紧凑高效的结构让他颇为满意,其压缩方案甚至可能具有一定的原创性。

IT 2022-06-19 18:18:07 / 累计浏览 5,060

文言文白话文互转:文言文转白话文(现代文),白话文(现代文)转文言文

这篇讲的是作者利用一个开源的文言文-现代文平行语料库,动手实践了双向互译模型的全过程。起点是东北大学团队整理的约96万句对经典古籍对齐数据,这份珍贵语料覆盖广且经过人工校对,为模型训练打下了基础。作者基于此,训练了文言文转白话文、白话文转文言文两个独立的神经网络机器翻译模型,并将它们集成到AINLP公众号,用户可通过指令直接测试。文中展示了几个转换示例,说明了模型已能完成基本互译,不过作者也坦诚效果基于现有数据和模型,“仅供一乐”。整体来看,这是一次从优质语料获取、模型训练到功能部署的完整技术实践,让古籍翻译的探索变得具体而可玩。

IT 2021-05-28 08:32:57 / 累计浏览 2,372

不变量及运算优化

这篇讲的是游戏引擎开发中一个看似简单却消耗10%以上CPU时间的性能坑。作者从实际Profiling结果出发,发现每帧重建渲染队列时,需要对每个可渲染物件的包围盒进行世界空间变换运算,而场景中大量静态物件的这类运算是重复的。 问题的根源在于输入数据量太大(40个float),直接用作缓存键并不现实。巧妙之处在于,作者利用数学库中矩阵已作为不变量拥有唯一ID这一现有设计,将世界空间矩阵的ID作为缓存键,大幅缩减了比较开销。最终,缓存能按“世界矩阵ID”匹配并直接复用上一帧计算好的所有子模型变换结果,避免了重复计算。这个优化思路透明地解决了问题,而无需对场景或资源系统进行大改。

IT 2021-02-13 23:27:09 / 累计浏览 2,033

对话任务中的“语言-视觉”信息融合研究

这篇讲的是如何让AI在视觉对话中更“会看眼色”。研究者们针对“目标导向的视觉对话”任务发现,现有模型有个明显短板:对话中的回答(比如“是”或“不是”)对视觉注意力的引导作用太弱。当回答改变时,AI的目光焦点本该相应转移,但旧方法往往只是简单地拼接语言和图像特征,没能突出这种动态调整。 为此,北京邮电大学与美团AI团队合作提出了一个“响应驱动的视觉状态估计器”(ADVSE)。这个模型的核心在于两个新机制:一个是“答案驱动的注意力更新”,它能根据当前回答是肯定还是否定,来决定是聚焦当前物体还是转移目光搜索新目标;另一个是“条件视觉信息融合”,可以自适应地混合图像的全局信息和差异信息。这使得模型能像人一样,根据对话进展灵活调整“看图”的策略。 在国际通用的GuessWhat?!数据集上,这个ADVSE模型在问题生成和回答任务上都取得了当时的最佳成绩。它让机器在需要通过多轮对话寻找目标物体(比如从一堆物品里找出某个)时,对话策略更有效率,也为智能助手或交互机器人等应用提供了更扎实的技术基础。

IT 2020-02-05 15:09:47 / 累计浏览 2,676

位运算技巧整理

这篇讲的是位运算(&、|、^、~)的基础规则与实用技巧的整理。作者从最基本的四种操作讲起,但真正的亮点在于后续展示的几招“组合技”:比如,利用“一个数和自己异或结果为0”的特性,可以高效找出数组中唯一一个出现奇数次数的数字;再如,当除数是2的幂次方时,用 `num & (len-1)` 来取余数,比直接用取模运算符 `%` 的效率更高。 文章还系统梳理了十进制与二进制之间的手工转换方法,补全了从原理到实践的理解链条。这些技巧并非炫技,在底层优化、嵌入式开发或算法面试中都有实实在在的应用。文章将零散的位运算知识串联成了可直接使用的“工具包”,对于想深入理解计算机底层运算的开发者来说,是一份清晰的备忘录。

IT 2019-04-09 00:38:55 / 累计浏览 2,176

活动 Web 页面人机识别验证的探索与实践

这篇讲的是电商活动页面如何“静悄悄”地对抗机器人刷单。作者从一个现实痛点出发:营销活动页因Web环境透明,易被脚本直接调用API“薅羊毛”,损害用户体验与平台利益。 文章核心是介绍一套多层次的人机识别技术方案。它不依赖显眼的验证码,而是通过前端收集用户进入页面、停留、滚动、点击等行为数据,结合设备环境信息,形成“行为指纹”提交给风控服务校验。为确保这套机制不被破解,方案在通信安全和代码保护上做了深入设计:利用有时效的动态Token绑定会话,对传输数据进行对称加密,并通过代码混淆与反调试处理增加前端逻辑的反编译成本。 作者详细拆解了从基础风控拦截、Token下发、数据加密上报到最终业务处理的完整流程。这套组合拳的目标是在用户无感知的前提下,让安全验证像背景一样自然运行,既防住了作弊,又守住了活动体验。

IT 2019-03-25 23:35:42 / 累计浏览 2,080

阿里面试题:为什么Map桶中个数超过8才转为红黑树

这篇讲的是一个经典的Java面试题:为什么HashMap的桶中链表长度达到8才转为红黑树?作者从一个好友的阿里面试经历切入,直接打开了源码中的注释,发现它只记录了阈值,却未解释原因。 文章的核心在于对源码“Implementation notes”的深入解读。作者指出,红黑树节点占用的空间是普通节点的两倍,因此转换是一种空间与时间的权衡。更关键的是,文章引用了源码中一段关于泊松分布的注释:在随机哈希算法下,桶中节点数量遵循特定的概率分布,链表长度达到8的概率极低(仅约千万分之六)。这从统计学角度证明了阈值8的选取并非随意,而是经过严谨计算的。 此外,文章也驳斥了一种常见但不够严谨的“性能对比”解释,强调了设计背后的科学性。通过剖析源码与概率模型,这篇文章将一个常见面试考点还原成了其严谨的设计思想,适合所有想理解Java集合框架底层优化的开发者。

IT 2019-03-25 23:27:39 / 累计浏览 3,685

机器学习算法之LightGBM

这篇讲的是GBDT模型的又一个高效实现:LightGBM。文章没有停留在简单介绍,而是从“既然XGBoost已经很好,为什么还需要LightGBM”这个问题切入,详细拆解了后者在工程上为应对海量数据所做的核心优化。 作者对比了两者的关键差异:XGBoost采用预排序算法,虽然精确但内存与时间开销巨大;LightGBM则引入了直方图算法,将连续特征离散化,使内存消耗降为原来的1/8,计算复杂度也从O(#data*#features)大幅优化。同时,它还摒弃了传统的按层生长策略,改用带有深度限制的按叶子生长,进一步提升了效率。 文章通过实验数据直观展示,这些改进让LightGBM的训练速度提升近10倍,内存占用仅为XGBoost的1/6,且准确率有所提高。这对于处理工业级大规模数据,同时追求模型性能与资源效率的场景,提供了非常切实的解决方案。全文对技术动机和实现原理的剖析,对于想理解模型“快”与“准”如何兼得的工程师很有启发。

IT 2019-03-25 23:07:36 / 累计浏览 1,854

你是如何了解或者进入NLP这个领域的?

这篇讲的是AINLP公众号发起的一次赠书留言征集活动,却意外收获了超过200条关于“如何进入NLP领域”的真实分享。作者将这些充满个人色彩的故事做了汇总,为我们勾勒出一幅生动的NLPer入行图景。 从留言中可以看到,许多人的起点充满了“偶然”:数学系的背景被导师安排做统计机器翻译,英语专业的学生因无法忍受纯人工内省而自学编程切入,甚至有心理学和文科背景的同学为了解决论文中的文本分析难题,独自摸索着走进了这个领域。另一个共性是强烈的自驱力——在缺乏系统指导的情况下,通过啃经典教材(如《统计自然语言处理》)、刷公开课、关注技术社区,从零搭建起知识体系。 这些故事背后,是一个个具体的技术探索:从Lucene分词的好奇,到词性标注与概率统计的实践,再到BERT、知识图谱的前沿追踪。它们共同指向了NLP领域的迷人之处:它用数学和代码为语言赋予了可计算的维度,而通往这个大门的道路却向所有充满热情和毅力的人敞开。活动本身也通过赠书和互动,完成了一次社区内宝贵的连接与传承。

IT 2019-02-21 21:49:23 / 累计浏览 2,351

一些不常见但是很重要的数据结构

这篇源自Stack Overflow高赞讨论的整理,系统梳理了那些在日常编程中不常被提及、却在特定领域发挥关键作用的数据结构。文章并非泛泛而谈,而是紧扣具体应用:比如Bloom filter如何在BigTable、Cassandra中用于快速存在性检查,Skip list作为Redis有序集合的底层实现原理,以及Rope数据结构如何通过高效的字符串拼接操作,在Java等语言中胜任繁重的文本处理任务。 作者将这些结构与经典方案对比着介绍,突出了各自的核心价值:Splay tree的简洁与良好性能,Suffix tree用于字符串搜索的O(n)构建优势,以及Cuckoo Hashing利用多哈希函数提升空间利用率的巧妙思路。同时,文章也涵盖了并查集、Merkle tree、无锁数据结构等并发与特定场景下的利器,甚至提及了缓存参数无关、左偏红黑树等更前沿的方向。 整篇文章更像一份精心挑选的“数据结构工具箱”清单,它不仅扩充了开发者的知识库,更揭示了在解决特定性能或规模问题时,超越常规选择的可能性。对于想夯实基础、或寻找更优解方案的技术读者,这份清单提供了明确的索引和深入探索的起点。

IT 2019-01-01 20:44:30 / 累计浏览 2,551

图数据库简介

这篇讲的是图数据库的核心概念与适用场景。作者从NoSQL的大家族中引出图数据库,指出它用节点和边来存储高度关联的数据,比如社交网络中用户之间的关注关系。文章重点解释了当前流行的“带标签的属性图”模型,节点和边都可以拥有多个属性和标签,这使得数据建模非常灵活。 文章将图数据库与传统关系数据库进行了对比。核心差异在于:关系数据库擅长处理结构规整的事务,但在进行多层、反向的关联查询(比如“谁的朋友的朋友买了什么”)时,会产生大量表连接,导致性能骤降。而图数据库将节点和关系视为一等公民,采用原生存储和双向指针,使得这类复杂关系遍历的查询速度能保持在很高水平。 因此,作者得出的结论是,图数据库并非要取代关系型数据库,而是为社交网络、推荐系统等依赖复杂关系图谱的场景提供了更高效的解决方案。它的优势在于更自然的数据建模、更快的关联查询性能以及更灵活的Schema调整。

IT 2019-01-01 20:23:19 / 累计浏览 1,949

哈希函数介绍 | 哈希算法

这篇讲的是哈希函数的核心概念与各类算法的应用对比。作者从哈希函数作为关键字到存储地址的“映射”本质出发,重点解析了哈希应用中需要解决的“冲突”问题。 文章的核心在于系统梳理了几类主流的加密哈希算法。它介绍了MD5、SHA-1、SHA-2、SHA-3及RIPEMD-160的基本原理,并结合Node.js代码示例,直观展示了它们的输出。关键点在于对比了它们的安全状态与适用场景:例如,MD5因碰撞问题已不适合安全用途,更多用于文件校验;SHA-1正被各大厂商逐步弃用;而SHA-2与SHA-3则代表了当前更安全的选择。 除加密算法外,文章也简要提及了用于高速查找的非加密算法,如MurmurHash,拓宽了“哈希”的应用图景。整体上,文章清晰地梳理了“为何用哈希”以及“如何根据场景选择不同哈希算法”这两个核心问题。

IT 2018-09-20 21:51:35 / 累计浏览 3,564

一维数组的聚类

这篇讲的是如何更智能地划分一维数据的区间。作者从分析订单价格分布的实际问题出发,指出简单按固定梯度(如每100元)划分可能忽视数据中天然存在的“分隔点”(比如Airbnb房价分布),导致分组不自然。 文章详细比较了三种解决一维聚类的方案。首先是将数据reshape成二维后使用通用的K-Means算法。其次是专门针对一维数据的Jenks Natural Breaks自然断点法,它通过最小化类内方差之和来寻找最佳分界点,并探讨了使用GVF指标来确定最优聚类数K的经验方法。第三种是利用核密度估计,通过寻找概率密度曲线的极值点(波峰与波谷)来自动划分数据。作者不仅阐述了原理,还提供了Python实现代码,清晰地展示了如何运用Jenks算法计算GVF值,以及如何用KDE寻找数据的自然断裂处。整个对比有助于读者根据数据特点和分析需求,选择最合适的区间划分工具。

IT 2018-07-05 14:00:16 / 累计浏览 3,681

相似度计算之兰氏距离

这篇讲的是相似度计算中的兰氏距离,也被称为堪培拉距离,它被认为是曼哈顿距离的加权版本。作者从定义公式出发,展示了兰氏距离如何通过绝对差值除以绝对值之和来计算两个向量间的距离,公式为 \( d(\mathbf{p}, \mathbf{q}) = \sum_{i=1}^{n} \frac{|p_i - q_i|}{|p_i| + |q_i|} \)。 兰氏距离有几个关键特性:它对接近于零(大于等于零)的值的变化非常敏感,这使得它在处理包含小数值的数据时特别有用。同时,与马氏距离类似,兰氏距离对数据的量纲不敏感,无需标准化即可处理不同尺度的变量。不过,它假定变量之间相互独立,没有考虑变量间的相关性,这在某些复杂数据场景下可能限制其应用。相比之下,曼哈顿距离更简单但缺乏加权机制,而马氏距离能捕捉相关性但计算更复杂。 文章还提供了Python实现,代码简洁地通过循环累加每个维度的距离贡献,并处理了零值情况。这种实现突出了兰氏距离在实际编程中的易用性,适合快速集成到数据分析流程中。整体上,这篇文章清晰地剖析了兰氏距离的核心概念、优缺点和实际应用,帮助读者理解它在选择距离度量时的独特价值。

IT 2018-07-04 23:47:09 / 累计浏览 3,319

常见相似度计算方法回顾

这篇技术博客系统梳理了数据科学和机器学习领域常见的五种相似度度量方法,为相关从业者提供了一个清晰的快速参考。文章从基础的空间距离概念出发,依次回顾了欧几里得距离(直观的直线距离)、曼哈顿距离(各坐标轴绝对差值之和)、闵氏距离(前两者的泛化形式)、余弦相似度(衡量向量方向差异而非长度)以及杰卡德相似度(基于集合的交并比)。 每种方法都配有形象的示意图和简洁的Python实现代码,使得理论概念与实践应用得以紧密结合。作者不仅解释了各自的数学定义,还隐含了它们的应用倾向:例如,欧氏距离适用于空间聚类,余弦相似度常用于文本向量比较,而杰卡德相似度则擅长处理离散的集合数据。 整体而言,这是一篇非常实用的“备忘录式”文章。它没有深入推导公式,而是通过清晰的对比和可运行的代码,帮助读者快速重温或上手这些关键工具,尤其适合需要在不同场景下选择合适度量方法时进行查阅。

IT 2018-07-04 23:45:09 / 累计浏览 3,198

相似度计算之马氏距离

这篇讲的是马氏距离(Mahalanobis Distance)。作者首先指出了它和常见的欧氏距离的本质区别:马氏距离通过引入协方差矩阵,巧妙地“吸收”了数据各维度之间的相关性,并且不受量纲(测量单位)影响。 文章的核心在于解释它如何工作。简单说,马氏距离可以看作是将原始数据投影到由协方差矩阵定义的“标准化”空间后的欧氏距离。文中用了一个直观的图示:在椭圆形的等高线分布中,红点到黑点的欧氏距离小于绿点到黑点,但若考虑数据分布的相关性,马氏距离的结论可能正好相反。这清晰地展示了它在处理特征相关时的威力。 文章不仅梳理了方差、协方差等前置概念,给出了严谨的数学定义,还提供了完整的Python计算示例,使用的是跨国数据。最后,作者总结了马氏距离的优点(如排除相关干扰、满足距离公理)和一个潜在缺点(可能夸大微小变化变量的作用)。 从理论概念、直观图解到代码实践,这篇文章为理解这个重要的相似度度量工具提供了一个相当完整的入口。

IT 2018-07-04 23:44:26 / 累计浏览 2,232

相似度计算之切比雪夫距离

这篇讲的是相似度计算中的切比雪夫距离,文章从国际象棋中国王的走法这个生动例子切入,解释了这种距离也被称为“棋盘距离”的由来——即两点间坐标差的最大值。 作者从二维平面的定义出发,将其推广到n维向量空间,并点明了它与闵可夫斯基距离的深层联系:切比雪夫距离是p趋向无穷大时的闵可夫斯基距离。文章还提供了清晰的Python实现代码,方便读者直接上手应用。 尤为精彩的是后半部分对切比雪夫距离与曼哈顿距离的对比。两者定义看似不同,但作者通过几何直观展示了它们的相互转化关系:将代表曼哈顿距离的正方形旋转45度并缩放,即可得到代表切比雪夫距离的正方形。这种视角揭示了不同距离度量在本质上的内在关联,有助于读者更灵活地选择和使用合适的距离计算方法。

IT 2018-07-04 10:46:03 / 累计浏览 3,326

密度聚类算法之OPTICS

这篇讲的是密度聚类算法OPTICS。它出发点是为了解决经典DBSCAN算法对邻域半径Eps和最小点数minPts这两个参数过于敏感的痛点。OPTICS作为DBSCAN的扩展,核心优势在于让聚类过程对半径参数Eps不再敏感,只需设定好minPts,轻微的Eps变化就不会干扰最终的聚类结构。 为了达成这一点,文章解释了两个关键新定义:核心距离和可达距离。核心距离是一个点成为核心对象所需的最小半径;可达距离则结合了核心距离,决定了点在排序中的位置。算法并不直接输出簇,而是通过维护“有序队列”和“结果队列”,生成一个基于可达距离的样本点排序。这个排序信息非常丰富,从它可以推导出在不同参数设置下DBSCAN的聚类结果。 最终,我们可以将这个排序可视化:以输出次序为横轴,可达距离为纵轴绘图。图中的“山谷”代表簇,谷越深簇越紧密;平坦区域或凸起则可能对应噪声。通过设定一个距离阈值切割这个图,就能灵活提取出聚类结构。文章最后还提及了OPTICS在异常检测、子空间聚类等方向的扩展算法。

IT 2018-06-28 12:09:18 / 累计浏览 3,259

聚类算法之ISODATA

聚类算法中的K-Means虽然经典,但需要预先设定簇数K且对初始中心敏感。这篇讲的是ISODATA算法,它作为一种迭代自组织数据分析方法,核心改进在于让聚类过程能够动态调整簇的数量。 文章指出,ISODATA在K-Means基础上引入了“合并”与“分裂”两个关键操作:当两个簇中心过于接近时进行合并,而当一个簇内部样本过于分散或数量过多时则尝试将其分裂。算法需要用户提供几个关键参数,如预期的初始簇数、允许的最小样本数、方差阈值等,这些参数共同划定了簇数量最终可能变化的范围(通常在初始设定值的半倍到两倍之间)。 作者也点明了ISODATA的一个现实困境:虽然原理直观地解决了“K值设定”难题,但由于需要调整的参数较多,且部分阈值难以准确指定,这使得它在实际应用中反而不如更简单的K-Means受欢迎。文章通过对比K-Means,清晰阐述了ISODATA的机制与适用边界。