具有给定总和和产品的对的数量

时间:2017-02-10 21:56:47

标签: algorithm data-structures

我有一个数组A以及3个变量kxy。 我必须找到无序对(i,j)的数量,以便两个元素mod k的总和等于x,并且相同的两个元素mod k的乘积等于{ {1}}。对不必是明显的。换句话说,y的数量使

(i,j)(A[i]+A[j])%k == x其中(A[i]*A[j])%k == y

例如,允许0 <= i < j < size of AA={1,2,3,2,1}k=2x=1。然后答案是6,因为对是:y=0(1,2)(1,2)(2,3)(2,1)(3,2)

我使用蛮力方法,但显然这是不可接受的。

3 个答案:

答案 0 :(得分:1)

模运算有以下两个规则:

((a mod k) * (b mod k)) mod k = (a * b) mod k
((a mod k) + (b mod k)) mod k = (a + b) mod k

因此,我们可以将所有值排序为包含separate chainingk存储桶的哈希表。

加成

查找m < k,以便针对给定的n < k(n + m) mod k = x。 这个问题只有一个解决方案:

  • 如果n < xm < x必须保留。因此m = x - n
  • 如果n == xm = 0
  • 如果n > x:我们需要找到m n + m = x + k。因此m = x + k - n

通过这种方式,我们可以轻松地为每个值列表确定相应的值,以便两个列表(a, b)的交叉产品的(a + b) mod k = x对保持不变。

乘法

乘法有点棘手。幸运的是,我们已经获得了用于加法的匹配同余类(见上文),它必须是乘法的匹配同余类,因为两个约束都需要保持。要验证给定的同余类是否匹配,我们只需要检查(n * m) mod k = ynm如上定义)。如果此表达式成立,我们可以构建对,否则不存在匹配元素。

实施

这将是以上示例的工作python代码:

def modmuladd(ls, x, y, k):
    result = []

    # create tuples of indices and values
    indices = zip(ls, range(0, len(ls)))

    # split up into congruence classes
    congruence_cls = [[] for i in range(0, k)]
    for p in indices:
        congruence_cls[p[0] % k].append(p)

    for n in range(0, k):
        # congruence class to match addition
        if n < x:
            m = x - n
        elif n == x:
            m = 0
        else:
            m = x + k - n

        # check if congruence class matches for multiplication
        if (n * m) % k != y or len(congruence_cls[m]) == 0:
            continue    # no matching congruence class

        # add matching tuple to result
        result += [(a, b) for a in congruence_cls[n] for b in congruence_cls[m] if a[1] <= b[1]]
        result += [(a, b) for a in congruence_cls[m] for b in congruence_cls[n] if a[1] <= b[1]]

    # sort result such according to indices of first and second element, remove duplicates
    sorted_res = sorted(sorted(set(result), key=lambda p: p[1][1]), key=lambda p: p[0][1])

    # remove indices from result-set
    return [(p[0][0], p[1][0]) for p in sorted_res]

请注意,仅需要排序和消除重复项,因为此代码集中于同余类的使用而非完美优化。这个例子可以很容易地调整,以提供排序,而无需通过微小的修改进行排序。

试运行

print(modmuladd([1, 2, 3, 2, 1], 1, 0, 2))

输出:

  

[(1,2),(1,2),(2,3),(2,1),(3,2),(2,1)]

编辑:

此算法的最坏情况复杂性仍为O(n^2),因为构建大小为n的列表的所有可能对为O(n^2)。但是,使用此算法,匹配对的搜索可以通过O(k)预处理减少到O(n)。因此,使用这种方法可以在O(n)中完成对结果对的计数。假设数字在同余类上均匀分布,则此算法可以构建O(n^2/k^2)中作为解决方案集的一部分的所有对。

编辑2:
只计算的实现可以这样工作:

def modmuladdct(ls, x, y, k):
    result = 0

    # split up into congruence classes
    congruence_class = {}
    for v in ls:
        if v % k not in congruence_class:
            congruence_class[(v % k)] = [v]
        else:
            congruence_class[v % k].append(v)

    for n in congruence_class.keys():
        # congruence class to match addition
        m = (x - n + k) % k

        # check if congruence class matches for multiplication
        if (n * m % k != y) or len(congruence_class[m]) == 0:
            continue    # no matching congruence class

        # total number of pairs that will be built
        result += len(congruence_class[n]) * len(congruence_class[m])

    # divide by two since each pair would otherwise be counted twice
    return result // 2

每对在结果中恰好出现两次:一次按顺序,一次反转顺序。通过将结果除以2,这将得到纠正。运行时为O(n + k)(假设字典操作为O(1))。

答案 1 :(得分:0)

在你的情况下,循环的数量是C(2,n)= 5!/(2!(5-2)!= 10个循环,没有任何魔法可以大大减少循环次数。 在JS中你可以这样做:

A = [1, 2, 3, 2, 1];
k = 2;
x = 1;
y = 0;
for(i=0; i<A.length; i++) {
  for(j=i+1; j<A.length; j++) {
    if ((A[i]+A[j])%k !== x) {
        continue;
    }
    if ((A[i]*A[j])%k !== y) {
        continue;
    }
    console.log('('+A[i]+', '+A[j]+')');
  }
}

答案 2 :(得分:0)

忽略A,我们可以找到n * (x - n) == y mod k的所有解决方案,其中0 <= n&lt; k。这是一个简单的O(k)算法 - 依次检查每个n

我们可以为每个n计算A[i] == n的频率,然后重新计算对数。如果cs是这些计数的数组,并且nn * (x - n) == y mod k的解决方案,则cs[n] * cs[(x-n)^k]中有An项解决与此n对应的方程式。为避免重复计算,我们只计算n < (x - n) % k def count_pairs(A, k, x, y): cs = [0] * k for a in A: cs[a % k] += 1 pairs = ((i, (x-i)%k) for i in xrange(k) if i * (x-i) % k == y) return sum(cs[i] * cs[j] for i, j in pairs if i < j) print count_pairs([1, 2, 3, 2, 1], 2, 1, 0)

A

总的来说,这构造了O(| k |)时间内的计数,其余代码以O(k)时间运行。它使用O(import { Component, OnInit } from '@angular/core'; @Component({ selector: 'wizard', template: ` <div> <ng-content></ng-content> <button>Back</button> <button>Next</button> </div> ` }) export class WizardComponent implements OnInit { steps = []; constructor() { } addStep(step) { this.steps.push(step); } ngOnInit() { } } )空格。