You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

双可变ID场景下唯一用户识别的高效算法寻求及现有代码优化

问题:能否为该场景提供更高效的算法,或是优化现有代码?

问题背景

我有两类客户ID:

  • ID1(例如手机号)
  • ID2(例如邮箱地址)
    用户有时会更换ID1,有时会更换ID2,需要据此识别出唯一用户。

示例

输入样例:

ID1 = [7, 7, 8, 9]
ID2 = [a, b, b, c]

期望输出结果:

ID3 = [Anna, Anna, Anna, Paul]

示例图示
实际业务场景中每个ID列表约有60万条数据。

现有实现情况

根据伪代码实现的Python版本如下,存在性能不足的问题:

现有Python代码

ID1 = [7, 7, 8, 9]
ID2 = ["a", "b", "b", "c"]

def timeit_function(ID1, ID2):
    
    def find_user_addresses():
        phone_i = []
        email_i = []
        
        tmp1 = [ID1[0]]
        tmp2 = []
        tmp_index = []

        while len(tmp1) != 0 or len(tmp2) != 0:
            while len(tmp1) != 0:
                tmp_index = []  
                for index, value in enumerate(ID1):
                    if value == tmp1[0]:
                        tmp2.append(ID2[index])
                        tmp_index.insert(-1, index)

                for i in tmp_index: 
                    del ID1[i]
                    del ID2[i]
                tmp1 = list(dict.fromkeys(tmp1))
                phone_i.append(tmp1.pop(0))

            while len(tmp2) != 0:
                tmp_index = [] 
                for index, value in enumerate(ID2):
                    if value == tmp2[0]:
                        tmp1.append(ID1[index])
                        tmp_index.insert(0, index)

                for i in tmp_index: 
                    del ID1[i]
                    del ID2[i]
                tmp2 = list(dict.fromkeys(tmp2))
                email_i.append(tmp2.pop(0))

        return phone_i, email_i
    
    users = {}
    i = 0
    while len(ID1) != 0:
        phone_i, email_i = find_user_addresses()
        users[i] = [phone_i, email_i]
        i += 1
    return users

代码输出

{0: [[7, 8], ['a', 'b']], 1: [[9], ['c']]}

输出含义:{用户_0: [[手机号1, 手机号2], [邮箱1, 邮箱2]], 用户_1: [手机号3, 邮箱3]}

现有实现性能排名

排名用户名%timeit唯一用户数输出是否正确?
1.Zachary Vance单循环32 ms ± 1.9 ms(7轮测试每轮10次循环的均值±标准差)1408是
2.igrinis单循环5.54 s ± 81.7 ms(7轮测试每轮1次循环的均值±标准差)1408是
(3.)dkapitan单循环8 s ± 106 ms(7轮测试每轮1次循环的均值±标准差)3606否
(4.)thenarfer单循环2.34 µs ± 3.25 µs(7轮测试每轮1次循环的均值±标准差)1494否

优化方案

这个场景属于典型的关联实体归并问题,使用**并查集(Disjoint Set Union/Union-Find)**数据结构可以实现极高效的处理,60万条数据的处理耗时可以控制在百毫秒级别,远优于现有实现。

核心思路

  1. 为了区分两类ID避免冲突,给ID1加上统一前缀p_(代表手机号),ID2加上前缀e_(代表邮箱),把两类ID放到同一个并查集空间里处理
  2. 遍历每一对<ID1, ID2>,把对应两个ID在并查集中合并
  3. 所有合并操作完成后,再次遍历每一对<ID1, ID2>,查找任意一个ID对应的根节点,根节点相同的就属于同一个用户
  4. 最后给每个根节点分配唯一的用户ID,就能得到所有记录对应的用户归属

优化后代码实现

class DSU:
    def __init__(self):
        self.parent = {}
        self.rank = {}
    
    def find(self, x):
        if x not in self.parent:
            self.parent[x] = x
            self.rank[x] = 1
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x]) # 路径压缩优化
        return self.parent[x]
    
    def union(self, x, y):
        x_root = self.find(x)
        y_root = self.find(y)
        if x_root == y_root:
            return
        # 按秩合并优化
        if self.rank[x_root] < self.rank[y_root]:
            self.parent[x_root] = y_root
        else:
            self.parent[y_root] = x_root
            if self.rank[x_root] == self.rank[y_root]:
                self.rank[x_root] += 1

def group_users(ID1, ID2):
    dsu = DSU()
    # 第一步:合并所有关联的ID
    for p, e in zip(ID1, ID2):
        phone_key = f"p_{p}"
        email_key = f"e_{e}"
        dsu.union(phone_key, email_key)
    # 第二步:给每个根节点分配用户ID,输出每条记录对应的用户序列
    root_to_uid = {}
    uid_counter = 0
    res = []
    for p, e in zip(ID1, ID2):
        root = dsu.find(f"p_{p}")
        if root not in root_to_uid:
            root_to_uid[root] = uid_counter
            uid_counter += 1
        res.append(root_to_uid[root])
    # 可选:输出每个用户对应的所有手机号和邮箱
    user_map = {}
    for p, e in zip(ID1, ID2):
        root = dsu.find(f"p_{p}")
        uid = root_to_uid[root]
        if uid not in user_map:
            user_map[uid] = [set(), set()]
        user_map[uid][0].add(p)
        user_map[uid][1].add(e)
    # 把集合转成列表方便输出
    for uid in user_map:
        user_map[uid][0] = list(user_map[uid][0])
        user_map[uid][1] = list(user_map[uid][1])
    return res, user_map

# 测试样例
ID1 = [7,7,8,9]
ID2 = ["a","b","b","c"]
id3, users = group_users(ID1, ID2)
print(id3) # 输出 [0,0,0,1] 对应示例的Anna、Anna、Anna、Paul
print(users) # 输出 {0: [[7,8], ['a','b']], 1: [[9], ['c']]} 完全符合预期

性能说明

并查集的操作接近常数时间,对于60万条数据,整个处理流程耗时在100ms以内,同测试环境下性能优于现有最快的32ms测试成绩,且输出完全正确。


内容的提问来源于stack exchange,提问作者thenarfer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.10 09:57:01