嵌套Rho算法高效寻针

内存高效的算法用于大海捞针

摘要

在密码学和密码分析中,最常见的任务之一是在一个指数级大的集合(干草堆)中找到某个有趣的事件(针),或者证明此类事件很可能不存在。特别是,我们感兴趣的是寻找那些定义为在几乎均匀分布于 N 个可能事件的干草堆中以异常高的概率 $ p \gg 1/N $ 发生的针。当搜索算法只能从此分布中采样值时,目前寻找此类事件所需的最佳已知时间/内存权衡为 $ O(1/Mp^2) $ 时间,给定 $ O(M) $ 内存。

在本文中,我们开发了在常见密码学场景下更快的寻找“针”的算法,该场景中的分布是通过对随机输入应用某个确定性函数 $ f $ 来定义的。这种分布可以用一个具有 $ N $ 个顶点的随机有向图进行建模,其中几乎所有顶点都有 $ O(1) $ 个前驱节点,而我们要寻找的目标顶点则具有异常多的 $ O(pN) $ 个前驱节点。当仅提供恒定量的内存时,我们提出了一种新的搜索方法,称为嵌套 Rho。随着 $ p $ 的增加,此类随机图会经历多个微妙的相变,因此时间复杂度 $ T $ 与 $ p $ 之间的对数-对数依赖关系变成一条分段线性的曲线,共发生四次弯曲。我们的新算法在整个 $ 1/N < p < 1 $ 范围内均优于此前最优算法的 $ O(1/p^2) $ 时间复杂度,特别是对于任意位于 $ N^{-0.75} < p < N^{-0.5} $ 范围内的 $ p $,时间复杂度显著降低了 $ \sqrt{N} $ 倍。当提供更多内存时,我们展示了如何将嵌套 Rho 技术与并行碰撞搜索技术相结合,以进一步降低其时间复杂度。最后,我们展示了如何将我们的新搜索技术应用于复杂分布。

关键词

密码分析 · Needles 在大海捞针中 · Mode 检测 · Rho 算法 · 并行碰撞搜索

1 引言

我们在构建和分析密码方案时所做的几乎所有事情都可以看作是在大海捞针:在所有可能的密钥中识别正确密钥,在哈希函数中寻找原像,查找流密码输出中的偏差,确定分组密码的最佳差分和线性特性,因数分解算法中搜寻光滑数等。作为密码分析者,我们的目标是使用最高效的算法找到这些针;而作为设计者,我们的目标是确保这些针要么不存在,要么极难被找到。

根据什么特征将针与其他干草元素区分开来,针可以有多种不同的定义方式。在密码学中,许多类型针的一个共同特征是,它们是概率最高的 $ p $ 概率事件,在干草堆中所有 $ N = 2^n $ 可能事件中最高。这样的元素称为分布的众数,为简单起见,我们首先考虑分布几乎平坦的情况:一个单峰的概率为 $ p \gg 1/N $,而其他所有事件的概率约为 $ 1/N $(如图1所示)。之后我们将考虑更一般的情况,即分布中有多个高度不同的峰值,我们需要找到所有这些峰值。

本文的目标是分析这一概率性寻找针问题的复杂度,假设干草堆分布以黑箱形式给出。通过忽略任务的细节并专注于其本质,我们使所提出的技术能够适用于各种不同情况。另一方面,在这种一般化形式下,我们无法使用特定的优化技巧来提高对某些特定类型针的搜索效率。

我们将致力于优化搜索算法的时间复杂度和内存复杂度。由于随机存取存储器通常比时间昂贵得多,因此我们主要关注内存高效的算法:我们首先分析仅能使用恒定量的内存的算法所能达到的最佳时间复杂度,然后研究通过使用一些额外内存如何降低时间复杂度。

本文的结构如下:第2节形式化描述我们的计算模型。第3节介绍此前已知的最佳民间算法用于解决该问题的方法。接着,我们展示如何使用标准的碰撞检测算法来识别众数,当其概率在第 $ p $ 足够大时,见第4节。随后,我们在第5节中,通过引入新的 2Rho 算法,该算法在原始分布上运行另一个碰撞检测算法得到放大的众数概率,并在此基础上使用碰撞检测算法。随后,在第6节中,通过在碰撞检测算法中进行更深层次的嵌套,将该算法推广为一般的 iRho。我们在第7节中讨论时间-内存权衡问题,并在第8节中探讨当概率分布具有多个峰值且我们希望找到所有峰值时所需进行的调整。第9节总结了本文内容。

2 问题陈述和模型描述

我们问题最简单的概念模型是:采样黑盒上有一个按钮,每次按下按钮时,我们花费一个时间单位,并从分布中获得一个新选择的事件。因此,我们可以通过统计在 $ O(1/p) $ 次试验中该 $ y $ 从分布中被采样的次数,来检验某个特定的 $ y $ 是否为众数 $ y_0 $。注意,当我们只有一个可用计数器时,必须对每个候选 $ y $ 分别运行此算法。最简单的算法依次尝试所有 $ N $ 个可能的候选,但我们也可以利用给定的分布,以更优的方式选择下一个待测试的候选。由于正确候选以更高的概率 $ 1/p $ 被提出,时间复杂度从 $ N/p $ 降低到 $ 1/p^2 $。当我们有 $ M $ 个可用计数器时,只要满足 $ 1/p \geq M $,就可以通过同时用相同数量的样本测试 $ M $ 个候选值来实现线性加速。这种简单直接的方法产生了目前已知的、用于寻找单峰平坦分布众数的最佳算法。

然而,仔细分析该问题后发现,在大多数密码学应用中,我们希望分析的分布实际上是由某个确定性函数 $ f $ 生成的,其输入是从均匀概率分布中随机选取的。例如,当我们寻找前 $ n $ 个流密码的输出比特,我们选择一个随机密钥,将其应用于确定性比特生成器,并通过以下方式定义(可能非均匀的)输出分布:某个特定比特串如果在 $ N $ 个可能的密钥中有 $ i $ 个密钥的输出字符串中作为前缀出现,则该比特串的概率为 $ i/N $。类似地,当我们寻找高概率的差分特性时,我们选择具有特定输入差分的随机明文对,并在某个固定密钥下进行确定性加密。此过程通过统计每个输出差分出现的次数来生成分布,该分布的众数表明了使用所选输入差分时分组密码的最佳差分特征。

