找到素数的方法比这更简单吗?

时间:2013-01-02 13:29:18

标签: java variables loops instance

是否有更强大,更有效,更清洁/更优雅的方式来查找素数?代码工作正常,但我只是写了一些对我来说最合乎逻辑的东西,我无法弄清楚任何其他方式,但说实话,它看起来并不好看:P。我知道编码不是最优雅的活动。

这是我的主要方法:

import java.util.Scanner;
public class DisplayPrimeNumbers
{
    public static void main(String[] args)
    {
        Scanner scan = new Scanner(System.in);
        System.out.print("Enter an integer that you'd like the system to print the prime numbers till: ");
        String input1 = scan.nextLine();
        int input = Integer.parseInt(input1);

        PrimeGenerator prime = new PrimeGenerator(input);

        for (int i = 1; i < input ; i++)
        {
            if(prime.isPrime())
            {
            System.out.println(prime.getNextPrime());
            }
        }
        System.out.println(1);

    }
}

这是我的班级:

public class PrimeGenerator 
{
    private int number;

    public PrimeGenerator(int n)
    {
        number = n;
    }

    public int getNextPrime ()
    {
        return number+1;
    }


    public boolean isPrime()
    {
        for(int i = 2; i < number; i++)
        {
            if (number % i == 0)
            {
                number--;
                return false;
            }
        }
    number--;   
    return true;    
    }
} 

9 个答案:

答案 0 :(得分:5)

虽然这个问题已经得到解答,但我认为无论如何我都会提供我的答案,希望有人觉得它有用:

您似乎主要关注优雅和效率。我还想指出正确性同样重要。除非你有特殊要求将1号视为素数,否则不再这样认为。您应该同样考虑用户输入素数时的情况。您还应该考虑一下您打印的数字的边界条件。特别是如果我输入数字7,您的用户是否希望它输出5,3,2,1或7,5,3,2,1。虽然我的个人倾向是针对后者,但使用简洁明了的信息可以使任一选项都有效。

您的解决方案缺乏优雅主要归功于您将两个概念结合起来: Prime Number Testing Prime Number Generation

素数测试是一种(快速)方法,用于确定单个任意选择的数字是否为素数。 素数生成器是一种生成素数序列的方法,这些素数通常是连续的。

正如您的程序演示的那样,您可以通过测试给定范围内的每个数字并仅选择那些素数来生成连续的素数序列!将此作为我们当前的基本策略,让我们弄清楚代码可能是什么:

根据我们之前的描述,我们说素数测试是一种方法(也就是函数)来确定一些任意选择的数字是否为素数。所以这个方法应该作为输入a(n任意选择)的数字并返回或者不给定的数量是素数(即:真/假)。让我们看看它的外观:

public interface PrimeNumberTest
{
    bool isPrime(int value);
}

并结合您的素数测试

public class BruteForcePrimeNumberTester : PrimeNumberTest
{
    public bool isPrime(int value)
    {
        bool isPrime = true;

        for(int i = 2; isPrime && i < value; i++)
        {
            if (value % i == 0)
            {
                isPrime = false;
            }
        }

        return isPrime;
    }
}

然后,您的主程序负责迭代每个数字并仅打印素数测试识别为素数的thsoe。

public static void main(String[] args)
{
    //Determine the range of prime numbers to print
    Scanner scan = new Scanner(System.in);
    System.out.print("Primes smaller than what number should be printed?: ");
    int max = Integer.parseInt(scan.nextLine());

    //Identify how prime numbers will be tested
    PrimeNumberTest test = new BruteForcePrimeNumberTest();

    //Uncomment the line below if you want to include the number 1. Favour adding it here so that you may
    //use re-use your prime number test elsewhere that atually needs to know if a number is prime.
    //System.out.println(1);

    //Print the prime numbers
    for (int i = 2; i < max ; i++)
    {
        if(test.isPrime(i))
        {
            System.out.println(i);
        }
    }
}

然而,您的主程序应该只关注素数生成。它并不真正关心如何生成那些素数的语义我们只想要素数。如果通过素性测试或任何其他算法找到素数并不重要。所以我们问自己素数生成器是什么样的?

对于启动器素数总是整数,所以我们不应该将它们存储在浮点数,双精度数或小数数内。留下32位和64位整数。如果你想生成更大的素数,那么显然你应该使用long类型,但我只是要使用int。在其他语言中,我们也必须考虑无符号数字之类的东西。

