【问题标题】:Find pairs in an array such that a%b = k , where k is a given integer在数组中查找对,使得 a%b = k ,其中 k 是给定的整数
【发布时间】:2012-10-04 17:54:11
【问题描述】:

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

我对此有一个简单的 O(n^2) 解决方案,我们可以检查所有对,例如 a%b=k。有效但效率低下。我们当然可以做得比这更好,不是吗?任何有效的算法相同?哦,这不是家庭作业。

【问题讨论】:

  • @harold 会有什么不同吗?
  • 最坏情况 - O(n^2) 对。 (4,4,7,7,10,3,3,3,3,3) k=1
  • 如果您可以将它们列为多集条目,您也可以比 O(n^2) 更快地列出它们。 (即,作为计数对)。
  • @dbaseman 和其他密切投票者,请注意这不是重复的。这个问题要求A%B = K 的算法。所链接的问题是要求(A+B)%k = 0 的算法
  • ... 而那些连两行都懒得读的人毁了另一个完美的问题。

标签: algorithm language-agnostic number-theory


【解决方案1】:

对数组进行排序和二分搜索,或者保留一个哈希表,其中包含数组中每个值的计数。

对于一个数字x,我们可以找到最大的y,使得x mod y = K 等于y = 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)) 总复杂度为(特别推荐在您的数字不是很大时)。

【讨论】:

    【解决方案2】:

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

    首先,你可以丢弃任何 b

    现在将 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
    

    在最坏的情况下,您最终会将每个数字与每个其他数字进行比较,这与简单的解决方案相同,但具有优先级队列和排序开销。但是,当多个 a 数字通过时,较大的 b 值可能在优先级队列中保持未经检查,在这种情况下这样做会更好 - 如果有很多数字要处理并且它们都是不同的,那么其中一些肯定很大。

    【讨论】:

      【解决方案3】:

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

      设 B 为输入数组,包含 N 个正整数。不失一般性,假设B[i] &gt; K 代表所有i,并且B 是升序排列的。 (注意x%B[i] &lt; K if B[i] &lt; K; and where B[i] = K, one can report pairs (B[i], B[j]) for all j&gt;i. 如果B最初没有排序,收取@的成本987654327@排序。)

      在算法 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 的元素,A 的那些元素可被p。但是给定一个 Bp 的列表 Bp 包含没有小因素的 B 元素,DL 将 Bp 的每个元素与 A 的所有元素进行测试。示例:如果 N=25p=13Bd=[18967, 23231] 和 @987654346 @,然后 DL 测试任何12779%18967, 162383%18967, 12779%23231, 162383%23231 是否为零。请注意,通过注意 12779&lt;18967,可以将本示例(以及许多其他示例)中的测试数量减少一半,但 amodb.py 不包含该优化。

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

      接下来显示的是两个程序运行的输出。在每个集合中,第一行 Found 来自一个简单的 O(N*N) 测试,第二行来自 DL。 (注意,如果逐步删除太小的 A 值,DL 和 naïve 方法都会运行得更快。)第一个测试的最后一行中的时间比率显示,对于 3.9 的加速比率低得令人失望。 DL vs naïve 方法。对于那次运行,factors 仅包含 25 个小于 100 的素数。对于第二次运行,factors 包含数字 2 到 13 以及最多 100 个素数,加速比更好,约为 4.4。

        $ 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
      

      程序 amodb.py:

      import random, time
      factors = [2,3,4,5,6,7,8,9,10,11,12,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97]
      X, N = 100000, 10000
      Y, K = 10*X, random.randint(X/2,X)
      print "N: ", N, "  K: ", K, "X: ", X, "  Y: ", Y
      B = sorted([random.randint(X,Y) for i in range(N)])
      NP = len(factors);  NP1 = NP+1
      A, Az, Bz = [], [[] for i in range(NP1)], [[] for i in range(NP1)]
      t0 = time.time()
      for b in B:
          a, aj, bj = b-K, -1, -1
          A.append(a)                 # Add a to A 
          for j,p in enumerate(factors):
              if a % p == 0:
                  aj = j
                  Az[aj].append(a)
              if b % p == 0:
                  bj = j
          Bz[bj].append(b)
      Bp = Bz.pop()   # Get not-factored B-values list into Bp
      di = time.time() - t0; t0 = time.time()
      c = 0
      for a in A:
          for b in B:
              if a%b == 0:
                  c += 1
      dq =  round(time.time() - t0, 3); t0 = time.time()
      c=0
      for i,Bd in enumerate(Bz):
          Ad = Az[i]
          for b in Bd:
              for ak in Ad:
                  if ak % b == 0:
                      c += 1
      for b in Bp:
          for ak in A:
              if ak % b == 0:
                  c += 1
      dr = round(di + time.time() - t0, 3)
      print "Found", c, " matches in", dq, "seconds"
      print "Found", c, " matches in", dr, "seconds"
      print dq, "/", dr, "=", round(dq/dr, 3)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-05-27
        • 2014-12-29
        • 2017-01-29
        • 2021-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-09-16
        相关资源
        最近更新 更多