查找大范围内具有奇数位的质数的算法

时间:2018-12-20 07:08:58

标签: java algorithm optimization

给定范围[1,1000000000],我们需要找到素数,并且素数的所有数字都必须为奇数。 (例如:23没问题,31没问题) 如果我们通过遍历每个数字并检查它是否为质数等来进行,则速度非常慢。有没有办法使它接近O(N)? 我尝试通过首先查看数字来消除尽可能多的错误。但是在消除了偶数后,素数测试太慢了。

  • 对于[1,N]中的所有数字
  • 检查所有数字,如果有偶数,请继续
  • 检查素数(此步骤非常慢)

素数测试不应太复杂(不可能进行概率等,必须在几分钟内实现)。我使用的是:

    private static boolean isPrime(int n) {
        boolean isPrime = true;
        for (int divisor = 2; divisor <= n / 2; divisor++) {
            if (n % divisor == 0) {
                isPrime = false;
                break;
            }
        }

        return isPrime;
   }

也许有一个技巧可以确保快速进行素数测试,但我找不到。有什么建议么?感谢您的阅读。

4 个答案:

答案 0 :(得分:2)

您不需要检查所有的毫安数字。生成仅包含奇数位的所有数字-最多有5 ^ 9〜2百万个数字。排除那些以5结尾并且没有生成被3整除的数字(在最后一位数字生成的那一刻)

然后检查这些数字的素性。请注意,循环限制可能是sqrt(n)

Ideone

class Ideone
{
   static int oddcnt;

        public static void checkprime(int x) {
           for (int i=3; i <= Math.sqrt(x); i +=2)
              if ((x % i) == 0)
                 return;
             oddcnt++;
        }


    public static void genodd(int x, int curlen, int maxlen) {
        x *= 10;
        for (int i=1; i<10; i+=2) {
            int nx = x + i;
            checkprime(nx);
            if (curlen < maxlen)
                genodd(nx, curlen + 1, maxlen);
        } 
    }

    public static void main (String[] args) throws java.lang.Exception
    {

    genodd(0, 1, 8);
    System.out.println(oddcnt);
    }
}

答案 1 :(得分:1)

如果数字顺序正确,则可以优化isPrime函数。

这是一个js示例版本。

var primes = [];
function checkDigits(n) {
	while(n > 1) {
		var d = n % 10;
		if ( d % 2 == 0) { return false; }
		n = parseInt(n/10,10);
	}
	return true;
}
function isPrime(n) {
    for(var i = 1; i < primes.length; i++) {
       if(n % primes[i] == 0) {
          return false;
       }
    }
    var lastPrime = primes.length > 2 ? primes[primes.length - 1] : 1; 
    var inc = 2;
    for(var i = lastPrime + inc; i < Math.sqrt(n); i += inc) {
       if(n % i == 0) {
           return false;
       }
    }
    primes.push(n);
    return true;
}

for(var i = 1; i < 100; i++) {
  if(checkDigits(i) && isPrime(i)) {
    console.log(i);
  }
}

答案 2 :(得分:1)

我能想到的最好的方法是运行Eratosthenes的Prime筛子来查找范围(0; sqrt(1000000000))-大约为(0,31622)-和时间复杂度O(n * log(log(n)))其中n = 31622。我们将需要那些素数来进行更快的素数测试。

然后,只需遍历每个带有奇数位的数字-就有5 ^ 10 = 9765625〜10000000这样的数字。与遍历原始范围内的所有数字相比,您节省了1000次。

使用我们在步骤1中找到的素数进行素数测试可以很快,因为您只需要检查素数


以下是Java实现

public class Execute {
    private ArrayList<Long> primes = new ArrayList<>();

    @org.junit.Test
    public void findOddDecimalPrimes() {
        primeSieve(32000);
        System.out.println(primes.size());
        for (int i = 0; i < 9765625; i++) {
            String inBase5 = convertFromBaseToBase(i);
            long evenDec = convertToOddDecimal(inBase5);
            if (isPrime(evenDec)) {
                System.out.println(evenDec);
            }
        }
    }

    private String convertFromBaseToBase(long i) {
        return Long.toString(i, 5);
    }

    private long convertToOddDecimal(String str) {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            s.append(1 + 2 * Integer.parseInt("" + str.charAt(i)));
        }
        return Long.parseLong(s.toString());
    }

    private boolean isPrime(long n) {
        for (int i = 0; i < primes.size(); i++) {
            if (primes.get(i) * primes.get(i) > n) break;
            long divisor = n / primes.get(i);
            if (divisor * primes.get(i) == n) return false;
        }
        return true;
    }

    /**
     * References: www.geeksforgeeks.org
     */
    private void primeSieve(int n)
    {
        // Create a boolean array "prime[0..n]" and initialize
        // all entries it as true. A value in prime[i] will
        // finally be false if i is Not a prime, else true.
        boolean prime[] = new boolean[n+1];
        for(int i=0;i<n;i++)
            prime[i] = true;

        for(int p = 2; p*p <=n; p++)
        {
            // If prime[p] is not changed, then it is a prime
            if(prime[p] == true)
            {
                // Update all multiples of p
                for(int i = p*p; i <= n; i += p)
                    prime[i] = false;
            }
        }

        for (int i = 2; i < prime.length; i++) {
            if (prime[i]) this.primes.add(Long.valueOf(i));
        }
    }
}

答案 3 :(得分:0)

这通常是数学和计算机科学领域的一个开放性问题。 简而言之,没有已知的方法可以解决O(1)中的问题,以使您的循环在整个范围内以O(N)运行。

如果您解决了这一问题,请不要告诉任何人,并通过破坏当今使用大质数的大多数加密来致富。

您可以做的是,使用sqrt(n)使除数的循环变小。

这将使内部循环从O(N ^ 2)降为O(sqrt(N))

从O(N ^ 2)到O(N * sqrt(N))= O(N ^(3/2))的整个复杂度

注释器优化将是在执行复杂的Prime计算之前首先检查奇数位