在这种情况下,我们将黑盒中的按钮替换为一个可以接受 $ N $ 种可能值的输入。盒子本身变为确定性的,我们通过向盒子提供随机选择的输入值来对分布进行采样。两种模型之间的主要区别在于:当我们反复按下按钮时,得到的是相互无关的样本;而当我们反复提供相同的输入值时,总是得到相同的输出值。正如本文所示,这一微小差异导致了令人惊讶的新型众数查找算法,其复杂度远优于上述平凡算法。

由函数定义的从输入到输出的映射 $ f $ 可以看作是一个随机有向二分图,如图2所示,其中一个顶点具有较大的入度。为简便起见,我们假设函数 $ f $ 具有相同数量的可能输入和输出 $ N $,然后可以将名称相同的输入和输出顶点合并,从而得到一个包含 $ N $ 个顶点的随机单后继图的标准模型。当我们在该图中迭代应用函数 $ f $ 时,会沿着一条 Rho 形路径行进,该路径以尾部开始,随后进入一个循环。该图包含少量不相交的循环,图中所有其他顶点则以树的形式悬挂在这些循环周围。

随着概率 $ p $ 从 $ 1/N $ 增加到 1,其中一个顶点 $ y_0 $ 作为目标变得越来越流行,图的性质也随之改变。例如,容易证明当 $ p $ 越过 $ O(1/\sqrt{N}) $ 这一阈值时,会出现突然的相变,此时 $ y_0 $ 预期会从一棵树进入某个环中(从而更容易定位),并且其环的预期长度开始缩短(而在此之前始终不变)。正如我们在论文后面所展示的,当 $ p $ 越过若干更早的阈值时,会发生更微妙的相变,因此我们众数搜索算法的对数-对数复杂度成为一个分段线性函数,在这些阈值处多次发生弯曲,如图5中的实线所示。与表示先前最佳 $ 1/p^2 $ 复杂度的虚线相比,我们在所有可能的 $ p $ 值范围内都取得了显著改进。

2.1 符号与约定

符号说明1。 集合 ${1, 2, …, N}$ 表示为 $[N]$。在本文中,所有函数都是从集合 $[N]$ 到其自身的映射。众数是指函数值域中具有最多原像的值。

问题设定: 我们研究的基本问题是以下内容。给定一个值 $ 0 < p < 1 $ 和一个函数 $ f: [N] \to [N] $,该函数由以下三个步骤生成:
1. 随机均匀地选择 $ y_0 \in [N] $。
2. 在所有大小为 $ pN $ 的子集中随机均匀地选择一个子集 $ S \subset [N] $。对所有 $ x \in S $ 设置 $ f(x) = y_0 $。
3. 对每个 $ x \notin S $,随机均匀地选择 $ f(x) \in [N] \setminus {y_0} $。

根据定义,$ f $ 在 $[N] \setminus S$ 上的取值可由一个返回 $[N] \setminus {y_0}$ 中值的真正随机预言机模拟。我们最初的目标是检测 $ y_0 $,并使用仅需 $ O(1) $ 个内存单元的最快可能算法。我们可以假设攻击者知道 $ p $,因为否则他可以运行一个简单的搜索算法,采用几何递减序列的概率(例如,$1, 1/2, …, 1/2^i, …$),以找到使他的攻击成功的 $ p $ 的最大值(或者当攻击变得过于昂贵时停止,从而得到 $ y_0 $ 概率的上界,但无法确定其确切值)。

3 简单的无内存算法

在本节中,我们正式提出针对不同 $ p $ 值检测众数的最简单无记忆算法。这些算法基于对随机点进行采样,然后检查它们是否确实为所需的众数。

3.1 无内存模式验证算法

我们通过介绍一个众数验证算法来开始讨论。该算法接受一个候选值 $ y $,并检查它是否为众数 $ y_0 $。检查过程是通过选择 $ O(1/p) $ 个随机值,并验证其中是否有足够多的值在函数 $ f $ 下被映射到 $ y $。该算法在算法1中给出。

算法1. 模式验证:确定给定的 y 是否为 y0

初始化一个计数器 ctr ← 0。
对于 i = 1 到 c/p 执行
    随机选取 x ∈ [N],并计算 y′ = f(x)。
    如果 y′ = y0 那么
        递增 ctr。
    结束 if
结束 for
如果 ctr ≥ t 则
    打印 y 是 y0。
结束 if

容易看出,算法1向 $ f $ 发出 $ c/p $(·)次查询。其成功率取决于所选择的常数 $ c $ 和 $ t $。假设确实 $ y $ 是 $ y_0 $,我们期望所选的 $ x $ 导致 $ y $ 的次数服从均值为 $ c $ 的泊松分布(否则,该分布服从均值为 $ c/Np $ 的泊松分布)。因此,对于任意期望的成功率,都可以轻松选择 $ c $ 和阈值 $ t $。例如,设置 $ c = 4 $ 和 $ t = 2 $ 可获得约90.8%的成功率。

3.2 无内存采样算法

算法2中提出的采样算法基于随机选择一个值 $ x $,计算验证算法的候选值 $ y = f(x) $,并验证 $ y $ 是否确实是正确的 $ y_0 $。容易看出,该算法预期会探测 $ O(p^{-1}) $ 个 $ y $ 的值,直到遇到 $ y_0 $ 为止,且每次验证耗时 $ O(p^{-1}) $,从而得到运行时间为 $ O(p^{-2}) $。

算法2. 通过采样寻找 y0:

while y0 未被找到 do
    随机选取 x ∈ [N]。
    计算 y = f(x)。
    调用算法1检查 y。
end while

4 使用基于 Rho 的碰撞检测算法

我们现在提出一类不同的用于检测众数的算法,这些算法结合了碰撞检测算法和简单的众数验证算法(算法1)。这些算法(例如弗洛伊德的 [7] 或其变体 [2,8])

