我有一个数组A
以及3个变量k
,x
和y
。
我必须找到无序对(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 A
,A={1,2,3,2,1}
,k=2
,x=1
。然后答案是6,因为对是:y=0
,(1,2)
,(1,2)
,(2,3)
,(2,1)
和(3,2)
。
我使用蛮力方法,但显然这是不可接受的。
答案 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 chaining和k
存储桶的哈希表。
查找m < k
,以便针对给定的n < k
:(n + m) mod k = x
。
这个问题只有一个解决方案:
n < x
:m < x
必须保留。因此m = x - n
n == x
:m = 0
n > x
:我们需要找到m
n + m = x + k
。因此m = x + k - n
通过这种方式,我们可以轻松地为每个值列表确定相应的值,以便两个列表(a, b)
的交叉产品的(a + b) mod k = x
对保持不变。
乘法有点棘手。幸运的是,我们已经获得了用于加法的匹配同余类(见上文),它必须是乘法的匹配同余类,因为两个约束都需要保持。要验证给定的同余类是否匹配,我们只需要检查(n * m) mod k = y
(n
和m
如上定义)。如果此表达式成立,我们可以构建对,否则不存在匹配元素。
这将是以上示例的工作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
是这些计数的数组,并且n
是n * (x - n) == y mod k
的解决方案,则cs[n] * cs[(x-n)^k]
中有A
对n
项解决与此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() { }
}
)空格。