在数组中查找对,使得%b = k,其中k是给定的整数

时间:2012-10-04 17:54:12

标签: algorithm language-agnostic number-theory

这是我遇到的一个有趣的编程难题。给定一个正整数数组和一个数字K.我们需要从数组中找到对(a,b)a % b = K

我有一个天真的 O(n ^ 2)解决方案,我们可以检查所有对,使得a%b = k。工作但效率低下。我们当然可以做得比这更好吗?任何有效的算法?哦,这不是功课。

3 个答案:

答案 0 :(得分:5)

对数组和二进制搜索进行排序,或者使用数组中每个值的计数保留哈希表。

对于代码x,我们可以找到最大的yx mod y = Ky = x - K。二进制搜索此y或在您的哈希中查找并相应地增加计数。

现在,这不一定是唯一可行的价值。例如,8 mod 6 = 8 mod 3 = 2。我们有:

x mod y = K => x = q*y + K =>
            => x = q(x - K) + K =>
            => x = 1(x - K) + K =>
            => x = 2(x - K)/2 + K =>
            => ...

这意味着您还必须测试y的所有除数。您可以在O(sqrt y)中找到除数,如果使用二进制搜索,则会给出O(n log n sqrt(max_value))的总复杂度;如果您的数字不是很大,则会推荐O(n sqrt(max_value))

答案 1 :(得分:0)

将问题视为具有两个单独的数组作为输入:一个用于a数字,一个%b = K,一个用于b数字。我假设一切都是> = 0。

首先,您可以丢弃任何b< = K。

现在想想b中的每个数字生成一个序列K,K + b,K + 2b,K + 3b ......你可以用一对数字(pos,b)记录这个数字,其中pos增加b在每个阶段。从pos = 0开始。

将这些序列保存在优先级队列中,这样您就可以在任何给定时间找到最小的pos值。对数字数组进行排序 - 事实上,您可以提前完成此操作并丢弃任何重复数据。

For each a number
  While the smallest pos in the priority queue is <= a
    Add the smallest multiple of b to it to make it >= a
    If it is == a, you have a match
    Update the stored value of pos for that sequence, re-ordering the priority queue

最糟糕的是,您最终会将每个数字与其他所有数字进行比较,这与简单解决方案相同,但具有优先级队列和排序开销。但是,在优先级队列中可以保留大的b值,而有几个数字通过,在这种情况下,这样做会更好 - 如果要处理的数量很多并且它们都不同,则其中一些必须很大。

答案 2 :(得分:0)

这个答案提到算法的要点(称为 DL ,因为它使用“除数列表”),并通过一个名为amodb.py的程序提供详细信息。

设B为输入数组,包含N个正整数。在不失一般性的情况下,假设所有B[i] > Ki,并且B按升序排列。 (请注意,x%B[i] < K如果B[i] < K;以及B[i] = K,则可以报告所有j>i的对(B [i],B [j])。如果B不是最初排序,收取O(N log N)的费用进行排序。)

在算法 DL 和程序amodb.py中,A是从输入数组元素中预先减去K的数组。即,A[i] = B[i] - K。请注意,如果a%b == K,那么对于某些j,我们会a = b*j + Ka-K = b*j。也就是说,a%b == K iff a-Kb的倍数。此外,如果a-K = b*jpb的任何因素,则pa-K的因子。

让2到97之间的素数称为“小因子”。当从某个区间[X,Y]中均匀地随机选择N个数时,在数字的N / ln(Y)的数量级上将没有小因子;类似数字的最小因子为2;而且不断下降的比例将会有更大的最大小因素。例如,平均约N/97将被97整除,约N/89-N/(89*97)乘89而不是97等。一般来说,当B的成员是随机的时,具有某些最大小因子的成员列表或不小的因素是亚O(N / ln(Y))的长度。

给定一个列表Bd包含可被最大小因子 p 整除的B成员, DL 测试Bd的每个元素对列表Ad的元素,那些可被C整除的元素 p 的。但是给定B的元素列表Bp没有小因子, DL 测试每个Bp元素对A的所有元素。示例:如果N=25p=13,{{1 }}和Bd=[18967, 23231],然后 DL 测试Ad=[12779, 162383]是否为零。请注意,通过注意12779%18967, 162383%18967, 12779%23231, 162383%23231,可以在此示例(以及许多其他示例)中将测试数量减少一半,但amodb.py不包括该优化。

DL 12779<18967个不同因素制作J个不同的列表;在amodb.py的一个版本中,J且因子集的素数小于100. J=25的值越大,J时间就会增加初始化除数列表,但会略微减少O(N*J)时间处理列表Bp对A的元素。参见下面的结果。处理其他列表的时间为O(N*len(Bp)),这与之前答案方法的O((N/logY)*(N/logY)*J)复杂性形成鲜明对比。

接下来显示的是两次程序运行的输出。在每个集合中,第一个O(n*sqrt(Y))行来自天真的O(N * N)测试,第二个来自 DL 。 (注意,如果逐渐移除太小的A值, DL 和初始方法都会运行得更快。)第一次测试的最后一行的时间比率显示令人失望的低加速比率为3.9 DL 与天真的方法。对于该运行,Found仅包括少于100的25个素数。对于第二次运行,具有更好的加速度~4.4,factors包括数字2到13并且素数达到100。

factors

编程amodb.py:

  $ python amodb.py 
  N:  10000   K:  59685 X:  100000   Y:  1000000
  Found 208  matches in 21.854 seconds
  Found 208  matches in 5.598 seconds
  21.854 / 5.598 = 3.904

  $ python amodb.py
  N:  10000   K:  97881 X:  100000   Y:  1000000
  Found 207  matches in 21.234 seconds
  Found 207  matches in 4.851 seconds
  21.234 / 4.851 = 4.377