Java 检查int是否更有效地为素数
我最近参加了学校的一个小型java编程竞赛。我和我的搭档刚刚完成了我们的第一节纯面向对象课程,大多数问题都是我们无法回答的,所以我们解决了这个问题(我的意思是):“给定一个输入整数n,返回下一个整数,它是素数,它的反数也是素数,例如,如果n=18,你的程序应该打印31”,因为31和13都是素数。然后,您的.class文件将有一个从1-20000000000的所有可能数字的测试用例传递给它,并且它必须在10秒内返回正确的答案才能被认为是有效的 我们找到了一个解决方案,但对于较大的测试用例,它将花费10秒以上的时间。我相当确定有一种方法可以将循环的范围从n…2000000000向下移动,因为当n是一个低数值时,需要循环的可能性很小,但无论哪种方法,当在两种条件下都找到一个素数时,我们都会打破循环。起初我们从2开始循环,…n不管它有多大,然后我想起了只循环到n的平方根的规则。关于如何使我的程序更有效率,有什么建议吗?我没有上过关于算法复杂性分析的课。这是我们的尝试Java 检查int是否更有效地为素数,java,math,numbers,primes,Java,Math,Numbers,Primes,我最近参加了学校的一个小型java编程竞赛。我和我的搭档刚刚完成了我们的第一节纯面向对象课程,大多数问题都是我们无法回答的,所以我们解决了这个问题(我的意思是):“给定一个输入整数n,返回下一个整数,它是素数,它的反数也是素数,例如,如果n=18,你的程序应该打印31”,因为31和13都是素数。然后,您的.class文件将有一个从1-20000000000的所有可能数字的测试用例传递给它,并且它必须在10秒内返回正确的答案才能被认为是有效的 我们找到了一个解决方案,但对于较大的测试用例,它将花费
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;
}