这个问题是在采访中问的(关于素数)
Russian Doll Primes

它们通常被称为可截断素数。
我在Wiki上找到了此代码

public static void main(String[] args){

    final int MAX = 1000000;

    //Sieve of Eratosthenes (using BitSet only for odd numbers)
    BitSet primeList = new BitSet(MAX>>1);
    primeList.set(0,primeList.size(),true);

    int sqroot = (int) Math.sqrt(MAX);
    primeList.clear(0);
    for(int num = 3; num <= sqroot; num+=2)
    {
        if( primeList.get(num >> 1) )
        {
            int inc = num << 1;
            for(int factor = num * num; factor < MAX; factor += inc)
            {
                //if( ((factor) & 1) == 1)
                //{
                primeList.clear(factor >> 1);
                //}
            }
        }
    }


    //Find Largest Truncatable Prime. (so we start from 1000000 - 1
    int rightTrunc = -1, leftTrunc = -1;
    for(int prime = (MAX - 1) | 1; prime >= 3; prime -= 2)
    {
        if(primeList.get(prime>>1))
        {
            //Already found Right Truncatable Prime?
            if(rightTrunc == -1)
            {
                int right = prime;
                while(right > 0 && primeList.get(right >> 1)) right /= 10;
                if(right == 0) rightTrunc = prime;
            }

            //Already found Left Truncatable Prime?
            if(leftTrunc == -1 )
            {
                //Left Truncation
                String left = Integer.toString(prime);
                if(!left.contains("0"))
                {
                    while( left.length() > 0 ){
                        int iLeft = Integer.parseInt(left);
                        if(!primeList.get( iLeft >> 1)) break;
                        left = left.substring(1);
                    }
                    if(left.length() == 0) leftTrunc = prime;
                }
            }
            if(leftTrunc != -1 && rightTrunc != -1) //Found both? then Stop loop
            {
                break;
            }
        }
    }
    System.out.println("Left  Truncatable : " + leftTrunc);
    System.out.println("Right Truncatable : " + rightTrunc);
}


这给出了输出:

Left  Truncatable : 998443
Right Truncatable : 796339


但是我无法解决这个特殊的俄罗斯玩偶素数问题,例如,如果您有素数,并且删除了该素数的左数或右数,那么该结果数是否是素数?

我对此并不陌生,所以请原谅任何错误。

最佳答案

让我们从头开始:

根据您提供的问题链接:


  “俄罗斯娃娃素是
  素数,其右数位可以重复删除,并且是
  还是素数。”


我假设您有一个函数boolean isPrime(int)来确定数字是否为质数。

谷歌搜索,我们从Wikipedia中发现,可以右截断的质数的数目最多为73,939,133,该数目为83,这使蛮力成为可行的选择;但可以在此处采用一些优化技术:


由于我们进行了右截断,因此我们可以安全地消除偶数(因为任何偶数都不是素数,因此在其上生成的任何数字都绝不会是俄罗斯娃娃素数)。
由于任何以5开头的数字都可以被5整除,因此根据我在前面提到的相同规则,我们可以消除5。


剩下的数字包含1、3、7和9。

现在,如果我们想生成这四个数字的所有可能组合,且不超过您提到的最大值(1,000,000),则只需进行4,096次迭代。

这种技术的缺点是我们现在有4,096个数字,其中包含可能的非素数,或者由非素数形成的素数,因此不是俄罗斯玩偶素数。我们可以通过遍历并检查这些数字来消除它们。或者更好的是,我们可以更仔细地研究俄罗斯娃娃的素数。

在检查了我从上面的链接中引用的规则后,我们发现俄罗斯玩偶素数是素数,其右位数字可以重复删除,并且仍然是素数(因此仍然是俄罗斯玩偶素数,反复给定单词)!

这意味着我们可以使用最小的一位俄罗斯玩偶素数进行运算,也可以使用上面使用的生成魔术,并且由于由俄罗斯玩偶素数形成的任何素数都是俄罗斯玩偶素数,因此我们可以消除非素数尽早产生了俄罗斯玩偶素数的完整清单,同时大大减少了此类程序的运行时间。

看一下下面的生成代码:

void russianDollPrimesGeneration(int x) {
    x *= 10;
    if (x * 10 >= 1000000) return;
    int j;
    for (int i=1; i<=9; i+=2) {
        if (i == 5) continue;
        j = x + i;
        if (isPrime(j)) {
            addToRussianDollPrimesList(j);
            russianDollPrimesGeneration(j);
        }
    }
}


前提是void addToRussianDollPrimesList(int x)是一个将x添加到我们先前保存的用于存储俄语娃娃素数的列表的函数。

更新说明

请注意,您可以在void russianDollPrimesGeneration(int x)函数的if条件内调用对void addToRussianDollPrimesList(int x)的调用,因为每当调用前一个函数时,我们总是会使用相同的参数来调用后一个函数。我在这里将它们分开以强调生成函数的递归性质。

另请注意,您必须使用整数0运行此函数。

最后要注意的是,在许多情况下,即使上面的生成函数void russianDollPrimesGeneration(int x)是Russian Doll Prime,也不会算在内。

还记得我们省略2和5时,因为偶数和被5除的数不能是素数,所以俄罗斯娃娃素数也不能吗?因此不能组成俄罗斯娃娃素体?好吧,这种情况不适用于2和5,因为它们是素数,并且由于它们是个位数,因此它们是俄罗斯娃娃素数,并且如果放在左边(如23),则有资格形成俄罗斯娃娃素数和53。

那么,如何更正我们的代码以包括这些特殊情况呢?

我们可以创建一个包装函数,将这两个数字相加并检查可以使用它们形成的Russian Doll Prime(这与我们上面使用的生成函数相同)。

void generationWrapperFunction(int x) {
    addToRussianDollPrimesList(2);
    russianDollPrimesGeneration(2);
    addToRussianDollPrimesList(5);
    russianDollPrimesGeneration(5);
    russianDollPrimesGeneration(0);
}


结束更新说明

这个小功能将产生一个俄罗斯娃娃素数列表,然后可以在其中搜索我们要寻找的数字。

以下递归函数是一个替代方法,但我认为它将更耗时:

boolean isRussianDollPrime(int n) {
    if (!isPrime(n)) return false;
    if (n < 10) return true;
    return isRussianDollPrime(n / 10);
}


可以修改此函数以使用可左截断的素数。但是,对于左截短的素数,基于世代的解决方案将很难实施。

关于java - 俄罗斯娃娃素,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25227098/

10-10 09:04