从某个随机点 $ x $ 开始,迭代地应用 $ f $,即生成序列 $ x, f(x), f^2(x) = f(f(x)), f^3(x), … $,直到检测到重复。后续我们将这类算法称为“Rho 算法”。我们将序列 $ x, f(x), f^2(x), … $ 中第一个重复的值记为 $ f^\mu(x) $,其第二次出现记为 $ f^{\mu+\lambda}(x) $,并将这个共同的值称为环的入口点。

当 $ p \gg N^{-1/2} $ 时的最优检测。 首先,我们证明当 $ p \gg N^{-1/2} $ 时,众数 $ y_0 $ 可以在时间 $ O(1/p) $ 内找到。该复杂度显然是最优的:如果对 $ f $ 的查询次数为 $ o(1/p) $,那么以极大概率不会查询到 $ y_0 $ 的原像,因此无法检测到 $ y_0 $。

其思路很简单:我们运行一个 Rho 算法,以任意随机起点 $ x $ 和某个小常数的序列长度上界 $ c/p $ 作为参数 $ c $。对于这样的长度,我们期望众数 $ y_0 $ 在序列中以高概率出现两次,而由于 $ c/p < \sqrt{N} $,Rho 所发现的碰撞不期望是其他随机值之一。我们在附录 A 中给出了该算法的完整分析。

通过使用无内存的 Rho 算法,我们得到的时间复杂度为 $ O(1/p) $,内存复杂度为 $ O(1) $。与往常一样,可以通过使用其他起始点重复该算法,并使用平凡众数验证算法在时间 $ O(1/p) $ 内检查每个建议的点,进一步提高检测到 $ y_0 $ 的概率。

重复 Rho 算法:任意 $ O(p^{-3}N^{-1}) $ 下的检测。 上述方法可用于任意的值。然而,当 $ p < 1/\sqrt{N} $ 时,Rho(即环的入口点)的输出确实是 $ y_0 $ 的概率显著下降。具体而言,我们有以下下界,并且可以轻松证明实际值不会明显更大。

命题1。 假设 $ p < 1/\sqrt{N} $,因此 Rho 在检测到碰撞之前会遇到 $ O(\sqrt{N}) $ 个不同的输出值。那么 Rho 输出 $ y_0 $ 的概率为 $ \Omega(p^2N) $。

证明。 由于以 $ y_0 $ 作为输出的概率是关于 $ p $ 的非减函数,因此不妨假设对于较小的 $ c $ 有 $ p = cN^{-1/2} $。在这种情况下,Rho 生成 $ y_0 $ 的概率的下界,等于在序列 $(x, f(x), …, f^t(x))$ 的前 $ \sqrt{N}/2 $ 步中,每个值 $ y’ \neq y_0 $ 至多出现一次,而 $ y_0 $ 恰好出现两次的概率。形式上,令 $ L’ = (x, f(x), …, f^t(x)) $,其中 $ t = \min(\mu + \lambda, \sqrt{N}/2) $。记 $ E_{y’} $ 为事件:“每个 $ y’ \neq y_0 $ 在 $ L’ $ 中至多出现一次”,$ E_{y_0} $ 为事件:“$ y_0 $ 在 $ L’ $ 中出现两次”。则

$$
\Pr[\text{Output(Rho)} = y_0] \geq \Pr[E_{y’} \land E_{y_0}] = \Pr[E_{y’}] \Pr[E_{y_0}|E_{y’}].
$$

正如我们在附录 A 中所示,我们有 $ \Pr[E_{y’}] \geq e^{-1/4} \approx 0.78 $ 和

$$
\Pr[E_{y_0}|E_{y’}] = \Pr[X \geq 2|X \sim \text{Poi}(|L’|p)] \geq \Pr[X \geq 2|X \sim \text{Poi}(\sqrt{N}p/2)].
$$

最后,对于任何足够小的 $ \lambda $,我们有

$$
\Pr[X \geq 2|X \sim \text{Poi}(\lambda)] = 1 - e^{-\lambda}(1 + \lambda) \approx 1 - (1 - \lambda)(1 + \lambda) = \lambda^2,
$$

因此,结合上述不等式可得

$$
\Pr[\text{Output(Rho)} = y_0] \geq 0.78(\sqrt{N}p/2)^2 = 0.19p^2N,
$$

如断言所示。 □

这得到了重复 Rho 算法——一种 $ O(p^{-3}N^{-1}) $ 算法,用于检测众数:运行 Rho 算法 $ O(1/p^2N) $ 次,并在 $ O(1/p) $ 时间内使用众数验证算法检查每个输出点。$ y_0 $ 以常数概率,Rho 的某次调用会提出该值,从而得以验证。由于 $ p^{-3}N^{-1} < p^{-2} $ 对所有 $ p $ 成立,该算法优于采样算法(算法2),后者的运行时间为 $ O(p^{-2}) $。图3展示了不同 $ p $ 值下各算法的比较。

上述分析隐含地假设了所有 Rho 的调用是相互独立的。然而,如果我们对同一函数 $ f $ 应用 Rho,而仅在每次调用中更改起始点 $ x $,则显然并非如此。事实上,由于 $ p < 1/\sqrt{N} $,$ y_0 $ 不太可能位于 $ f $ 的循环上,因此无论我们使用相同的 $ f $ 但不同的起始点运行多少次 Rho 算法,我们都永远不会遇到 $ y_0 $ 作为循环入口点。

为了使 Rho 的调用基本相互独立,我们引入了 $ f $ 的口味(flavors)概念,类似于赫尔曼经典时间-内存权衡攻击 [3] 中使用的口味。更具体地说,我们定义 $ v $ 的 $ f $ 口味为函数 $ f_v(x) = f(x + v) $,其中加法是在模 $ N $ 下计算的。$ f $ 的不同口味具有一些局部性质(例如,它们保持每个 $ y $ 的原像个数不变,因此 $ y_0 $ 仍然是该函数的众数),但具有不同的全局性质(例如,在迭代时,其图的树和环划分完全不同)。特别是,通常将 $ f $ 的各种口味视为互不相关的函数,尽管这在形式上并未得到严格证明。我们将第 $ v $ 次调用 Rho 的输出定义为:从点 $ v $ 开始,使用不同随机选择的口味多次调用 Rho 并运行 RepeatedRho 算法后,进入由 $ f_v $ 定义的环的入口点。

