T9拨号盘搜索和排序算法

T9拨号盘搜索和排序算法


一. 背景

今日头条为何能突破bat的壁垒,很大程度在于它精确的推荐算法, 能够根据用户的喜爱推荐适合用户的资讯,不断根据用户的浏览记录构建用户的偏好生态圈,进而精准投放流量。

大家平常拨打电话应该都有用T9拨号盘吧,输入几个数字后一般会把当前通讯录匹配中的联系人显示出来并高亮显示命中的字符,根据输入的数字串显示哪些联系人数据以及这些联系人数据如果排序显得十分重要,站在用户角度,尽可能的少输入数字并能把用户想拨打的联系人优先显示到前列,这里面涉及的算法就尤为重要了,在此感谢产品mm对权重分配的打磨。

二. T9匹配和排序思路

  1. 加载本地通讯录,本地通讯录作为数据源;
  2. 获取本地通话记录,通话记录作为其中一项影响因子;
  3. 本地联系人的名字转化为T9键盘上对应的数字,根据输入的数字串进行匹配;
  4. 根据首字母匹配、全拼匹配、号码匹配三种类型确认匹配的数据源;
  5. 根据各项影响因子分别计算对应的权重,最终累加权重作为联系人排序的依据。

三. 联系人实体类

拿名字曾轶可 拨号盘输入95举例,注意曾是多音字


t9.png

联系人SimpleContact

字段 类型 描述
mName 曾轶可 String 名字
mNumber 13912345678 String 号码
isFirstMatch true boolean 是否首字母匹配
firstCharactorCounts 2 int 拼音匹配到多少汉字
numberMatchId -1 int 号码匹配的下标
pinyinMatchId (10,13) List<Integer> 关键字在全拼中的下标
startIndex 1 int keyword在contact首字母拼接字符串的起始位置
searchType 1 int 搜索类型( 拼音SEARCH_TYPE_PINYIN1,号码SEARCH_TYPE_NUMBER2)
PinYin PinYin 拼音类
simplePinyinIndex 0 int 记录用的是哪个首字母拼接字符串
hightLighter 12 List<Integer> 高亮的字符位置
mSearchWeight 0 float 权重
spaceIndexList null List<Integer> 名字中空格的下标

拼音PinYin

字段 类型 描述
firstMatchId 0 10 13 List<Integer> 首字母在全拼中的下标
mNormalPinyin zeng_ceng yi ke String words每个汉字的拼音拼接,并且每个汉字拼音之间用空格隔开
mT9Pinyin 9364_2364 94 53 String 和normalPinyin对应,转化为T9上数字
nameArray zeng_ceng yi ke String[] words中每个汉字的拼音
normalPinyinFirstList zyk cyk List<String> 首字母
simplePinyin zyk String words拼音首字母的拼接
t9PinyinFirstList 995 295 List<String> 首字母对应的t9数字

四. 匹配数据源逻辑

