给定范围[1,1000000000],我们需要找到素数,并且素数的所有数字都必须为奇数。 (例如:23没问题,31没问题) 如果我们通过遍历每个数字并检查它是否为质数等来进行,则速度非常慢。有没有办法使它接近O(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;
}
也许有一个技巧可以确保快速进行素数测试,但我找不到。有什么建议么?感谢您的阅读。
答案 0 :(得分:2)
您不需要检查所有的毫安数字。生成仅包含奇数位的所有数字-最多有5 ^ 9〜2百万个数字。排除那些以5结尾并且没有生成被3整除的数字(在最后一位数字生成的那一刻)
然后检查这些数字的素性。请注意,循环限制可能是sqrt(n)
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计算之前首先检查奇数位