5 2Rho 算法

在本节中,我们介绍 2Rho 算法,并证明在 Rho 算法的结果上运行一个 Rho 算法优于所有先前提出的算法。

新算法背后的主要思想是,单次应用 Rho 可被视为一个引导步骤,用于放大 $ y_0 $ 被采样的概率。实际上,根据命题1,当使用随机选择的变体运行 Rho 时,输出 $ y_0 $ 的概率为 $ \Omega(p^2N) $,并且只要 $ p \gg N^{-1} $,该概率就显著大于单次调用 $ p $ 时 $ y_0 $ 被采样的概率。

请注意,根据对称性,Rho 以随机“味道”返回 $ y $ 所有其他值的概率仍然均匀地保持较低水平。因此,我们面临的是完全相同的寻找针的问题,但在相同位置 $ y_0 $ 处具有放大的概率峰值。特别地,如果这个新的概率峰值超过 $ N^{-0.5} $,我们就可以通过使用一个简单的 Rho 算法来找到它。另一方面,现在单次评估 Rho 比单次评估 $ f $ 更加耗时,因此这种自举方法在操作总数与每次操作的成本之间产生了一个权衡,因此应适当地选择参数,以降低总复杂度。

我们的目标是正式定义新的内层函数 $ g(x) $,该函数将被外层的 Rho 算法使用。此 $ g $ 函数将一种口味 $ v $ 映射到通过在 $ v $ 的 $ f $(即 $ f_v $)的口味上运行 Rho 算法并以初始值 $ v $ 开始所定义的环的入口点。当我们迭代 $ g $ 时,我们从一个口味跳转到一个循环入口点,然后利用该循环入口点的身份来确定下一个口味和起始点。这就在小型 Rho 结构之上构建了一个大型 Rho 结构,如图4所示,其中不同的颜色表示算法中使用的 $ f $ 的不同口味。每条虚线代表我们在切换到新口味时迈出的第一步,仅用于在视觉上区分各个 Rho 结构,使图形更易于理解。需要注意的是,大环中的碰撞发生在我们第二次遇到相同的循环入口点时,但这并不意味着发生碰撞的两个小型 Rho 结构或它们的起始点是相同的,因为它们通常使用不同的口味;只有当我们第二次和第三次遇到相同的循环入口点时,其对应的 Rho 结构才会变得完全相同,此后我们将不断重复经过相同的 Rho 结构。

我们现在将注意力转向一个特定的概率值域 $ p $,在此范围内,2Rho 算法(在内层 Rho 算法之上运行外层 Rho 算法)相较于之前描述的算法具有显著优势。

5.1 值域 $ N^{-3/4} \leq p \leq N^{-1/2} $ 中 2Rho 的分析

假设 $ N^{-3/4} \leq p \leq N^{-1/2} $,并如上所述构造函数 $ g $。定义 $ p’ = \Pr[g(x) = y_0] $,我们已经证明了 $ p’ = \Omega(p^2N) \gg N^{-1/2} $,因此可以使用 2Rho 算法通过 $ O(1/p’) $ 次对 $ g $ 的求值最优地找到 $ g $ 的众数。

为了找到 2Rho 的总复杂度,我们必须计算 $ g $ 每次求值的复杂度,即 Rho 算法一次求值的复杂度。

注意,对于 $ c > 1 $,所有值 $ x, f(x), f^2(x), …, f^{c\sqrt{N}}(x) $ 互不相同的概率至多为

$$
\frac{(N-1)(N-2)\cdots(N-c\sqrt{N})}{(N-1)^{c\sqrt{N}}} \leq \frac{(N-\sqrt{N})(N-\sqrt{N}-1)^{(c-1)\sqrt{N}}}{(N-1)^{c\sqrt{N}}} \leq (N-\sqrt{N}/N)^{(c-1)\sqrt{N}} \approx e^{-(c-1)}.
$$

因此,以极高的概率,Rho 算法在 $ O(\sqrt{N}) $ 次操作中找到一个循环。为了避免这些算法在少数情况下花费更多时间,我们可以稍微修改任何 Rho 算法,通过在预定次数的 $ f $ 评估后停止它(例如,$ 10\sqrt{N} $),在这种情况下,$ g(x) = \text{Rho}(f, x+1) $。无论如何,对 $ g $ 进行一次评估的期望时间复杂度是 $ O(\sqrt{N}) $ 次 $ f $ 的评估。

因此,2Rho 的时间复杂度为 $ O( \frac{1}{p’} \cdot \sqrt{N}) = O(p^{-2}N^{-1/2}) $ 次操作。这明显快于采样算法,也显著快于 RepeatedRho,因为对于所有 $ p < N^{-1/2} $,$ p^{-2}N^{-1/2} < p^{-3}N^{-1} $ 成立。

与 Rho 算法类似,当 $ p < N^{-3/4} $ 时,可重复执行嵌套的 2Rho 算法,从而得到适用于任意 $ p $ 的算法。实际上,重复执行 2Rho 直到找到众数(并通过验证算法进行验证),需要对 $ g $ 进行 $ O(p’^{-3}N^{-1}) = O((p^2N)^{-3}N^{-1}) = O(p^{-6}N^{-4}) $ 次求值,或对 $ f $ 进行 $ O(p^{-6}N^{-3.5}) $ 次求值。因此,对于 $ p > N^{-5/6} $,Repeated2Rho 优于 RepeatedRho 算法,而对于 $ p < N^{-5/6} $ 则较差。

表1描述了我们在值域 $ N^{-0.79} \leq p \leq N^{-0.5} $ 内对不同 $ p $ 值的 2Rho 算法进行的实验验证。我们使用了相对较小的 $ N = 2^{28} $(这使得在 $ p = N^{-0.75} $ 处的过渡比在较大的 $ N $ 情况下更加平缓),并对每个实验使用不同的随机函数 $ f $ 重复进行了100次。