现在我们需要找到一种方法一次性返回所有这些数字。由于我们将要生成一个连续的序列,树木确实没有意义。堆栈没有意义,因为消费者通常希望数字按生成顺序排列。可以使用队列,因为它们符合先进先出规则。实际上,如果最终应用程序具有异步素数生成器(生产者)和单独的异步消费者,则此类型将是理想的。对于这个例子,我想要一些只读的东西。本质上,素数生成器是Iterable<int>

public class PrimeNumberTestGenerator : Iterable<int>
{
    private int limit;
    private PrimalityTester tester;

    public PrimeNumberTestGenerator(PrimalityTester tester, int limit)
    {
        this.tester = tester;
        this.limit = limit;
    }

    private class PrimeNumberIterator : Iterator<int>
    {
        private int current;

        public PrimeNumberIterator()
        {
        }

        public bool hasNext()
        {
            return next < limit;
        }

        public int moveNext()
        {
            if (!hasNext())
            {
                throw new NoSuchElementException();
            }

            int result = next;

            do
            {
                next++;
            } while(hasNext() && !tester.isPrime(next));


            return result;
        }

        public void remove()
        {
            throw new UnsupportedOperationExecution();
        }
    }

    public Iterator<int> iterator()
    {
        return new PrimeNumberIterator();
    }
}

那么我们如何将它们联系在一起?

public static void main(String[] args)
{
    //Determine the range of prime numbers to print
    Scanner scan = new Scanner(System.in);
    System.out.print("Primes smaller than what number should be printed?: ");
    int max = Integer.parseInt(scan.nextLine());

    //Identify how prime numbers will be tested
    Iterable<int> primes = new PrimeNumberTestGenerator(max, new BruteForcePrimeNumberTest());

    //Print the prime numbers
    foreach (int prime : primes)
    {
        System.out.println(prime);
    }
}

效率

现在问题的另一面是确定指定范围内素数的有效方法。虽然快速的互联网搜索应该产生许多不同的“快速”算法,用于确定一组比蛮力方式更紧固的素数。其中一种方法是阿特金筛选:

public class AtkinSieve : Iterable<int>
{
    private BitSet primes;

    public AtkinSieve(int limit)
    {
        primes = new BitSet(limit);

        int root = (int)Math.sqrt(limit);

        primes.set(2);
        primes.set(3);

        //this section can be further optimized but is the approach used by most samples
        for (int x = 1; x <= root; x++)
        {
            for (int y = 1; y <= root; y++)
            {
                int number;
                int remainder;


                number = (4 * x * x) + (y * y);
                remainder = number % 12;
                if (number < limit && (remainder == 1 || remainder == 5))
                {
                    primes.flip(number);
                }

                number = (3 * x * x) + (y * y);
                remainder = number % 12;
                if (number < limit && remainder == 7)
                {
                    primes.flip(number);
                }

                if (x < y)
                {
                    number = (3 * x * x) - (y * y);
                    remainder = number % 12;
                    if (number < limit && remainder == 11)
                    {
                        primes.flip(number);
                    }
                }
            }
        }

        for (int i = 5; i <= root; i++)
        {
            if (primes.get(i))
            {
                int square = i * i;
                for (int j = square; j < limit; j += square)
                {
                    primes.clear(j);
                }
            }
        }
    }
}

public class SetBitIterator : Iterator<int>
{
    private BitSet bits;
    private int next;
    private bool isReadOnly;

    public SetBitIterator(BitSet bits)
    {
        this.bits = bits;
        next = bits.nextSetBit(0);   
    }

    public bool hasNext()
    {
        return next <> -1;
    }

    public int moveNext()
    {
        int result = next;

        next = bits.nextSetBit(next);

        return result;
    }

    public void remove()
    {
        throw new UnsupportedOperationException();
    }
}

我们现在只需更改前一个主程序中的一行,就可以使用这个素数生成器!

变化:

//Identify how prime numbers will be tested
Iterable<int> primes = new PrimeNumberTestGenerator(max, new BruteForcePrimeNumberTest());

要:

//Identify how prime numbers will be tested
Iterable<int> primes = new AtkinSieve(max);

