Algorithm 确定同余系是否有解
有一个线性同余系统,我想确定它是否有解。使用简单的算法来解决这样的系统是不可能的,因为答案可能呈指数增长 我的一个假设是,如果一个同余系统没有解,那么其中有两个相互矛盾。我不知道这是否成立,如果成立的话,这将导致一个简单的O(n^2 logn)算法,因为检查一对同余是否有解需要O(logn)时间。然而,对于这个问题,我宁愿看到更接近O(n)的东西Algorithm 确定同余系是否有解,algorithm,math,modulo,Algorithm,Math,Modulo,有一个线性同余系统,我想确定它是否有解。使用简单的算法来解决这样的系统是不可能的,因为答案可能呈指数增长 我的一个假设是,如果一个同余系统没有解,那么其中有两个相互矛盾。我不知道这是否成立,如果成立的话,这将导致一个简单的O(n^2 logn)算法,因为检查一对同余是否有解需要O(logn)时间。然而,对于这个问题,我宁愿看到更接近O(n)的东西 我们可以假设没有模量超过10^6,特别是我们可以快速地将它们全部因子化。我们甚至可以进一步假设所有模的总和不超过10^6(但是,它们的乘积可能是巨大的
我们可以假设没有模量超过10^6,特别是我们可以快速地将它们全部因子化。我们甚至可以进一步假设所有模的总和不超过10^6(但是,它们的乘积可能是巨大的)。正如您所怀疑的,有一种相当简单的方法可以确定同余集是否有解,而不需要实际构建该解。您需要:
x=a(mod n)
;从评论中,听起来好像你已经有了这个n=p1^e1*p2^e2*…*pk^ek
x=a(mod n)
替换为一组同余x=a(mod pi^ei)
,步骤2中找到的k
素数幂对应一个同余x=a(mod p^e)
和x=b(mod p^f)
,它们是兼容的当且仅当a=b(mod p^(min(e,f))
。确定兼容性后,您可以丢弃模较小的同余,而不会丢失任何信息
有了正确的数据结构,您可以在一次通过同余点的过程中完成所有这一切:对于遇到的每个素数p
,您需要跟踪到目前为止发现的最大指数e
,以及相应的右侧(为了方便起见,减少模p^e
)。运行时间可能主要由模分解决定,但如果没有模超过10^6
,则也可以通过从1..10^6
范围内的每个整数预构建到其最小素数因子的映射来加快这一步
编辑:由于这应该是一个编程站点,这里有一些(Python 3)代码来说明上述内容。(对于Python 2,将
range
调用替换为xrange
,以提高效率。)
最后一点注意:在上面,我们利用了模很小的事实,因此计算素数幂因子并不是什么大问题。但是如果你确实需要对更大的模(数百或数千位)这样做,它仍然是可行的。你可以跳过因子分解步骤,而是找到一个“互质基”对于模集合:即成对相对素数正整数的集合,这样同余中出现的每个模都可以表示为乘积(可能有重复)集合中的元素。现在按照上面的步骤进行,但要参考互质基,而不是素数和素数幂的集合。请参见Daniel Bernstein提供的计算一组正整数的互质基的有效方法。您可能会在列表中进行两次遍历:一次计算互质基,另一次d来检查一致性。这似乎是math.stackexchange.com的一个问题。不过,你的假设是有效的:如果一致性是成对一致的,那么它们是相互一致的。你需要这一点来计算什么大小的模?我们是否处于将这些模分解为素数幂非常容易的范围内(例如,所有的模量都小于2**32),或者这必须对更大的模量起作用吗?你能提供一个简短的证据来证明为什么这个假设是正确的吗?我把我的问题放在这里是因为唯一阻止我用O(n log n)来解它的东西事实上,存储部分解可能会超过可用内存,所以这不是一个真正的数学问题。将其分解为素数幂:根据中国剩余定理,同余是相互一致的,如果它们是模的素数幂块的相互一致模,对于每个相关素数,这就简化为这种情况其中所有模都是一个素数的幂。在这种情况下,当它们都与包含最大模的同余一致时,它们是相互一致的。证据应该可以在网上的某个地方找到;到目前为止,我发现最好的是第5页,我想通过同余可能是O(m log n)其中m是同余数,n是最大模,因为n最多可以有O(logn)个不同的素因子。这就是我需要的,谢谢!是的,如果你不考虑因子分解的成本,
O(mlogn)
听起来不错。
def prime_power_factorisation(n):
"""Brain-dead factorisation routine, for illustration purposes only."""
# DO NOT USE FOR LARGE n!
while n > 1:
p, pe = next(d for d in range(2, n+1) if n % d == 0), 1
while n % p == 0:
n, pe = n // p, pe*p
yield p, pe
def compatible(old_ppc, new_ppc):
"""Determine whether two prime power congruences (with the same
prime) are compatible."""
m, a = old_ppc
n, b = new_ppc
return (a - b) % min(m, n) == 0
def are_congruences_solvable(moduli, right_hand_sides):
"""Determine whether the given congruences have a common solution."""
# prime_power_congruences is a dictionary mapping each prime encountered
# so far to a pair (prime power modulus, right-hand side).
prime_power_congruences = {}
for m, a in zip(moduli, right_hand_sides):
for p, pe in prime_power_factorisation(m):
# new prime-power congruence: modulus, rhs
new_ppc = pe, a % pe
if p in prime_power_congruences:
old_ppc = prime_power_congruences[p]
if not compatible(new_ppc, old_ppc):
return False
# Keep the one with bigger exponent.
prime_power_congruences[p] = max(new_ppc, old_ppc)
else:
prime_power_congruences[p] = new_ppc
# If we got this far, there are no incompatibilities, and
# the congruences have a mutual solution.
return True