$ p = \Pr[y_0] $ 值 $ \log_N(p) $ 速率 成功
$ 2^{-14} $ -0.5 100 %
$ 2^{-15} $ -0.54 100 %
$ 2^{-16} $ -0.57 100 %
$ 2^{-17} $ -0.61 97%
$ 2^{-18} $ -0.64 91%
$ 2^{-19} $ -0.68 71%
$ 2^{-20} $ -0.71 32%
$ 2^{-21} $ -0.75 8%
$ 2^{-22} $ -0.79 0%

6 Rho 算法的更深嵌套

我们现在展示如何嵌套 iRho 以获得 (i+1)Rho。我们分析了由此产生的复杂度,并表明,尽管对于较小的 $ i $,它能产生更好的结果,当 $ i $ 变得更大时,它的表现不如更简单的算法。特别是,将 NestedRho 算法嵌套最多四次是有利的,但进行第五次嵌套则不然。

3Rho 算法用于 $ N^{-7/8} \leq p \leq N^{-3/4} $

假设 $ N^{-7/8} \leq p \leq N^{-3/4} $,定义一个新函数 $ h(x) $,它将输入类型 $ x $ 映射到由 2Rho 算法定义的环的入口点。如同上述对 2Rho 的分析,我们定义 $ p’’ = \Pr[h(x) = y_0] $,并可证明 $ p’’ = \Omega(p’^2N) = \Omega(p^4N^2N) \gg N^{-1/2} $。因此,使用 Rho 算法可在 $ O(1/p’‘) $ 次对 $ h $ 的求值中最优地找到 $ h $ 的众数。由于每次对 $ h $ 的求值需要 $ O(\sqrt{N}) $ 次对 $ g $ 的求值,而每次对 $ g $ 的求值又需要 $ O(\sqrt{N}) $ 次对 $ f $ 的求值,因此该算法的整体复杂度为 $ O(p^{-4}N^{-3}N) = O(p^{-4}N^{-2}) $ 次对 $ f $ 的求值。我们将此算法称为 3Rho,因为它本质上是对 2Rho 再次增加了一层嵌套。

算法3 (i+1)Rho 针对函数 $ f(\cdot) $ 的算法(基于 iRho)

输入:一个随机输入 x ∈ [N]。
设置 z ← iRho(f_x, x)。  // 请注意,在递归过程中,f的类型会累加。
当未遇到 z 的重复值时执行
    设置 z ← iRho(f_z, z)。
结束循环
找出重复的 z 值。a
返回 z.

可以使用弗洛伊德的算法 [7], 或其任何变体来完成识别。

3Rho 算法的复杂度始终优于 RepeatedRho,并且对于所有 $ p < N^{-3/4} $ 都优于重复 2Rho 算法的 $ O(p^{-6}N^{-3.5}) $ 复杂度。

与 2Rho 的情况类似,3Rho 算法也可以重复进行,形成带模式验证的 Repeated3Rho,从而得到适用于任意 $ p $ 的算法。其产生的复杂度为 $ O(p’‘^{-3}N^{-1}) = O((p^4N^3)^{-3}N^{-1}) = O(p^{-12}N^{-10}) $ 次对 $ h $ 的求值,或 $ O(p^{-12}N^{-9}) $ 次对 $ f $ 的求值。该算法在 $ p > N^{-8/9} $ 时优于 RepeatedRho,在 $ p < N^{-8/9} $ 时较差。然而,对于 $ N^{-9/10} \leq p \leq N^{-7/8} $,我们可以通过再次嵌套 3Rho 来获得更好的结果。

用于 $ N^{-9/10} \leq p \leq N^{-7/8} $ 的 4Rho 算法

假设 $ N^{-15/16} \leq p \leq N^{-7/8} $,并定义一个新的映射 $ \mathcal{D}(x) $,该映射将一个类型 $ x $ 映射到由 3Rho 算法找到的环的入口点。与上述 3Rho 的情况类似,我们有 $ p’‘’ = \Pr[\mathcal{D}(x) = y_0] = \Omega(p’‘^2N) = \Omega(p^8N^6N) \gg N^{-1/2} $。因此,使用 Rho 算法可以在 $ O(1/p’‘’) $ 次 $ \mathcal{D} $ 的评估中最优地找到 $ \mathcal{D} $ 的众数。由于每次计算 $ \mathcal{D} $ 需要对 $ f $ 进行 $ O(N^{1.5}) $ 次求值,因此该算法的整体复杂度是对 $ f $ 进行 $ O(p^{-8}N^{-7}N^{1.5}) = O(p^{-8}N^{-5.5}) $ 次求值。我们将此算法称为 4Rho,因为它执行了四层嵌套的 Rho 算法。

与之前的算法不同,4Rho 并非在整个值域 $ N^{-15/16} \leq p \leq N^{-7/8} $ 内都优于所有先前的算法。事实上,当 $ p \to N^{-15/16} $ 时,

4Rho 方法的复杂度 $ N^2 $,这甚至高于直接的采样算法。特别是,4Rho 只有在重复 Rho 满足 $ p > N^{-0.9} $ 时才更快,这也解释了为什么复杂度曲线在图5的右上角斜率减小。

我们注意到,对于任何 $ p $ 而言,5Rho 的自然扩展显然更差,因为外层 Rho 每一步的复杂度都需要 $ N^2 $ 步,这已经高于采样算法的整体复杂度。

我们所能实现的最佳算法的复杂度(作为 $ p $ 的函数)以数学形式呈现在表2中,以图形形式呈现在图5中。

7 时间-内存权衡

在本节中,我们重新审视检测众数的基本问题,但假设我们有 $ O(M) $ 个内存单元可用。我们的目标是以尽可能高的效率检测众数,其中复杂度表示为参数 $ N $、$ p $ 和 $ M $ 的函数。

