Java 检查int是否更有效地为素数

Java 检查int是否更有效地为素数,java,math,numbers,primes,Java,Math,Numbers,Primes,我最近参加了学校的一个小型java编程竞赛。我和我的搭档刚刚完成了我们的第一节纯面向对象课程,大多数问题都是我们无法回答的,所以我们解决了这个问题(我的意思是):“给定一个输入整数n,返回下一个整数,它是素数,它的反数也是素数,例如,如果n=18,你的程序应该打印31”,因为31和13都是素数。然后,您的.class文件将有一个从1-20000000000的所有可能数字的测试用例传递给它,并且它必须在10秒内返回正确的答案才能被认为是有效的 我们找到了一个解决方案,但对于较大的测试用例,它将花费

我最近参加了学校的一个小型java编程竞赛。我和我的搭档刚刚完成了我们的第一节纯面向对象课程,大多数问题都是我们无法回答的,所以我们解决了这个问题(我的意思是):“给定一个输入整数n,返回下一个整数,它是素数,它的反数也是素数,例如,如果n=18,你的程序应该打印31”,因为31和13都是素数。然后,您的.class文件将有一个从1-20000000000的所有可能数字的测试用例传递给它,并且它必须在10秒内返回正确的答案才能被认为是有效的

我们找到了一个解决方案,但对于较大的测试用例,它将花费10秒以上的时间。我相当确定有一种方法可以将循环的范围从n…2000000000向下移动,因为当n是一个低数值时,需要循环的可能性很小,但无论哪种方法,当在两种条件下都找到一个素数时,我们都会打破循环。起初我们从2开始循环,…n不管它有多大,然后我想起了只循环到n的平方根的规则。关于如何使我的程序更有效率,有什么建议吗?我没有上过关于算法复杂性分析的课。这是我们的尝试

public class P3
{

   public static void main(String[] args){

    long loop = 2000000000;
    long n = Integer.parseInt(args[0]);
    for(long i = n; i<loop; i++)
    {
      String s = i +"";
      String r = "";
      for(int j = s.length()-1; j>=0; j--)
        r = r + s.charAt(j);
      if(prime(i) && prime(Long.parseLong(r)))
      {
          System.out.println(i);
          break;
      }
    }
    System.out.println("#");
  }