答案 1 :(得分:3)

  1. 您可以通过在PrimeGenerator内存储已在私人收藏中找到的素数来加快搜索新素数的速度。通过仅尝试将它们作为潜在的除数而不是for(int i = 2; i < number; i++)循环,您将不得不做更少的划分
  2. 你可以停止&#34;找到除数&#34;在到达number之前循环好:具体来说,当你的候选除数超过目标数的平方根时,你可以停止。这是有效的,因为你按升序尝试候选除数:如果在平方根之上有除数,则除法的结果将低于平方根,所以你已经找到了它们。
  3. 您的getNextPrime方法应在内部调用isPrime,然后将值返回给调用者。否则,getNextPrime的调用不能说是返回下一个素数。

答案 2 :(得分:3)

首先也是最重要的是......你不需要检查直到     一世

for(int i = 2; i < number; i++)

你需要检查,直到我小于数字/ 2 ......

for(int i = 2; i < (number/2); i++)

答案 3 :(得分:2)

是的,有。我不知道它是否是最有效的,但它比这更有效。检查Miller Rabin测试。

即便如此,如果您想使用您的代码,我可以告诉您,您应该这样做:

public boolean isPrime(int number)
{
    // You should know, that every straight number can not be prime,so you can say i+= 2
   if (number == 2)
       return true;
   if (number % 2 == 0)
   {
      return false;
   }
    for(int i = 3; i < number; i+=2)
    {
       if (number % i == 0)
       {
          number--;
          return false;
       }
     --number;
     return true;
 }

答案 4 :(得分:2)

这就是我为简单起见而写的方式

public static void main(String... args) {
    System.out.print("Enter an integer that you'd like the system to print the prime numbers till: ");
    Scanner scan = new Scanner(System.in);
    int input = scan.nextInt();

    if (input >= 2)
        System.out.println(2);
    OUTER: for (int i = 3; i <= input; i += 2) { // skip every even number
        for (int j = 3; j * j <= i; j += 2) // stop when j <= sqrt(i)
            if (i % j == 0)
                continue OUTER;
        System.out.println(i); // 99+% of the time will be spent here. ;)
    }
}

答案 5 :(得分:1)

为什么PrimeGenerator会产生不是素数的数字?那不优雅。删除isPrime() - 方法并重写getNextPrime() - 方法,以便它始终返回素数。

答案 6 :(得分:1)

作为改进,您可以逐步减少6而不是2,并在每个步骤中进行2次检查。看看我找到了here

  

基本上,每个数字都可以写成(6k,6k + 1,6k + 2,6k + 3,   6k + 4,或6k + 5)。 6k显然不是素数。项目6k + 2到6k + 4可以   写成2(3k + 1),3(2k + 1)和2(3k + 2),因此不是   因为它们可以被2或3整除。所以./ / p>

所以我的观点如下。如果我们想要找到高达1000的数字,我们可以做以下事情。

int [] primes = new int[1000];
primes[0] = 2;
primes[1] = 3;
primes[2] = 5;
primes[3] = 7;
index = 4;
for(int i = 12; i < 1000; i += 6) {
    boolean prime1 = true;
    boolean prime2 = true;
    int j = 1; // No need to divide by 2, the number is odd.
    while(j < index && (prime1 || prime2)) {
        if (prime1 && ((i - 1) % primes[j] == 0)) {
            prime1 = false;
        }
        if (prime2 && ((i + 1) % primes[j] == 0)) {
            prime2 = false;
        }
        j++;
    }
    if (prime1) {
        primes[index++] = i - 1;
    }
    if (prime2) {
        primes[index++] = i + 1;
    }
}

答案 7 :(得分:1)

试试这个代码mate.I写了这个。我认为这更优雅:)

 **import java.util.*;

    public class PrimeNum{
    public static void main(String args[]){
           Scanner x=new Scanner(System.in);
           System.out.println("Enter the number :  ");
           long y=x.nextLong();
           long i;
           for( i=2;i<y;i++){
           long z=y%i;
               if(z==0){
               System.out.println(y+" is not a prime");
               System.out.println(y+" Divide by "+i);
               i=y;    
                             }

               }if(i==y) System.out.println("Number is prime"); 
                if(y==1) System.out.println("Number 1 is not a prime");
         }
    }**

答案 8 :(得分:0)

根据我的观察,基本方法是使用它:

int prime(int up_limit){
        int counter =0;
        for(int i=1;i<=up_limit;i++)
              {
        if(up_limit%i==0)
             counter++;

        }
    if(count==2){
    return up_limit;
    }