在开始之前,我们注意到我们只讨论 $ p < N^{-1/2} $ 的情况,因为我们已经有了一个针对 $ p \geq N^{-1/2} $ 情况的最优无内存算法(如第4节所示)。

我们首先描述 [10] 的基本并行碰撞搜索算法。然后,我们描述一系列算法,这些算法通过并行碰撞搜索扩展了 iRho 无记忆算法。

7.1 并行碰撞搜索

van Oorschot 和 Wiener 提出的并行碰撞搜索(PCS)算法 [10] 是一种内存高效的算法,用于在将 $[N]$ 映射到 $[N]$ 的函数 $ f $ 中以较低的每次碰撞分摊成本寻找多个碰撞。自提出以来,该算法已广泛应用于密码分析(例如,[4–6,9])。

给定 $ M $ 个内存单元,该算法构建一个结构 $ M $ 链,类似于 Hellman 的时间-内存权衡算法中构建的链 [3]。

结构中的链从任意点 $ x $ 开始,通过重复应用 $ f $ 进行计算(即,$ f^i(x) = f(f^{i-1}(x)) $)。链在大约经过 $ \sqrt{N}/M $ 次对 $ f $ 的计算后终止,因此该结构总共包含约 $ M \cdot \sqrt{N}/M = \sqrt{NM} $ 个点。此外,由于 $ \sqrt{N}/M \cdot \sqrt{NM} = N $,根据生日悖论,每条链预计将与结构中的另一条链发生碰撞,因此该链结构包含 $ O(M) $ 次碰撞。为了高效地找到 $ O(M) $ 次碰撞,我们定义一组显著点,并在每条链到达此类点时将其终止。在本例中,我们定义了 $ \sqrt{NM} $ 个显著点(例如,其 $(\log_2(N) + \log_2(M))/2$ 最低有效位为零的点),因此期望的链长度为 $ N/\sqrt{NM} = \sqrt{N}/M $,符合要求。实际的 $ O(M) $ 碰撞点通过排序链的 $ M $ 终止点(即显著点)并为每一对发生碰撞的链重新启动链计算来恢复。为完整起见,我们在附录 B 中给出了 PCS 的伪代码(算法6)。总体而言,该算法在 $ \sqrt{NM} $ 时间内使用 $ O(M) $ 内存找到 $ O(M) $ 次碰撞。

7.2 使用内存的模式验证

基本无记忆众数验证算法(算法1)可以通过利用内存来扩展,以同时检查多个目标。也就是说,给定 $ M $ 个候选 $ y_i $,可以以 $ O(1/p) $ 次对 $ f $ 的查询代价同时检查所有这些候选,如算法4所示。

使用算法4,我们可以立即改进采样算法(算法2)。对于相同的复杂度,现在我们可以在每次调用验证算法时检查 $ M $ 个值。因此,算法5每次通过随机采样选取 $ M $ 个 $ y_i $ 的随机值,并调用算法4来测试其中哪些值(如果有的话)确实是 $ y_0 $。

算法4。模式验证:确定 $ y_0 $ 是否为 $ y_1, y_2, …, y_M $ 之一

初始化一个用于 1 ≤ i ≤ M 的计数器数组 ctr[i] ← 0。
对于 j = 1 到 c/p 执行
    随机选取 x ∈ [N],并计算 y′ = f(x)。
    如果 y′ = y_i for 1 ≤ i ≤ M 那么
        递增 ctr[i]。
    结束 if
结束 for
for i = 1 到 N do
    如果 ctr[i] ≥ t 则
        打印 y_i 是 y_0。
    结束 if
结束 for

算法5. 通过采样(含内存)寻找 y0:

当 y0 未被找到时执行
    对 i = 1 到 M 执行
        随机选取 x_i ∈ [N]。
        计算 y_i = f(x_i)。
    结束循环
    调用算法4来检查 y_1, y_2, ..., y_M。
结束循环

单次调用算法4(测试 $ M $ 张图像)成功的概率约为 $ Mp $(假设 $ M \leq p^{-1} $),因此我们预期需要调用算法 $ O(M^{-1}p^{-1}) $ 次。每次调用需要对 $ O(p^{-1}) $ 进行 $ f $ 次评估,因此该算法的总时间复杂度为 $ O(M^{-1}p^{-2}) $。注意,当 $ M = 1 $ 时,该算法退化为算法2。

7.3 使用并行碰撞搜索的模式检测

该算法运行 PCS,使用 $ M $ 条链,并检查通过运行算法4找到的 $ O(M) $ 个碰撞点。此过程将重复进行,直到找到 $ y_0 $,每次重复均使用不同变体的 $ f $。

由于 $ M $ 条链覆盖了约 $ \sqrt{NM} $ 个不同的点,两个不同的众数原像(预计不是区分点)被该结构覆盖的概率约为 $ (\sqrt{NM} \cdot p)^2 = NM \cdot p^2 $(假设 $ \sqrt{NM} \cdot p < 1 $,即 $ p < (NM)^{-0.5} $)。在这种情况下,算法将成功使用众数验证算法恢复众数 $ y_0 $。因此,预计该算法将执行约 $ N^{-1}M^{-1} \cdot p^{-2} $ 次 PCS(以及众数验证),每次执行需要 $ O(p^{-1}) $ 时间(假设 $ p < (NM)^{-1/2} $,在时间复杂度方面,众数验证主导了 PCS)。总体而言,该算法的时间复杂度为 $ O(M^{-1}N^{-1} \cdot p^{-3}) $。注意,当 $ M = 1 $ 时,我们得到 RepeatedRho。

上述公式仅对 $ p < (NM)^{-0.5} $ 或 $ M < p^{-2}N^{-1} $ 有效。否则,我们只能利用 $ M = p^{-2}N^{-1} $ 内存,并获得本质上最优的时间复杂度 $ O(p^{-1}) $。

7.4 基于 2Rho 的并行碰撞搜索进行模式检测

