📜  在Modulo p下找到平方根| (当p是两个素数的乘积,形式为4 * i + 3)

📅  最后修改于: 2021-04-27 20:39:37             🧑  作者: Mango

给定一个整数N和一个表示两个质数的乘积的整数P ,任务是找到模PN的所有可能平方根(如果存在)。假定Pp1p2的乘积,其中p1和p2是4i + 3形式的质数,其中i是任何整数。这样的质数的一些示例是(7、11、19、23、31)。

例子:

天真的方法:
解决此问题的最简单方法是考虑从2P – 1的所有数字,对于每个数字,检查其是否为模PN的平方根。如果发现是真的,则打印该数字并中断。
时间复杂度: O(P)
辅助空间: O(1)

高效方法:
为了优化上述方法,我们的想法是使用素因式分解获得两个因子p1p2 ,然后使用mod P下的Square root求两个因子的平方根。然后使用中国剩余定理找到p1 * p2的平方根模。每个集合将包含两个方程式(因为模p1p2每个都有两个平方根)。将它们全部结合起来,就得到了四组方程,给出了四个可能的答案。
请按照以下步骤解决此问题:

  • 通过使用素数分解对数P进行分解。因素将是两个素数p1p2
  • 求平方根模质数p1p2
  • 找到两组答案,每个素数一组。每套包含两个数字。
  • 因此,如果从每组中选择一个,则将有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)