中国剩余定理


21

中国剩余定理告诉我们,我们总能找到一些能产生下不同的素模所需的任何剩余物。您的目标是编写代码以在多项式时间内输出这样的数字。最短的代码胜出。

例如,假设我们得到了以下约束(%代表mod):

n % 7  == 2
n % 5  == 4
n % 11 == 0

一种解决方案是n=44。第一个条件满足,因为44 = 6*7 + 2,所以44有剩余2时,除以7,因此44 % 7 == 2。也满足了其他两个约束。还有其他解决方案,例如n=814n=-341

输入项

对的非空列表(p_i,a_i),其中每个模数p_i是一个不同的质数,每个目标a_i是该范围内的自然数0 <= a_i < p_i。您可以采用任何方便的形式进行输入。它实际上不一定是对列表。您可能不认为输入已排序。

输出量

这样的整数n,以便n % p_i == a_i于每个索引i。该值不必是最小值,也可以是负数。

多项式时间限制

为了防止廉价的解决方案,只是尝试n=0n=1n=2,等等,你的代码必须在多项式时间在运行中输入的长度。请注意,m输入中的数字具有length Θ(log m),因此m其长度不是多项式。这意味着您最多不能计数m或不能进行一次运算m,但是可以对这些值进行算术运算。

您可能不会使用像一元这样的低效率输入格式来解决此问题。

其他禁令

不允许执行以下操作的内置函数:实现中国余数定理,求解方程式或因数。

您可以使用内置函数查找mod并进行模块化加法,减法,乘法和乘幂运算(使用自然数指数)。您可能不使用其他内置的模块化操作,包括模块化逆运算,除法和查找顺序。

测试用例

这些给出最小的非负解。您的答案可能有所不同。如果直接检查输出是否满足每个约束,可能会更好。

[(5, 3)] 
3

[(7, 2), (5, 4), (11, 0)]
44

[(5, 1), (73, 4), (59, 30), (701, 53), (139, 112)]
1770977011

[(982451653, 778102454), (452930477, 133039003)]
68121500720666070

为什么不划分?
jimmy23013 2015年

@ user23013没有模块化除法,因为它基本上是模块化的逆函数。
xnor

矩阵求逆算作求解方程吗?
瑕疵的

@flawr:我会这样认为。
Alex A.

@xnor:你觉得呢?那么优化功能呢?
瑕疵的

Answers:


9

Mathematica,55 51 45

禁止取模逆,但允许取模幂。根据费马的小定理,n^(-1) % p == n^(p-2) % p

(PowerMod[x=1##&@@#/#,#-2,#]x).#2&@@Thread@#&

例:

In[1]:= f = (PowerMod[x=1##&@@#/#,#-2,#]x).#2&@@Thread@#&;

In[2]:= f[{{5, 3}}]

Out[2]= 3

In[3]:= f[{{7, 2}, {5, 4}, {11, 0}}]

Out[3]= 1584

In[4]:= f[{{5, 1}, {73, 4}, {59, 30}, {701, 53}, {139, 112}}]

Out[4]= 142360350966

只是为了好玩:

ChineseRemainder@@Reverse@Thread@#&

1
您可以通过交换的最里面的函数的参数,订单,这样你可以节省使用一个字节PowerMod[#2,#-2,#],我也并不认为有对功能的要求将被命名,把它降低到48
马丁安德

是的,未命名的功能还可以。
xnor

6

Python 2 165 101 99 98 85字节

像其他答案一样,使用费马小定理。因为我们对最小的解决方案不感兴趣,所以不必将最终总和保持在模块化范围内。感谢Volatility节省了13个字节。

l=input();x=reduce(lambda a,b:a*b[0],l,1)
print sum(x/a*b*pow(x/a,a-2,a)for a,b in l)

[(5, 3)]
3
[(7, 2), (5, 4), (11, 0)]
1584
[(5, 1), (73, 4), (59, 30), (701, 53), (139, 112)]
142360350966

1
您可以在之前删除空格for
isaacg

1
x/a*b*pow(x/a,a-2,a)for a,b in l应该管用。
波动率

好点!我试图摆脱那里明显的冗余,但忘了我可以拆包。
乌里·格兰塔


3

露比129

好的,同志们,似乎Ruby解决方案必须更长一些,因为如果不加载openssl库并进行到OpenSSL :: BN的转换,就无法使用模块化幂运算。尽管如此,还是很有趣的编写它:

require("openssl")
z=eval(gets)
x=1
z.map{|a,b|x*=a}
s=0
z.map{|a,b|e=a.to_bn;s+=(x/a).to_bn.mod_exp(e-2,e).to_i*b*x/a}
puts(s)

呼叫,或时require,不需要括号。evalputs
Tutleman '17

2

Python 2,61

n=P=1
for p,a in input():n+=P*(a-n)*pow(P,p-2,p);P*=p
print n

这采用了产品结构的变体其他答案使用。

想法是遍历约束条件并更新解决方案n以满足当前约束条件,而不会弄乱先前的约束条件。为此,我们跟踪P到现在为止所看到的素数的乘积,并观察到添加倍数对以P任何已经看过的素数为模都没有影响。

因此,我们只需通过添加的正确倍数来进行更改n以满足。我们求解系数:n%p == aPc

(n + P*c) % p == a

这要求c = (a-n) * P^(-1)逆取模p。就像其他人指出的那样,逆可以由费马小定理计算为P^(-1) = pow(P,p-2,p)。所以c = (a-n) * pow(P,p-2,p),我们更新nn+= P * (a-n) * pow(P,p-2,p)


1

Haskell,68 100字节

f l=sum[p#(m-2)*n*p|(m,n)<-l,let a#0=1;a#n=(a#div n 2)^2*a^mod n 2`mod`m;p=product(map fst l)`div`m]

用法:f [(5,1), (73,4), (59,30), (701,53), (139,112)]-> 142360350966

编辑:现在具有快速的“电源/修改”功能。具有内置电源功能的旧版本(68字节):

f l=sum[l#m^(m-2)`mod`m*n*l#m|(m,n)<-l]
l#m=product(map fst l)`div`m

我怀疑您对power-mod的实现不是多项式时间,因为指数在mod之前产生了大量的数字。您是否尝试过最后一个测试用例?
xnor

@xnor:最后一个测试用例在我的2GB机器上几秒钟后用完了内存。我添加了快速电源/调制功能。
nimi 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.