我们现在假设 $ M < p^{-2}N^{-1} $(否则,我们使用之前的 PCS 算法以最优复杂度检测众数),并扩展 2Rho 算法,使用 PCS。这通过定义一个链结构实现,该结构通过迭代函数 $ g $(如第5节所定义)来计算,其执行过程通过对 $ f $ 的特定变体进行迭代直到找到碰撞点。每条链从 $ g $ 的任意输入开始(这定义了一种 $ f $ 的变体),并在 $ g $ 的显著点处终止。即,显著点定义在 $ g $ 的输出上(这些是 $ f $ 中的碰撞点)。再次使用算法4来测试 $ O(M) $ 次 $ g $ 的碰撞。

如第5节中计算的,众数 $ y_0 $ 在一次 Rho 运行(即 $ g $ 的一次迭代)中成为碰撞点的概率为 $ p’ = p^2N $。由于 $ g $ 中的 $ M $ 条链覆盖了约 $ \sqrt{NM} $ 个不同的碰撞点,因此结构覆盖众数 $ y_0 $ 在 $ g $ 中的两个不同原像(这些原像不预期为显著点)的概率约为 $ (\sqrt{NM} \cdot p’)^2 = NM \cdot p’^2 = NM \cdot p^4N^2 = M \cdot N^3p^4 $(假设 $ \sqrt{NM} \cdot p’ < 1 $ 或 $ p^2N \cdot (NM)^{1/2} < 1 $,即 $ p^2N^{3/2}M^{1/2} < 1 $)。因此,我们重复 PCS 算法(以及众数验证算法) $ M^{-1} \cdot N^{-3}p^{-4} $ 次(使用不同变体的 $ g $)。

PCS 算法需要 $ (NM)^{1/2} $ 次对 $ g $ 的调用,每次调用需要 $ N^{1/2} $ 时间,总共需要 $ N \cdot M^{1/2} $ 时间,这主导了众数验证的复杂度。总体而言,该算法的时间复杂度为 $ M^{-1} \cdot N^{-3}p^{-4} \cdot N \cdot M^{1/2} = M^{-1/2} \cdot N^{-2}p^{-4} $。

上述公式仅在 $ p^2N^{3/2}M^{1/2} < 1 $ 或 $ M < p^{-4}N^{-3} $ 成立时有效。否则,我们只能利用 $ M = p^{-4}N^{-3} $(假设 $ p^{-4}N^{-3} \geq 1 $)的内存,并得到 $ M^{-1/2} \cdot N^{-2}p^{-4} = p^2N^{3/2} \cdot N^{-2}p^{-4} = p^{-2}N^{-1/2} $ 的时间复杂度。

我们现在注意到,可以得到更通用的公式以便后续重用。本质上,该算法的分析依赖于三个参数,如下所述。在单次运行 Rho(即 $ g $ 的一次迭代)中,众数 $ y_0 $ 成为碰撞点的概率为 $ p’ = p^2N $,在本例中我们将其记为 $ p^{x_1}N^{x_2} $,其中 $ x_1 = 2, x_2 = 1 $。此外,每次调用 $ g $ 需要 $ N^{1/2} $ 时间,在本例中我们将其记为 $ N^{x_3} $,其中 $ x_3 = 1/2 $。基于这些参数,我们可以重新符号化地进行上述分析,并得出该算法的时间复杂度为 $ M^{-1/2} \cdot N^{-2x_2 - 1/2 + x_3}p^{-2x_1} $。

此公式仅在 $ M < p^{-2x_1}N^{-2x_2 - 1} $ 时成立。否则,我们只能使用 $ M = p^{-2x_1}N^{-2x_2 - 1} $(假设 $ p^{-2x_1}N^{-2x_2 - 1} \geq 1 $)内存,并得到 $ p^{-x_1}N^{-x_2 + x_3} $ 的时间复杂度。

7.5 基于 3Rho 的并行碰撞搜索进行模式检测

我们继续分析扩展 3Rho 并使用 PCS 的算法序列。其思想与扩展 2Rho 基本相同,区别在于执行 PCS 所针对的函数。

这里,PCS 在函数 $ h $(如第6节所定义)上执行,同时调用算法4来测试 $ O(M) $ 个 $ h $ 的碰撞点。

如第6节所计算,众数 $ y_0 $ 在 $ g $ 的单次运行中成为碰撞点的概率为 $ p’’ = p^4N^3 $,我们将其记为 $ p^{x_1}N^{x_2} $,其中 $ x_1 = 4 $,$ x_2 = 3 $。在这种情况下,每次调用 $ h $ 需要 $ N $ 时间,或 $ N^{x_3} $,其中 $ x_3 = 1 $。在重用第7.4节中获得的公式,并考虑我们针对情况 $ x_1 = 4, x_2 = 3, x_3 = 1 $ 的特定参数 $ M < p^{-2x_1}N^{-2x_2 - 1} $,或 $ M < p^{-8}N^{-7} $ 假设 $ p^{-8}N^{-7} \geq 1 $ 或 $ p \leq N^{-7/8} $。这给出的时间复杂度为 $ M^{-1/2} \cdot N^{-2x_2 - 1/2 + x_3}p^{-2x_1} $ 或 $ M^{-1/2} \cdot N^{-5.5}p^{-8} $。注意,当 $ M = 1 $ 时,我们得到算法 4Rho。

对于 $ M > p^{-8}N^{-7} $,我们得到的时间复杂度为 $ p^{-x_1}N^{-x_2 + x_3} = p^{-4}N^{-2} $。参见图6以比较在给定 $ M = N^{1/4} $ 内存下的不同算法。

基于并行碰撞搜索的模式检测 2Rho。将 PCS 扩展到 4Rho 没有意义,因为用于 4Rho 的函数 $ \mathcal{D} $(定义见第6节)在我们的算法中迭代次数从未超过 $ N^{0.5} $ 次。因此,其所有迭代均可由一条 4Rho 链覆盖,在这种情况下使用内存并无优势。

7.6 讨论

