给定一个整数N和一个表示两个质数的乘积的整数P ,任务是找到模P下N的所有可能平方根(如果存在)。假定P是p1和p2的乘积,其中p1和p2是4i + 3形式的质数,其中i是任何整数。这样的质数的一些示例是(7、11、19、23、31)。
例子:
Input: N = 67, P = 77
Output: 23 65 12 54
Explanation:
All possible answers are (23, 65, 12, 54)
Input: N = 188, P = 437
Output: 25 44 393 412
Explanation:
All possible answers are (25, 44, 393, 412)
天真的方法:
解决此问题的最简单方法是考虑从2到P – 1的所有数字,对于每个数字,检查其是否为模P下N的平方根。如果发现是真的,则打印该数字并中断。
时间复杂度: O(P)
辅助空间: O(1)
高效方法:
为了优化上述方法,我们的想法是使用素因式分解获得两个因子p1和p2 ,然后使用mod P下的Square root求两个因子的平方根。然后使用中国剩余定理找到p1 * p2的平方根模。每个集合将包含两个方程式(因为模p1和p2每个都有两个平方根)。将它们全部结合起来,就得到了四组方程,给出了四个可能的答案。
请按照以下步骤解决此问题:
- 通过使用素数分解对数P进行分解。因素将是两个素数p1和p2 。
- 求平方根模质数p1和p2 。
- 找到两组答案,每个素数一组。每套包含两个数字。
- 因此,如果从每组中选择一个,则将有4组方程。
- 执行中国余数定理,求出平方根模p1 * p2并将其打印出来。
下面是上述方法的实现,其中Java中的BigInteger类用于处理非常大的数字:
Java
// Java program for the above approach
import java.math.*;
import java.util.*;
class SqrtMod {
// Function to find the modulo inverse
// of a with respect to m
static BigInteger modInverse(BigInteger a,
BigInteger m)
{
BigInteger m0 = m;
BigInteger y = new BigInteger("0"),
x = new BigInteger("1");
if (m.compareTo(new BigInteger("1")) == 0)
return new BigInteger("0");
// Perform Extended Euclid Algorithm
while (a.compareTo(new BigInteger("1")) > 0) {
if (m.toString().equals("0"))
break;
BigInteger q = a.divide(m);
BigInteger t = m;
// M is remainder now, process
// Extended Euclid Algorithm
m = a.mod(m);
a = t;
t = y;
y = x.subtract(q.multiply(y));
x = t;
}
// Make x positive
while (x.compareTo(new BigInteger("0")) < 0)
x = x.add(m0);
return x;
}
// Function to apply the Chinese
// Remainder Theorem
static String CRT(BigInteger num[],
BigInteger rem[],
int k)
{
BigInteger prod = new BigInteger("1");
// Find product of all numbers
for (int i = 0; i < k; i++)
prod = prod.multiply(num[i]);
BigInteger result = new BigInteger("0");
// Apply the above formula
for (int i = 0; i < k; i++) {
BigInteger pp = prod.divide(num[i]);
result = result.add(rem[i]
.multiply(modInverse(pp,
num[i]))
.multiply(pp));
}
// Return result
return result.mod(prod).toString();
}
// Function to perform modular
// exponentiation
static BigInteger powMod(BigInteger base1,
BigInteger exponent,
BigInteger modulus)
{
BigInteger result = new BigInteger("1");
base1 = base1.mod(modulus);
while (exponent
.compareTo(new BigInteger("0"))
> 0) {
if (exponent
.mod(new BigInteger("2"))
.equals(new BigInteger("1")))
result = (result.multiply(base1))
.mod(modulus);
exponent = exponent
.divide(new BigInteger("2"));
base1 = base1
.multiply(base1)
.mod(modulus);
}
// Return the result
return result;
}
// Function that returns square root
// of n under modulo p if exists
static BigInteger squareRoot(
BigInteger n,
BigInteger p)
{
// For Invalid Input
if (!p.mod(new BigInteger("4"))
.equals(new BigInteger("3"))) {
System.out.print("Invalid Input");
return new BigInteger("-1");
}
n = n.mod(p);
// Try "+(n^((p + 1)/4))"
BigInteger x = powMod(n,
(p.add(new BigInteger("1")))
.divide(new BigInteger("4")),
p);
if ((x.multiply(x)).mod(p).equals(n)) {
return x;
}
// Try "-(n ^ ((p + 1)/4))"
x = p.subtract(x);
if ((x.multiply(x)).mod(p).equals(n)) {
return x;
}
// If none of above two work,
// then sqrt doesn't exist
return new BigInteger("-1");
}
// Function to find the ceiling
// of square root of a number
public static BigInteger
sqrtC(BigInteger x)
{
// If number < zero
if (x.compareTo(BigInteger.ZERO) < 0) {
return new BigInteger("-1");
}
// If number is zero or 1
if (x == BigInteger.ZERO
|| x == BigInteger.ONE) {
return x;
}
BigInteger two
= BigInteger.valueOf(2L);
BigInteger y;
// Iterate to find the square root
for (y = x.divide(two);
y.compareTo(x.divide(y)) > 0;
y = ((x.divide(y)).add(y)).divide(two))
;
if (x.compareTo(y.multiply(y)) == 0) {
return y;
}
else {
return y.add(BigInteger.ONE);
}
}
// Function to factorise number P
static BigInteger[] factorise(BigInteger p)
{
BigInteger ans[] = new BigInteger[2];
BigInteger b = new BigInteger("2");
BigInteger ONE = new BigInteger("1");
BigInteger ZERO = new BigInteger("0");
// Iterate over [2, sqrt(N)]
for (; b.compareTo(
sqrtC(p).add(ONE))
< 0;
b = b.add(ONE)) {
// Check if mod is zero
if (p.mod(b).equals(ZERO)) {
ans[0] = b;
ans[1] = p.divide(b);
}
}
// Return the ans
return ans;
}
// Function to find the all possible
// squareRoot of a under modulo m
public static void
solve(BigInteger a, BigInteger m)
{
// Find factorization of m
BigInteger s[] = factorise(m);
BigInteger ZERO = new BigInteger("0");
// If number P is not product
// of two primes
if (!s[0].multiply(s[1]).equals(m)) {
System.out.println("Not a product "
+ "of two primes");
}
// Find the numbers
else {
BigInteger s1[] = new BigInteger[4];
BigInteger a1[] = new BigInteger[4];
BigInteger a2[] = new BigInteger[2];
// Find squareRoot
a1[0] = squareRoot(a.mod(s[0]), s[0]);
a1[1] = squareRoot(a.mod(s[1]), s[1]);
a1[2] = a1[0].multiply(new BigInteger("-1"));
a1[3] = a1[1].multiply(new BigInteger("-1"));
// Compare to Zero
while (a1[2].compareTo(ZERO) < 0)
a1[2] = a1[2].add(s[0]);
while (a1[3].compareTo(ZERO) < 0)
a1[3] = a1[3].add(s[1]);
s1[0] = s[0];
s1[1] = s[1];
s1[2] = s[0];
s1[3] = s[1];
// Condition for no solution
if (a1[0].equals(new BigInteger("-1"))
|| a1[1].equals(new BigInteger("-1"))) {
System.out.println("No Solution");
}
// Else print all possible answers
else {
// Perform Chinese Remainder
// Theorm and print numbers
System.out.print(
CRT(s, a1, 2) + " ");
a2[0] = a1[0];
a2[1] = a1[3];
System.out.print(
CRT(s, a2, 2) + " ");
a2[0] = a1[2];
a2[1] = a1[1];
System.out.print(
CRT(s, a2, 2) + " ");
a2[0] = a1[2];
a2[1] = a1[3];
System.out.print(
CRT(s, a2, 2) + " ");
}
}
}
// Driver Code
public static void
main(String[] args) throws Exception
{
// Given Number N
BigInteger N = new BigInteger("188");
// Given product of Prime Number
BigInteger P = new BigInteger("437");
// Function Call
solve(N, P);
}
}
25 44 393 412
时间复杂度: O(√P+ logN)
辅助空间: O(1)