6

これよりも素数を見つけるためのより効率的でクリーンでエレガントな方法はありますか? コードは正常に動作しますが、私は自分にとって最も論理的と思われるものを書いただけで、他の方法を理解することはできませんが、正直言って見栄えがよくありません: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;    
    }
} 
4

9 に答える 9

5

この質問はすでに回答されていますが、誰かが役立つことを期待して、とにかく回答を提供すると思いました。

あなたは主にエレガンスと効率の両方に関心があるようです。また、正確性も同様に重要であることを指摘したいと思います。数値 1 を素数として扱う特別な要件がない限り、素数とは見なされなくなりました。ユーザーが素数を入力する場合のシナリオも同様に考慮する必要があります。また、出力する数値の境界条件についても考慮する必要があります。具体的には、数字の 7 を入力した場合、ユーザーはそれが 5,3,2,1 または 7,5,3,2,1 を出力することを期待しますか? 私の個人的な傾向は後者ですが、明確で簡潔なメッセージを使用すると、どちらのオプションも機能します。

優雅

ソリューションにエレガンスが欠けていると認識されているのは、主に素数テスト素数生成という 2 つの概念の組み合わせによるものです。

素数テストは、任意に選択された単一の数が素数であるかどうかを判断する (迅速な) メソッドです。素数ジェネレーターは、多くの場合連続する一連の素数を生成する方法です。

プログラムが示すように、指定された範囲内の各数値をテストし、素数である数値のみを選択することで、素数の連続したシーケンスを生成できます。これを当面の基本的な戦略として維持し、コードがどのようなものになるかを考えてみましょう:

前の説明から、素数テストは、任意に選択された数が素数であるかどうかを判断するためのメソッド (別名関数) であると述べました。したがって、このメソッドは入力として (n 任意に選択された) 数値を取り、指定された数値が素数であるかどうか (つまり、true/false) を返す必要があります。それがどのように見えるか見てみましょう:

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;
    }
}

次に、メイン プログラムは、各数値を繰り返し処理し、素数テストで素数と識別されたものだけを出力します。

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);
    }
}

効率

あなたの質問の反対側は、指定された範囲内で素数を決定する効率的な方法でした。簡単なインターネット検索では、力ずくの方法よりもはるかに高速な一連の素数を決定するためのさまざまな「高速」アルゴリズムが得られるはずです。そのようなアプローチの 1 つが Atkin のふるいです。

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();
    }
}

便利なことに、以前のメイン プログラムの 1 行を変更するだけで、この素数ジェネレータを使用できるようになりました。

変化する:

//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);
于 2013-01-07T16:54:46.610 に答える
3
  1. 内のプライベートコレクションにすでに見つかった素数を保存することで、新しい素数の検索を高速化できますPrimeGenerator。ループの代わりに潜在的な除数としてそれらだけを試すことによりfor(int i = 2; i < number; i++)、はるかに少ない除数を実行する必要があります
  2. :に到達するかなり前に、「除数の検索」ループnumberを停止できます。具体的には、候補除数がターゲット数の平方根を超えたときに停止できます。これは、候補の除数を昇順で試すために機能します。平方根の上に除数がある場合、除算の結果は平方根の下にあるため、すでにそれらを見つけているはずです。
  3. メソッドは、値を呼び出し元に返す前に内部getNextPrimeで呼び出す必要があります。isPrimeそうでなければ、の呼び出しはgetNextPrime次の素数を返すとは言えません。
于 2013-01-02T13:35:10.270 に答える
3

最初で最も重要なことは....私が確認するまで確認する必要はありません

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

i が number/2 未満になるまでチェックする必要があります...

for(int i = 2; i < (number/2); i++)
于 2013-01-02T14:13:15.643 に答える
2

ええ、あります。それが最も効率的かどうかはわかりませんが、これよりもはるかに効率的です。ミラーラビン素検定を確認してください。

それでも、コードを操作したい場合は、次のように行う必要があります。

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;
 }
于 2013-01-02T13:33:13.820 に答える
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. ;)
    }
}
于 2013-01-02T13:35:44.850 に答える
1

PrimeGenerator素数ではない数を生成するのはなぜですか? それはエレガントではありません。-method を削除し、常に素数を返すようisPrime()に -method を書き直してください。getNextPrime()

于 2013-01-02T14:06:04.497 に答える
1

改善として、2 ではなく 6 ずつステップし、各ステップで 2 つのチェックを行うことができます。ここで見つけたものを参照してください。

基本的に、すべての数値は (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 で割り切れるので素数ではありません。

だから私のポイントは次のとおりです。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;
    }
}
于 2013-01-02T14:06:15.790 に答える
1

このコードメイトを試してみてください。私はこれを書きました。これは私が思うよりエレガントです:)

 **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");
         }
    }**
于 2013-12-05T15:16:38.027 に答える
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;
    }
于 2014-12-10T18:34:44.473 に答える