  public static boolean prime(long p){


for(int i = 2; i<(int)Math.sqrt(p); i++)
    {
      if(p%i==0)
        return false;
    }
    return true;
  }
}
公共类P3
{
公共静态void main(字符串[]args){
长循环=2000000000;
long n=Integer.parseInt(args[0]);
对于(长i=n;i=0;j--)
r=r+s.charAt(j);
if(prime(i)和prime(Long.parseLong(r)))
{
系统输出打印LN(i);
打破
}
}
System.out.println(“#”);
}
公共静态布尔素数(长p){

对于(int i=2;i来说,你似乎在增加1,但你应该增加2。没有偶数是素数,而是2。

首先,你应该使用类似的方法预计算所有素数,最大为2000000000。你可以将每个数是否为素数存储在一个位数组中

这相当快,然后检查每个数字的素性是一个简单的查找


如果你不能这样做,因为你需要为每个测试用例运行一个新的程序实例,那么使用一个快速的素性测试算法,比如。

这里最大的低效是你的素性测试方法
prime
。考虑一下它必须测试相同的数字的次数,并集中精力考虑如何使用ad内存结构的优势,以避免某些重复计算。

您的素数检查效率非常低。事实上,您不需要重新检查已检查数的倍数。因此,当您检查%2时,您不需要检查%4

要确定一个数字是否是素数,你只需尝试将其除以所有已知素数,直到达到要检查的数字的平方根。这样做可以大大减少除数:如果你的应用程序有一个从2..~44721的素数列表(例如,作为准备步骤计算),您可以很快检查所有数字,直到2000000000

此外,您应该确保先检查两种排列中较小的排列(例如,在您的样本中,先检查13,然后检查31)

编辑:

下面是我用C#快速编写的一个示例(您需要做一些小的语法更改才能在Java上运行它,但我手头有一个C#编译器):

公共静态长反转(长值){
长结果=0;
而(值>0){
结果=结果*10+(值%10);
数值/=10;
}
返回结果;
}
public static long[]knownPrimes=新长[1000000];
公共静态int knownPrimeCount=0;
公共静态bool isPrime(长值){
//我们循环遍历所有已知的素数,并尝试用这些素数进行除法(有点像筛子)
对于(int primeIndex=0;primeIndex非素数
返回false;
}
如果((primeToCheck*primeToCheck)>值){
//平方超过值->为素数,无需更多检查
返回true;
}
}
//如果我们来到这里,我们已经用完了要检查的素数,因此我们应该将其指示为错误
抛出新的ArgumentException(string.Format(“{0}”太大,无法对照已知的素数、value和value进行检查);
}
公共静态void Main(字符串[]args){
长循环=2000000000;
长n=1999990000;
//首先,我们初始化最终计算可能需要的所有素数
KnownPimes[KnownPimeCount++]=2;
for(长i=3;真;i++){
如果(i){
//存储新的prime
KnownPimes[KnownPimeCount++]=i;
如果((i*i)>循环){
break;//我们现在有了所有需要的素数
}
}
}
//现在我们尝试寻找匹配项

对于(长时间i=n;i使用,可以显著减少您需要非常有效地检查的病例数量

我以前没有这样做过,但我想到了一些事情

如果你的平方根是一个整数,那么这个数字就不是素数

如果数字以0、2、4、5、6或8结尾,则它不是素数/除了2本身

若数字之和可以被3整除,那个么数字可以被3整除 如果总和为9,则乘以9

我不知道这方面的测试是否对你有帮助,至少平方根测试应该有帮助,因为你无论如何都要计算它,而且你已经可以完成了

哦,当然,若你们做米勒-拉宾素性测试,你们的效率会大大提高。
您的实际测试只需要在不确定的情况下进行。

您可以在
main
中进行的另一个速度改进是将循环更改为预过滤一些复合数字,将一些迭代展开为一系列测试。最简单的方法是
public static long reverse(long value) {
    long result = 0;
    while (value > 0) {
        result = result*10+(value%10);
        value /= 10;
    }
    return result;
}

public static long[] knownPrimes = new long[1000000];
public static int knownPrimeCount = 0;

public static bool isPrime(long value) {
    // we loop through all already known primes and try to divide by those (sieve sort of)
    for (int primeIndex = 0; primeIndex < knownPrimeCount; primeIndex++) {
        long primeToCheck = knownPrimes[primeIndex];
        if (value % knownPrimes[primeIndex] == 0) {
            // can be divided by the given prime -> not a prime
            return false;
        }
        if ((primeToCheck * primeToCheck) > value) {
            // square exceeds the value -> is a prime, no more checks needed
            return true;
        }
    }
    // if we come here, we've run out of primes to check against, and therefore we should indicate this as error
    throw new ArgumentException(string.Format("{0} is too large to be checked against known primes", value), "value");
}

public static void Main(String[] args){
    long loop = 2000000000;
    long n =    1999990000;

    // first we initialize all the primes we may be needing for the final computation
    knownPrimes[knownPrimeCount++] = 2;
    for (long i = 3; true; i++) {
        if (isPrime(i)) {
            // store the new prime
            knownPrimes[knownPrimeCount++] = i;
            if ((i * i) > loop) {
                break; // we have all the primes we need now
            }
        }
    }

    // now we try to find matches
    for (long i = n; i <= loop; i++) {
        long reversed = reverse(i);
        if ((reversed <= i) && isPrime(reversed) && isPrime(i)) {
            Console.WriteLine("{0} <-> {1}", i, reversed);
        }
    }
    Console.WriteLine("#");
    Console.ReadKey(true);
}
typedef uint64_t BigIntT;
typedef int64_t SBigIntT;

// This function calculations the power of b^e mod phi
// As long as 
//      b*b is smaller than max(BigIntT) 
//      b*phi is smaller than max(BigIntT)
// we will not have overflow.
BigIntT calculatePower (BigIntT b, BigIntT e, BigIntT m) {
    BigIntT result = 1;

    while (e != 0) {
        if (e & 1) {
            result = (result * b) % m;
        }

        e = e >> 1;
        b = (b * b) % m;
    }

    return result;
}

// This function implements simple jacobi test.
// We can expect compiler to perform tail-call optimisation.
SBigIntT jacobi (SBigIntT a, SBigIntT b) {
    if (a == 0 || a == 1) {
        return a;
    } else if (a % 2 == 0) {
        if (((b*b - 1) / 8) % 2 == 0) {
            return jacobi(a/2, b);
        } else {
            return -jacobi(a/2, b);
        }
    } else if ((((a-1) * (b-1)) / 4) % 2 == 0) {
        return jacobi(b % a, a);
    } else {
        return -jacobi(b % a, a);
    }
}

// This function implements : http://en.wikipedia.org/wiki/Solovay-Strassen_primality_test
bool testPrime (BigIntT p) {
    int tests = 10;

    if (p == 2) {
        return true;
    }

    while (tests-- > 0) {
        BigIntT a = generateRandomNumber(2, p);

        if (greatestCommonDivisor(a, p) == 1) {
            BigIntT l = calculatePower(a, (p-1)/2, p);
            SBigIntT j = jacobi(a, p);

            // j % p == l
            if ((j == -1) && (l == p-1) || (j == l)) {
                // So far so good...
            } else {
                // p is composite
                return false;
            }
        } else {
            // p is composite
            return false;
        }
    }

    return true;
}