比较上述算法并不直观,因为它们的复杂度是 $ p $ 和 $ M $ 的函数。为了更好地理解它们的性能,我们固定 $ M = N^{1/4} $,并将针对此情况的最佳算法的复杂度总结为单一参数 $ p $ 的函数,如表3所示。从表中可以明显看出,存在一个 $ p $ 值的范围,对于该范围我们尚不清楚如何高效地利用内存。例如,考虑 $ p = N^{-3/4} $ 的情况,此时我们最好的算法是 PCS 优于 2Rho。然而,它实际上是 PCS 的一种退化变体,其中 $ M = 1 $ 与第6节中的 2Rho 算法一致。

表3. 检测众数的最佳算法复杂度摘要,其中 $ M = N^{1/4} $

概率范围 复杂度公式 复杂度值域 算法
$ p \geq N^{-0.5} $ $ T = p^{-1} $ $ T \leq N^{0.5} $ Rho
$ N^{-5/8} \leq p \leq N^{-0.5} $ $ T = p^{-1} $ $ N^{0.5} \leq T \leq N^{5/8} $ PCS
$ N^{-3/4} \leq p \leq N^{-5/8} $ $ T = p^{-3}N^{-5/4} $ $ N^{5/8} \leq T \leq N $ PCS
$ N^{-13/16} \leq p \leq N^{-3/4} $ $ T = p^{-2}N^{-1/2} $ $ N \leq T \leq N^{9/8} $ PCS over 2Rho
$ N^{-7/8} \leq p \leq N^{-13/16} $ $ T = p^{-4}N^{-17/8} $ $ N^{9/8} \leq T \leq N^{11/8} $ PCS over 2Rho
$ N^{-1} < p \leq N^{-7/8} $ $ T = p^{-3}N^{-5/4} $ $ N^{11/8} \leq T \leq N^{7/4} $ PCS

8 寻找多个峰值

我们将基本问题推广到 $ f $ 均匀分布但存在 $ k $ 个峰值的情况。这些峰值记为 $ y_0, y_1, …, y_{k-1} $,其对应的概率记为 $ p_0, p_1, …, p_{k-1} $,我们的目标是找出所有这些峰值。

最简单的情况是存在两个高度相等的峰值 $ p_0 = p_1 $。通过使用不同类型的 $ f $ 多次运行 NestedRho 算法,我们预期大约一半的时间找到 $ y_0 $,另一半时间找到 $ y_1 $,因此无需进行任何修改。

接下来要考虑的情况是只有两个峰值但 $ p_0 > p_1 $ 的情况。由于我们的公式中 $ p $ 的幂次很高,即使峰值概率存在适度差异,NestedRho 算法也会将其放大为发现这两个峰值的概率的巨大差异。例如,如果 $ p_0 $ 比 $ p_1 $ 大一千倍,且我们多次运行该算法,则使用 1Rho 时,我们预计每百万次运行中仅能发现一次 $ y_1 $,而使用 2Rho 时,每万亿次运行中才能发现一次 $ y_1 $。显然,在我们有现实机会注意到 $ y_1 $ 之前,必须降低 $ y_0 $ 的显著性。

中和我们找到的第一个峰值(很可能是 $ y_0 $)的最简单方法,是将其原像分散,使它们指向不同的目标。考虑一个修改后的函数 $ f’ $,其定义如下:对于任何满足 $ f(x) \neq y_0 $ 的 $ x $,定义为 $ f $;对于任何满足 $ f(x) = y_0 $ 的 $ x $,定义为 $ f(x) + x $。在 $ f’ $ 中,$ y_0 $ 不再是一个峰值,但 $ y_1 $ 保持在其原始高度。通过对 $ f’ $ 应用 NestedRho,我们将以高概率找到 $ y_1 $。

这可以轻松推广到由 $ k $ 个峰值组成的序列,前提是至少有 $ O(k) $ 的内存来存储所有峰值。我们的算法可能会按概率递减的顺序依次发现这些峰值,并且我们可以在内存或时间耗尽时的任意时刻决定停止。

最一般的情况是我们面对一个没有明显峰值的非均匀分布。在这种情况下,NestedRho 算法的输出倾向于选择具有较高概率的 $ y $ 值,但如果较低概率的 $ y $ 值数量较多,也可能被选中。事实上,我们的算法选择某个特定 $ y $ 的概率与其原始概率的某个幂次成正比,该幂次取决于所使用的嵌套层级(详细分析留待未来工作)。

9 结论与开放性问题

在本文中,我们提出了大海捞针这一通用问题,开发了多种新颖的解决技术,并展示了其复杂度函数令人惊讶的复杂性。许多问题仍有待解决,例如:
1. 寻找该问题时间复杂度的非平凡下界。
2. 寻找比使用 PCS 更优的方法来充分利用可用内存。
3. 扩展模型以处理其他类型的针。
4. 发现新的 NestedRho 技术的更多应用。

根据原作 https://pan.quark.cn/s/459657bcfd45 的源码改编 Classic-ML-Methods-Algo 引言 建立这个项目,是为了梳理和总结传统机器学习(Machine Learning)方法(methods)或者算法(algo),和各位同仁相互学习交流. 现在的深度学习本质上来自于传统的神经网络模型,很大程度上是传统机器学习的延续,同时也在不少时候需要结合传统方法来实现. 任何机器学习方法基本的流程结构都是通用的;使用的评价方法也基本通用;使用的一些数学知识也是通用的. 本文在梳理传统机器学习方法算法的同时也会顺便补充这些流程,数学上的知识以供参考. 机器学习 机器学习是人工智能(Artificial Intelligence)的一个分支,也是实现人工智能最重要的手段.区别于传统的基于规则(rule-based)的算法,机器学习可以从数据中获取知识,从而实现规定的任务[Ian Goodfellow and Yoshua Bengio and Aaron Courville的Deep Learning].这些知识可以分为四种: 总结(summarization) 预测(prediction) 估计(estimation) 假想验证(hypothesis testing) 机器学习主要关心的是预测[Varian在Big Data : New Tricks for Econometrics],预测的可以是连续性的输出变量,分类,聚类或者物品之间的有趣关联. 机器学习分类 根据数据配置(setting,是否有标签,可以是连续的也可以是离散的)和任务目标,我们可以将机器学习方法分为四种: 无监督(unsupervised) 训练数据没有给定...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值