/** 
* 根据关键字,返回搜索列表。 
* @param queryString  当前的搜索关键字 
* @param preInput    上一次的搜索关键字 
* @param searchCache  上一次的搜索结果列表 
* @param contactList   总搜索数据源列表
/
public static List<SimpleContact> searchT9StringInContacts(String queryString, String preInput, 
List<SimpleContact> searchCache,                                                           ArrayList<SimpleContact> contactList)

以首字母-全拼-号码依次优先级匹配:

  1. 首字母
    根据首字母在全拼中的下标firstMatchId字段匹配当前输入数字串,若前者包含后者,则首字母命中,获取首字母匹配个数和数字串在全拼中的下标,设为拼音类型。
    如曾轶可(zengyike),首字母完全匹配995(zyk)、首字母非完全匹配(9、5、99、95等)
  2. 全拼
    若1不符合,根据mT9Pinyin(全拼音转化为t9数字)匹配输入数字串,若前者包含后者,则全拼命中,获取数字串在全拼中的下标,设为拼音类型。
    如曾轶可(zengyike),全拼完全匹配9364 94 53、全拼非完全匹配(936、93649、945、53等),全拼匹配必须连续,且以字的首字母开头,364就不符合规则
  3. 号码
    若1和2不符合,根据mNumber(号码)匹配输入数字串,若前者包含后者,则号码命中,获取数字串在号码中的起始匹配位置,设为号码类型。
    如曾轶可的号码为13912345678,只要是该号码的字串即可(39、123、5678等)。

高亮规则
不管是首字母、全拼还是号码匹配,都必须是连续的,命中首字母则对应的汉字高亮,匹配到几个则几个高亮。

五. 排序算法

整体的排序优先级是根据各项影响因子来累积权值,根据最终权重大小排个高低。
影响因子有:

  • 联系人匹配类型(首字母匹配、全拼匹配、号码匹配)
  • 完全匹配、非完全匹配
  • 关键字的首字母在源数据的起始配置
  • 名字长度
  • 通话记录
  • 名字高亮数量
  • T9上的字母顺序
    根据各因子的权值、系数来控制算法的合理性
private static  int FirstMatch= 20;//首字母匹配初始值
private static  int AllFirstMatch=10000;//首字母全匹配
private static  int charMatch=15;//拼音匹配初始值
private static  int AllCharMatch=10000;//拼音完全匹配
private static  int numberMatch=10;//号码匹配初始值
private static  int AllNumberMatch=10000;//号码完全匹配
private static int NotAllCharMatchIni=600;//拼音字母非完全匹配初始值
private static int NotAllNumMatchIni=0;//号码非完全匹配初始值
private static  int Charorder=2;//字母顺序系数
private static  int StartIndexweight=1;//首字母位置系数
private static  float FirstCharactorCounts=0.5f;//首字母数量系数
private static  float NameCounts=0.05f;//名字数量系数
private static  int Fristchar=65;//第一位系数
private static  int Secondchar=15;//第二位系数
private static  int Thirdchar= 10;//第三位系数
private static  int CalllogNum=100;//通话纪录初始值
private static  int MaxCallLogNum=550;//通话记录最大数
private static int CalllogNumweight=1;//通话记录系数
private static int CharAsciiMax=127;//字母顺序最大值


详细算法和各因子权值公式

T9排序算法 .png

六. 该权值分配下的算法详述

根据权值分配的不同可实现不同的产品场景

1. 完全匹配

完全匹配的搜索结果一定高于非完全匹配的搜索结果。通话记录、名字高亮数量、字母顺序等,都不能使得非完全匹配的结果高于完全匹配结果。

在输入同样数字,符合完全匹配的情况下:首字母完全匹配>拼音完全匹配>号码完全匹配。

同是完全匹配情况下,则按照以下逻辑进行排序。

  • 通话记录说明:

如数字426,对应源数据“黄安墨”(首字母完全匹配)、“汉”(拼音完全匹配)、426(号码完全匹配),默认排序位置:黄安墨>汉>426(号码),在无通话记录影响下,黄安墨(10000+20+0.5*3)>汉(10000+15+0.5)>426(10000+10)。

首字母完全匹配、拼音完全匹配、号码完全匹配,其中一个类型只要拥有通话记录,则一定会超越剩下两种无通话记录的类型。其余影响因素如首字母匹配数量、字母系数等,都无法帮助其超越。

如“汉”拥有了一条呼出记录,则汉分值调整为(10000+15+100+15+0.5),最新排名调整为汉>黄安墨>426。

在同样都拥有相同数量的通话记录情况下,首字母完全匹配+呼出/呼入1条>拼音完全匹配+呼出/呼入1条>号码完全匹配+呼入/呼出1条。

如黄安墨呼入1条分值为(10000+20+100+101+0.53)、汉呼出1条(10000+15+100+15*1+0.5)。则排名为黄安墨>汉。

  • 首字母匹配数量说明:

    首字母匹配数量,在无通话记录影响,无字母系数、无完全匹配类型(拼音完全匹配&姓名完全匹配&号码完全匹配)影响,其数值表现比较明显。

如数字482,对应源数据“胡啊”(拼音完全匹配)、“花”(拼音完全匹配)、482(号码完全匹配),默认排序位置:胡啊>花>482。

计算其首字母匹配数量,胡啊命中2个首字母;花仅命中名字的一个首字母;482手机号码则不命中首字母数量,得分排名为胡啊(10000+15+2*0.5)>花(10000+15+0.5)>482(10000+10=10010)

  • 字母顺序说明:

首字母系数数值,在无通话记录影响、无首字母匹配数量、无完全匹配类型(拼音完全匹配&姓名完全匹配&号码完全匹配)影响,其数值表现比较明显。

如输入“45”,对应源数据“胡凯”和“郭磊”,均属于首字母完全匹配。由于胡凯的“胡”占数字键盘4的第二位,郭磊的“郭”占数字键盘4的第一位,在字母系数权重处,郭磊比胡凯会多两分。且由于其余影响因素数值相同,则排位为郭磊>胡凯。

首字母匹配数量数值要低于字母顺序。如输入“482”,对应源数据“胡啊”“瓜”,则胡啊分数为10000+15+0.5×2=10016,瓜分数10000+15+0.5×1+2(G比H多2)=10018,则排名为瓜>胡啊。

2. 非完全匹配
完全匹配的搜索结果一定高于非完全匹配的搜索结果。通话记录、首字母匹配数量、字母顺序数值累加,都不能使得非完全匹配的结果高于完全匹配结果。

非完全匹配情况下,默认姓名非完全匹配的初始值(600分)要大于号码的非完全匹配。即无论“号码非完全匹配”的其他因子权重如何相加,都无法超越“姓名非完全匹配。”

若用户以数字作为姓名,纯数字姓名,则以首字母匹配进行搜索;若是汉字+数字,则根据用户输入的拨号盘数字,对应的是拼音搜索还是首字母搜索,来加对应分值。如源数据“盈盈23”,若用户输入992,则属于首字母不完全匹配搜索,若用户输入“946494642”则属于拼音不完全匹配搜索。

  • 关键字的首字母命中源数据的位置。

在无通话记录影响情况下,输入同样数字,数字命中源数据位置1、2位分值,影响其排序。如输入数字“99”,对应源数据“郭盈盈”、“盈盈家”,由于第1个数字9命中郭盈盈名字的第二个首字母,命中“盈盈家”名字的第一个首字母,郭盈盈得分(600+15+20=635),盈盈家得分(600+65+20=685)。则排名“盈盈家”>“郭盈盈”。

  • 通话记录说明:

在同等源数据位置情况下,首字母非完全匹配、拼音非完全匹配,其中一个类型只要拥有通话记录,则一定会超越剩下剩下无通话记录的类型。其余影响因素如首字母匹配数量、字母系数等,都无法帮助其超越。

如数字26,对应源数据“阿宁家”(首字母非完全匹配)、“安家乐”(拼音非完全匹配),则“阿宁家”得分(600+65+20+0.52);”安家乐”得分(600+65+15++0.7+0.51)。

在同等源数据位置情况下,在同样都拥有相同数量的通话记录情况下,首字母非完全匹配+呼出/呼入n条>拼音非完全匹配+呼出/呼入n条。
在不同源数据位置情况下,首字母非完全匹配、拼音非完全匹配,则一定会超越剩下无通话记录的类型。其余影响因素如首字母匹配数量、字母系数等,都无法帮助其超越。
在不同源数据位置且都拥有通话记录的情况下,属于命中源数据位置后几位的源数据,需要比前面位置的源数据,多几条通话记录才能超越前面位置的源数据位置。

如99对应源数据“郭盈盈”“盈盈家”。若“盈盈家”呼出1条,“郭盈盈”呼出5条,则“盈盈家”得分(600+65+20+100+151+0.52);郭盈盈得分(600+15+20+100+155+0.52),此时排名顺序:郭盈盈>盈盈家。

  • 拼音不完全匹配的字符长度:

拼音非完全匹配的字符长度,只有在其他因素不变的情况下,数值变化比较明显。
如4对应源数据“个杀个脚后跟”,“郭烧开后风给好得更快了”。则在其他数值相同的情况下,由于前者名称更短,则前者排名更高。

  • 字母匹配数量说明:

首字母匹配数量,在无匹配命中源数据位置,无通话记录影响,无字母系数,无完全匹配类型(拼音完全匹配&姓名完全匹配&号码完全匹配)等因素影响,其数值表现比较明显。

如数字482,对应源数据“胡啊美”(拼音不完全匹配)、“花朵儿”(拼音不完全匹配)、4820(号码不完全匹配)。则胡啊美的分值(600+65+15+0.7+0.52),花朵儿分值(600+65+15+0.7+0.51),4820(20+10分)。排名为:胡啊美>花朵儿>4820

  • 字母顺序说明:

首字母系数数值,在无通话记录影响、无首字母匹配数量、无完全匹配类型(拼音
完全匹配&姓名完全匹配&号码完全匹配)影响,其数值表现比较明显。

如输入“45”,对应源数据“胡凯和”和“郭磊和”,均属于首字母非完全匹配。由于胡
和的“胡”占数字键盘4的第二位,郭磊和的“郭”占数字键盘4的第一位,在字母系数权重处,郭磊和比胡凯和会多两分。且由于其余影响因素数值相同,则排位为郭磊和>胡凯和。

首字母匹配数量数值要低于字母顺序。

如输入“45”,对应源数据“郭梅和”“胡磊和”,由于同在数字键盘4的G比H多2分,而郭梅和仅命中1个首字母,胡磊和命中2个首字母,则郭梅和分数为600+65+20+0.5×1+2(G比H多2),胡磊和分数为600+65+20+0.5×2,则排名为郭梅和>胡磊。

七. T9Demo工程

话不多说,直接上工程,包括T9搜索和排序相关的实体和操作类以及一个简陋的界面。


项目图.png

工程链接:
https://github.com/tslearner/T9demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343