素数を生成する最もエレガントな方法[クローズ]


84

この関数を実装するための最もエレガントな方法は何ですか?

ArrayList generatePrimes(int n)

この関数は最初のn素数を生成するため(編集:where n>1)、withgeneratePrimes(5)を返します。(私はこれをC#で行っていますが、Javaの実装、またはその他の同様の言語(Haskellではない)に満足しています)。ArrayList{2, 3, 5, 7, 11}

この関数の書き方は知っていますが、昨夜やったときは思ったほどうまくいきませんでした。これが私が思いついたものです:

ArrayList generatePrimes(int toGenerate)
{
    ArrayList primes = new ArrayList();
    primes.Add(2);
    primes.Add(3);
    while (primes.Count < toGenerate)
    {
        int nextPrime = (int)(primes[primes.Count - 1]) + 2;
        while (true)
        {
            bool isPrime = true;
            foreach (int n in primes)
            {
                if (nextPrime % n == 0)
                {
                    isPrime = false;
                    break;
                }
            }
            if (isPrime)
            {
                break;
            }
            else
            {
                nextPrime += 2;
            }
        }
        primes.Add(nextPrime);
    }
    return primes;
}

明らかに非効率にしたくないのですが、私は速度についてあまり心配していません。どちらの方法を使用するか(ナイーブまたはふるいなど)は問題ありませんが、かなり短く、どのように機能するかを明確にしたいと思います。

編集:多くの人が私の実際の質問に答えなかったが、答えてくれたすべての人に感謝します。繰り返しになりますが、素数のリストを生成するきれいなコードが必要でした。私はすでにさまざまな方法でそれを行う方法を知っていますが、私はそれができるほど明確ではないコードを書く傾向があります。このスレッドでは、いくつかの優れたオプションが提案されています。

  • 私が最初に持っていたもののより良いバージョン(Peter Smit、jmservera、Rekreativc)
  • エラトステネスのふるい(スターブルー)の非常にクリーンな実装
  • Javaのを使用BigIntegernextProbablePrime、非常に単純なコードに使用しますが、特に効率的であるとは想像できません(dfa)
  • LINQを使用して、素数のリストを遅延生成します(Maghis)
  • たくさんの素数をテキストファイルに入れて、必要に応じて読みます(ダリン)

編集2:ここに記載されているいくつかのメソッドとここに記載されていない別のメソッドをC#で実装しました。それらはすべて最初のn個の素数を効果的に見つけます(そして私はふるいに提供する限界を見つけるためのまともな方法を持っています)。


12
いいえ、それはプロジェクトオイラーにも当てはまりません:-)
David Johnstone

1
私にとっては、無数の<int>を再評価し、1つずつ生成する方がよいでしょう
Felice Pollano 2012

4
私が知りたいのは、素数を生成するための最もエレガントでない方法は何ですか。Accessデータベースに関することを考えていますか?
j_random_hacker 2013

1
比較のために、BMephによる2008年のHaskellコードnubBy (((>1).).gcd) [2..]。これは、2から始まる自然数の中で重複しないものだけを残しgcdますが、以前に見つかった数のいずれかが1より大きい数は重複していると見なします。これは非常に非効率的で、生成される素数の数が2次です。しかし、それはエレガントです。
ネスになります

最もエレガントなIMOはHaskellのものですimport Data.List.Ordered ; let { _Y g = g (_Y g) ; primes = 2 : _Y( (3:) . minus [5,7..] . unionAll . map (\p-> [p*p, p*p+p*2..]) ) }が、それはもちろん完全に意見に基づいています。
ウィルネス

回答:


48

見積もりを使用する

pi(n) = n / log(n)

nまでの素数の数で制限を見つけてから、ふるいを使用します。推定では、nまでの素数の数をいくらか過小評価しているため、ふるいは必要以上にわずかに大きくなります。これは問題ありません。

これは私の標準的なJavaふるいであり、通常のラップトップで約1秒で最初の100万素数を計算します。

public static BitSet computePrimes(int limit)
{
    final BitSet primes = new BitSet();
    primes.set(0, false);
    primes.set(1, false);
    primes.set(2, limit, true);
    for (int i = 0; i * i < limit; i++)
    {
        if (primes.get(i))
        {
            for (int j = i * i; j < limit; j += i)
            {
                primes.clear(j);
            }
        }
    }
    return primes;
}

3
これはエラトステネスのふるいの非常に優れた実装です
David Johnstone

1
i <= Math.sqrt(limit)外側のループにいる間にループするだけで十分ではありませんか?
クリストフ

1
@David Johnstoneいいえ、pi(n)= n / log(n)は、反対方向に進むnまでの素数の数を過小評価しています。しかし、もっと良い近似を見つけてくれてうれしいです。
starblue 2009

1
独自のループで2の倍数をすべて削除する場合は、ループの増分としてj + = 2 * iを使用して余分なランタイムを節約でき、ビットシフトを使用して1回計算できます
Nick Larsen

5
BitSet2、3、5のホイール因数分解を実装するクラスに置き換えることで、ほぼ3倍高速になります。
starblue 2010年

37

有益な回答をしてくれたすべての人に感謝します。これは、C#で最初のn個の素数を見つけるいくつかの異なる方法の私の実装です。最初の2つの方法は、ほとんどここに投稿されたものです。(ポスターの名前はタイトルの横にあります。)私はいつかアトキンのふるいをするつもりですが、それは現在ここでの方法ほど単純ではないと思います。誰かがこれらの方法のいずれかを改善する方法を見ることができるなら、私は知りたいです:-)

標準的な方法Peter SmitjmserveraRekreativc

最初の素数は2です。これを素数のリストに追加します。次の素数は、このリストのどの数でも均等に割り切れない次の数です。

public static List<int> GeneratePrimesNaive(int n)
{
    List<int> primes = new List<int>();
    primes.Add(2);
    int nextPrime = 3;
    while (primes.Count < n)
    {
        int sqrt = (int)Math.Sqrt(nextPrime);
        bool isPrime = true;
        for (int i = 0; (int)primes[i] <= sqrt; i++)
        {
            if (nextPrime % primes[i] == 0)
            {
                isPrime = false;
                break;
            }
        }
        if (isPrime)
        {
            primes.Add(nextPrime);
        }
        nextPrime += 2;
    }
    return primes;
}

これは、テストされる数の平方根までの分割可能性をテストすることによってのみ最適化されています。奇数をテストするだけです。これは、別の形態の数値だけを試験することによって最適化することができ6k+[1, 5]、又は30k+[1, 7, 11, 13, 17, 19, 23, 29]あるいはそうで

エラトステネスのふるいスターブルー

これにより、kのすべての素数が見つかります。最初のn個の素数のリストを作成するには、最初にn番目の素数の値を概算する必要があります。ここ説明する次のメソッドは、これを行います。

public static int ApproximateNthPrime(int nn)
{
    double n = (double)nn;
    double p;
    if (nn >= 7022)
    {
        p = n * Math.Log(n) + n * (Math.Log(Math.Log(n)) - 0.9385);
    }
    else if (nn >= 6)
    {
        p = n * Math.Log(n) + n * Math.Log(Math.Log(n));
    }
    else if (nn > 0)
    {
        p = new int[] { 2, 3, 5, 7, 11 }[nn - 1];
    }
    else
    {
        p = 0;
    }
    return (int)p;
}

// Find all primes up to and including the limit
public static BitArray SieveOfEratosthenes(int limit)
{
    BitArray bits = new BitArray(limit + 1, true);
    bits[0] = false;
    bits[1] = false;
    for (int i = 0; i * i <= limit; i++)
    {
        if (bits[i])
        {
            for (int j = i * i; j <= limit; j += i)
            {
                bits[j] = false;
            }
        }
    }
    return bits;
}

public static List<int> GeneratePrimesSieveOfEratosthenes(int n)
{
    int limit = ApproximateNthPrime(n);
    BitArray bits = SieveOfEratosthenes(limit);
    List<int> primes = new List<int>();
    for (int i = 0, found = 0; i < limit && found < n; i++)
    {
        if (bits[i])
        {
            primes.Add(i);
            found++;
        }
    }
    return primes;
}

サンダラムのふるい

このふるいを最近発見したばかりですが、非常に簡単に実装できます。私の実装はエラトステネスのふるいほど速くはありませんが、素朴な方法よりもはるかに高速です。

public static BitArray SieveOfSundaram(int limit)
{
    limit /= 2;
    BitArray bits = new BitArray(limit + 1, true);
    for (int i = 1; 3 * i + 1 < limit; i++)
    {
        for (int j = 1; i + j + 2 * i * j <= limit; j++)
        {
            bits[i + j + 2 * i * j] = false;
        }
    }
    return bits;
}

public static List<int> GeneratePrimesSieveOfSundaram(int n)
{
    int limit = ApproximateNthPrime(n);
    BitArray bits = SieveOfSundaram(limit);
    List<int> primes = new List<int>();
    primes.Add(2);
    for (int i = 1, found = 1; 2 * i + 1 <= limit && found < n; i++)
    {
        if (bits[i])
        {
            primes.Add(2 * i + 1);
            found++;
        }
    }
    return primes;
}

参考までに-オーバーフローを防ぐために、メインループカウンターを「for(int i = 0; i * i <= limit && i * i> 0; i ++)」に変更する必要がありました。
Jacobs Data Solutions

サンダラムのふるいのこの実装は、そこにある数少ない正しい実装の1つです。それらのほとんどは、計算中にiとjに間違った境界を使用i+j+2*i*jし、誤った出力につながります。
jahackbeth 2015

14

古い質問を復活させましたが、LINQで遊んでいるときに偶然見つけました。

このコードには、並列拡張機能を備えた.NET4.0または.NET3.5が必要です

public List<int> GeneratePrimes(int n) {
    var r = from i in Enumerable.Range(2, n - 1).AsParallel()
            where Enumerable.Range(1, (int)Math.Sqrt(i)).All(j => j == 1 || i % j != 0)
            select i;
    return r.ToList();
}

1
なぜこれが受け入れられない答えではないのですか?ここでのコードは、受け入れられた回答のコードよりもはるかに短く、エレガントで、はるかに高速です。複数回賛成できたらいいのに!
Avrohom Yisroel

9

あなたは良い道を進んでいます。

いくつかのコメント

  • primes.Add(3); この関数はnumber = 1では機能しません

  • テストする数の平方根よりも大きい素数で除算をテストする必要はありません。

推奨コード:

ArrayList generatePrimes(int toGenerate)
{
    ArrayList primes = new ArrayList();

    if(toGenerate > 0) primes.Add(2);

    int curTest = 3;
    while (primes.Count < toGenerate)
    {

        int sqrt = (int) Math.sqrt(curTest);

        bool isPrime = true;
        for (int i = 0; i < primes.Count && primes.get(i) <= sqrt; ++i)
        {
            if (curTest % primes.get(i) == 0)
            {
                isPrime = false;
                break;
            }
        }

        if(isPrime) primes.Add(curTest);

        curTest +=2
    }
    return primes;
}

1
平方根を事前に計算する代わりに、ループ内でprime * prime <= curTestをテストすると、おそらく高速になり、より一般的になります(bignumなどで機能します)
yairchu 2009年

なぜ平方根を使用するのですか?そのようなオプションの数学的背景は何ですか?私は、おそらく鈍く、唯一の2で割るだろう
ルイス・フィリペ

3
数に素因数がある場合、それらの少なくとも1つは平方根以下でなければならないためです。a * b = cおよびa <= bの場合、a <= sqrt(c)<= b。
デビッドジョンストーン

8

確率的素数を確認する必要があります。特に、ランダム化アルゴリズムミラー-ラビン素数性テストを見てください

完全を期すために、java.math.BigIntegerを使用することもできます。

public class PrimeGenerator implements Iterator<BigInteger>, Iterable<BigInteger> {

    private BigInteger p = BigInteger.ONE;

    @Override
    public boolean hasNext() {
        return true;
    }

    @Override
    public BigInteger next() {
        p = p.nextProbablePrime();
        return p;
    }

    @Override
    public void remove() {
        throw new UnsupportedOperationException("Not supported.");
    }

    @Override
    public Iterator<BigInteger> iterator() {
        return this;
    }
}

@Test
public void printPrimes() {
    for (BigInteger p : new PrimeGenerator()) {
        System.out.println(p);
    }
}

2
Miller-Rabbinは非常に高速で、コードは非常に単純です。十分な反復を行うことで、誤検知の可能性に関してランダムなCPU障害と競合するのに十分な信頼性が得られます。アルゴリズムの欠点は、それが実際に機能する理由を理解することが難しい作業であるということです。
ブライアン

6

決して効率的ではありませんが、おそらく最も読みやすいものです。

public static IEnumerable<int> GeneratePrimes()
{
   return Range(2).Where(candidate => Range(2, (int)Math.Sqrt(candidate)))
                                     .All(divisor => candidate % divisor != 0));
}

と:

public static IEnumerable<int> Range(int from, int to = int.MaxValue)
{
   for (int i = from; i <= to; i++) yield return i;
}

実際、ここにあるいくつかの投稿のバリエーションであり、より適切なフォーマットになっています。


5

Copyrights 2009 by St.Wittum 13189 Berlin GERMANY under CC-BY-SA License https://creativecommons.org/licenses/by-sa/3.0/

すべての素数を計算するためのシンプルですが最もエレガントな方法はこれですが、この方法は遅く、ファカルティ(!)関数を使用するため、メモリコストは数値が大きいほど高くなります...しかし、アプリケーションでウィルソンの定理のバリエーションを示していますPythonで実装されたアルゴリズムによってすべての素数を生成します

#!/usr/bin/python
f=1 # 0!
p=2 # 1st prime
while True:
    if f%p%2:
        print p
    p+=1
    f*=(p-2)

4

素数ジェネレーターを使用してprimes.txtを作成してから、次の操作を行います。

class Program
{
    static void Main(string[] args)
    {
        using (StreamReader reader = new StreamReader("primes.txt"))
        {
            foreach (var prime in GetPrimes(10, reader))
            {
                Console.WriteLine(prime);
            }
        }
    }

    public static IEnumerable<short> GetPrimes(short upTo, StreamReader reader)
    {
        int count = 0;
        string line = string.Empty;
        while ((line = reader.ReadLine()) != null && count++ < upTo)
        {
            yield return short.Parse(line);
        }
    }
}

この場合、メソッドシグネチャでInt16を使用しているため、primes.txtファイルには0〜32767の数値が含まれています。これをInt32またはInt64に拡張する場合は、primes.txtを大幅に大きくすることができます。


3
OPの引用:「どちらの方法(ナイーブまたはふるいなど)を使用してもかまいませんが、かなり短く、どのように機能するかを明確にしたいと思います」。私の答えは完全に関連していると思います。これは最速の方法でもあります。
ダリンディミトロフ

14
「どちらの方法でもいい…」と言っても「素数のリストを開く」ということはないと思います。それは、「コンピューターを買う」という質問に「コンピューターを作る方法」に答えるようなものです。-1
stevenvh 2009年

8
素数をファイルから読み取るのではなく、実際にソースコード自体に書き込んだ方が速いでしょう。
ダニエルダラナス2009年

3
多くのメモリを消費しますか?素数リスト全体をテキストとしてメモリに読み込むだけではありませんか?.netで文字列がどのように機能するか知っていますか?
jmservera 2009年

6
素数のリストは無限ですが不変のリストであるため、アプリケーションの上限となる可能性のある上限まで、事前に計算されたリストを使用することは理にかなっています。要件を満たすために使用できる正しい公開リストがあるのに、バグがある可能性のあるコードを書くのに時間を無駄にするのはなぜですか。
AndyM 2009年

4

次のC#ソリューションを提供できます。それは決して速いわけではありませんが、それが何をするかについては非常に明確です。

public static List<Int32> GetPrimes(Int32 limit)
{
    List<Int32> primes = new List<Int32>() { 2 };

    for (int n = 3; n <= limit; n += 2)
    {
        Int32 sqrt = (Int32)Math.Sqrt(n);

        if (primes.TakeWhile(p => p <= sqrt).All(p => n % p != 0))
        {
            primes.Add(n);
        }
    }

    return primes;
}

制限が負であるか2より小さい場合、チェックを省略しました(現時点では、メソッドは常に少なくとも2を素数として返します)。しかし、それはすべて簡単に修正できます。

更新

次の2つの拡張方法を使用する

public static void Do<T>(this IEnumerable<T> collection, Action<T> action)
{
    foreach (T item in collection)
    {
        action(item);
    }
}

public static IEnumerable<Int32> Range(Int32 start, Int32 end, Int32 step)
{
    for (int i = start; i < end; i += step)
    }
        yield return i;
    }
}

次のように書き直すことができます。

public static List<Int32> GetPrimes(Int32 limit)
{
    List<Int32> primes = new List<Int32>() { 2 };

    Range(3, limit, 2)
        .Where(n => primes
            .TakeWhile(p => p <= Math.Sqrt(n))
            .All(p => n % p != 0))
        .Do(n => primes.Add(n));

    return primes;
}

効率は劣りますが(平方根は頻繁に再評価されるため)、コードはさらにクリーンになります。素数を怠惰に列挙するようにコードを書き直すことは可能ですが、これはコードをかなり乱雑にします。


平方根の計算がJITコンパイラーによって最適化されることはほぼ間違いありません(最適化を有効にしてコンパイルした場合)。生成されたアセンブリを調べてこれを確認する必要があります(ILは部分的にしか最適化されておらず、JITコンパイラによって実行される最適化にはほど遠いです。ループの巻き上げやその他のマイクロ最適化の時代は終わりました。実際、 JIT裏をかくことができ遅くあなたのコードを。
デイブ・ブラック

4

C#でのSieve ofEratosthenesの実装は次のとおりです。

    IEnumerable<int> GeneratePrimes(int n)
    {
        var values = new Numbers[n];

        values[0] = Numbers.Prime;
        values[1] = Numbers.Prime;

        for (int outer = 2; outer != -1; outer = FirstUnset(values, outer))
        {
            values[outer] = Numbers.Prime;

            for (int inner = outer * 2; inner < values.Length; inner += outer)
                values[inner] = Numbers.Composite;
        }

        for (int i = 2; i < values.Length; i++)
        {
            if (values[i] == Numbers.Prime)
                yield return i;
        }
    }

    int FirstUnset(Numbers[] values, int last)
    {
        for (int i = last; i < values.Length; i++)
            if (values[i] == Numbers.Unset)
                return i;

        return -1;
    }

    enum Numbers
    {
        Unset,
        Prime,
        Composite
    }

私は列挙型の代わりにブール値でそれを行います...–
レターマン

3

同じアルゴリズムを使用すると、少し短くすることができます。

List<int> primes=new List<int>(new int[]{2,3});
for (int n = 5; primes.Count< numberToGenerate; n+=2)
{
  bool isPrime = true;
  foreach (int prime in primes)
  {
    if (n % prime == 0)
    {
      isPrime = false;
      break;
    }
  }
  if (isPrime)
    primes.Add(n);
}

3

あなたがHaskell以外の解決策を求めたことは知っていますが、質問に関連しているので、ここにこれを含めています。また、Haskellはこの種のことで美しいです。

module Prime where

primes :: [Integer]
primes = 2:3:primes'
  where
    -- Every prime number other than 2 and 3 must be of the form 6k + 1 or 
    -- 6k + 5. Note we exclude 1 from the candidates and mark the next one as
    -- prime (6*0+5 == 5) to start the recursion.
    1:p:candidates = [6*k+r | k <- [0..], r <- [1,5]]
    primes'        = p : filter isPrime candidates
    isPrime n      = all (not . divides n) $ takeWhile (\p -> p*p <= n) primes'
    divides n p    = n `mod` p == 0

ええ、私もHaskellの大ファンです(もっとよく知っていればいいのですが)
David Johnstone

3

いくつかのLINQを使用して、c#で簡単なエラトステネスの実装を作成しました。

残念ながら、LINQはintの無限シーケンスを提供しないため、int.MaxValue :(を使用する必要があります。

キャッシュされたプライムごとに計算されないように、候補sqrtを匿名タイプでキャッシュする必要がありました(少し見苦しいようです)。

候補者のsqrtまで以前の素数のリストを使用します

cache.TakeWhile(c => c <= candidate.Sqrt)

2から始まるすべてのIntをそれに対してチェックします

.Any(cachedPrime => candidate.Current % cachedPrime == 0)

コードは次のとおりです。

static IEnumerable<int> Primes(int count)
{
    return Primes().Take(count);
}

static IEnumerable<int> Primes()
{
    List<int> cache = new List<int>();

    var primes = Enumerable.Range(2, int.MaxValue - 2).Select(candidate => new 
    {
        Sqrt = (int)Math.Sqrt(candidate), // caching sqrt for performance
        Current = candidate
    }).Where(candidate => !cache.TakeWhile(c => c <= candidate.Sqrt)
            .Any(cachedPrime => candidate.Current % cachedPrime == 0))
            .Select(p => p.Current);

    foreach (var prime in primes)
    {
        cache.Add(prime);
        yield return prime;
    }
}

もう1つの最適化は、リストを作成する前に偶数をチェックせず、2だけを返すことです。このようにして、呼び出し元のメソッドが1つの素数を要求するだけの場合、すべての混乱を回避できます。

static IEnumerable<int> Primes()
{
    yield return 2;
    List<int> cache = new List<int>() { 2 };

    var primes = Enumerable.Range(3, int.MaxValue - 3)
        .Where(candidate => candidate % 2 != 0)
        .Select(candidate => new
    {
        Sqrt = (int)Math.Sqrt(candidate), // caching sqrt for performance
        Current = candidate
    }).Where(candidate => !cache.TakeWhile(c => c <= candidate.Sqrt)
            .Any(cachedPrime => candidate.Current % cachedPrime == 0))
            .Select(p => p.Current);

    foreach (var prime in primes)
    {
        cache.Add(prime);
        yield return prime;
    }
}

1
この答えをよりよく理解して理解するのに十分なLINQを知っていればいいのですが:-)また、これはエラトステネスのふるいの実装ではなく、概念的には元の機能と同じように機能するように感じます(次を見つける以前に見つかった素数のいずれでも割り切れない数)。
David Johnstone

はい。ただし、「以前に見つかった素数のいずれかで割り切れない次の数を見つける(数よりも小さい)」は、概念的にはエラトステネスのふるいに似ています。必要に応じて、LINQに慣れていない場合でも、少しリファクタリングして読みやすくすることができます。イテレータに精通していますか?
マギス2009年

このアプローチで私が気に入っているのは、呼び出し元が要求したときに次の素数が計算されるため、「最初のn個の素数を取る」や「nよりも小さい素数を取る」などが簡単になることです
Maghis

1
ありがとう、しかし私はそれが何をしているのかを多かれ少なかれ知るのに十分理解できます:-)私は遅延評価が好きです、しかし私はまだこれをエラトステネスのふるいの実装とは呼びません。
David Johnstone

1

よりエレガントにするには、IsPrimeテストを別のメソッドにリファクタリングし、それ以外のループとインクリメントを処理する必要があります。


1

私が書いた関数型ライブラリを使用してJavaでそれを行いましたが、私のライブラリは列挙型と同じ概念を使用しているため、コードは適応可能であると確信しています。

Iterable<Integer> numbers = new Range(1, 100);
Iterable<Integer> primes = numbers.inject(numbers, new Functions.Injecter<Iterable<Integer>, Integer>()
{
    public Iterable<Integer> call(Iterable<Integer> numbers, final Integer number) throws Exception
    {
        // We don't test for 1 which is implicit
        if ( number <= 1 )
        {
            return numbers;
        }
        // Only keep in numbers those that do not divide by number
        return numbers.reject(new Functions.Predicate1<Integer>()
        {
            public Boolean call(Integer n) throws Exception
            {
                return n > number && n % number == 0;
            }
        });
    }
});

1

これは私が急いで考えることができる最もエレガントです。

ArrayList generatePrimes(int numberToGenerate)
{
    ArrayList rez = new ArrayList();

    rez.Add(2);
    rez.Add(3);

    for(int i = 5; rez.Count <= numberToGenerate; i+=2)
    {
        bool prime = true;
        for (int j = 2; j < Math.Sqrt(i); j++)
        {
            if (i % j == 0)
            {
                    prime = false;
                    break;
            }
        }
        if (prime) rez.Add(i);
    }

    return rez;
}

これがあなたにアイデアを与えるのに役立つことを願っています。これは最適化できると確信していますが、バージョンをよりエレガントにする方法がわかるはずです。

編集:コメントに記載されているように、このアルゴリズムは実際にnumberToGenerate <2に対して間違った値を返します。私は彼に素数を生成するための優れた方法を投稿しようとしていなかったことを指摘したいと思います(そのためのアンリの答えを見てください)、私は彼の方法をよりエレガントにする方法を少しだけ指摘していました。


3
これはnumberToGenerate <2に対して間違った結果を返します
Peter Smit

これは本当ですが、私はアルゴリズムを設計していませんでした。彼の方法をよりエレガントにする方法を彼に示しただけです。したがって、このバージョンは、最初の質問のバージョンと同じように間違っています。
デヴィッド・Božjak

1
n = 1で壊れたとは思いもしませんでした。関数がn> 1でのみ機能するように質問を少し変更しました:
David Johnstone

これは素数の二乗を素数として認めます。
ウィルネス

1

関数型Javaでストリームベースのプログラミングを使用して、私は次のことを思いつきました。タイプNaturalは基本的にBigInteger> = 0です。

public static Stream<Natural> sieve(final Stream<Natural> xs)
{ return cons(xs.head(), new P1<Stream<Natural>>()
  { public Stream<Natural> _1()
    { return sieve(xs.tail()._1()
                   .filter($(naturalOrd.equal().eq(ZERO))
                           .o(mod.f(xs.head())))); }}); }

public static final Stream<Natural> primes
  = sieve(forever(naturalEnumerator, natural(2).some()));

これで、持ち運びできる値ができました。これは、素数の無限の流れです。あなたはこのようなことをすることができます:

// Take the first n primes
Stream<Natural> nprimes = primes.take(n);

// Get the millionth prime
Natural mprime = primes.index(1000000);

// Get all primes less than n
Stream<Natural> pltn = primes.takeWhile(naturalOrd.lessThan(n));

ふるいの説明:

  1. 引数ストリームの最初の数値が素数であると想定し、それを戻りストリームの先頭に配置します。リターンストリームの残りの部分は、要求された場合にのみ生成される計算です。
  2. 誰かがストリームの残りを要求した場合は、引数ストリームの残りでsieveを呼び出し、最初の数で割り切れる数を除外します(除算の残りはゼロです)。

次のインポートが必要です。

import fj.P1;
import static fj.FW.$;
import static fj.data.Enumerator.naturalEnumerator;
import fj.data.Natural;
import static fj.data.Natural.*;
import fj.data.Stream;
import static fj.data.Stream.*;
import static fj.pre.Ord.naturalOrd;

1

個人的には、これは非常に短くてクリーンな(Java)実装だと思います。

static ArrayList<Integer> getPrimes(int numPrimes) {
    ArrayList<Integer> primes = new ArrayList<Integer>(numPrimes);
    int n = 2;
    while (primes.size() < numPrimes) {
        while (!isPrime(n)) { n++; }
        primes.add(n);
        n++;
    }
    return primes;
}

static boolean isPrime(int n) {
    if (n < 2) { return false; }
    if (n == 2) { return true; }
    if (n % 2 == 0) { return false; }
    int d = 3;
    while (d * d <= n) {
        if (n % d == 0) { return false; }
        d += 2;
    }
    return true;
}

1

このLINQクエリを試してみてください。期待どおりに素数が生成されます。

        var NoOfPrimes= 5;
        var GeneratedPrime = Enumerable.Range(1, int.MaxValue)
          .Where(x =>
            {
                 return (x==1)? false:
                        !Enumerable.Range(1, (int)Math.Sqrt(x))
                        .Any(z => (x % z == 0 && x != z && z != 1));
            }).Select(no => no).TakeWhile((val, idx) => idx <= NoOfPrimes-1).ToList();

1
// Create a test range
IEnumerable<int> range = Enumerable.Range(3, 50 - 3);

// Sequential prime number generator
var primes_ = from n in range
     let w = (int)Math.Sqrt(n)
     where Enumerable.Range(2, w).All((i) => n % i > 0)
     select n;

// Note sequence of output:
// 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
foreach (var p in primes_)
    Trace.Write(p + ", ");
Trace.WriteLine("");

0

これは、200万未満のすべての素数の合計を出力するPythonコードの例です。

from math import *

limit = 2000000
sievebound = (limit - 1) / 2
# sieve only odd numbers to save memory
# the ith element corresponds to the odd number 2*i+1
sieve = [False for n in xrange(1, sievebound + 1)]
crosslimit = (int(ceil(sqrt(limit))) - 1) / 2
for i in xrange(1, crosslimit):
    if not sieve[i]:
        # if p == 2*i + 1, then
        #   p**2 == 4*(i**2) + 4*i + 1
        #        == 2*i * (i + 1)
        for j in xrange(2*i * (i + 1), sievebound, 2*i + 1):
            sieve[j] = True
sum = 2
for i in xrange(1, sievebound):
    if not sieve[i]:
        sum = sum + (2*i+1)
print sum

0

最も簡単な方法は試行錯誤です。2からn-1までの任意の数が候補の素数nを除算するかどうかを試行します。
もちろん、最初のショートカットは、a)奇数をチェックするだけでよく、b)sqrt(n)までの分周器をチェックするだけです。

プロセスで以前のすべての素数も生成する場合は、リスト内のsqrt(n)までの素数のいずれかがnを除算するかどうかを確認するだけで済みます。
あなたがあなたのお金のために得ることができる最も速いはずです:-)

編集
OK、コード、あなたはそれを求めました。しかし、私はあなたに警告しています:-)、これは5分です-迅速で汚いDelphiコード:

procedure TForm1.Button1Click(Sender: TObject);
const
  N = 100;
var
  PrimeList: TList;
  I, J, SqrtP: Integer;
  Divides: Boolean;
begin
  PrimeList := TList.Create;
  for I := 2 to N do begin
    SqrtP := Ceil(Sqrt(I));
    J := 0;
    Divides := False;
    while (not Divides) and (J < PrimeList.Count) 
                        and (Integer(PrimeList[J]) <= SqrtP) do begin
      Divides := ( I mod Integer(PrimeList[J]) = 0 );
      inc(J);
    end;
    if not Divides then
      PrimeList.Add(Pointer(I));
  end;
  // display results
  for I := 0 to PrimeList.Count - 1 do
    ListBox1.Items.Add(IntToStr(Integer(PrimeList[I])));
  PrimeList.Free;
end;

1
そして、これをコードでどのように表現しますか?:-)
David Johnstone

0

最初の100個の素数を見つけるには、次のJavaコードを検討できます。

int num = 2;
int i, count;
int nPrimeCount = 0;
int primeCount = 0;

    do
    {

        for (i = 2; i <num; i++)
        {

             int n = num % i;

             if (n == 0) {

             nPrimeCount++;
         //  System.out.println(nPrimeCount + " " + "Non-Prime Number is: " + num);

             num++;
             break;

             }
       }

                if (i == num) {

                    primeCount++;

                    System.out.println(primeCount + " " + "Prime number is: " + num);
                    num++;
                }


     }while (primeCount<100);

0

これは、Wikkiで「Sieveof Atkin」を最初に読んだことと、これに与えたいくつかの事前の考えによって得られました。私は最初からコーディングに多くの時間を費やし、コンパイラのような非常に高密度のコーディングに批判的な人々に完全にゼロになります。スタイル+私はコードを実行する最初の試みさえしていません...私が使用することを学んだパラダイムの多くはここにあります、ただ読んで泣いて、あなたができることを手に入れてください。

使用する前に、これらすべてを実際にテストすることを絶対に完全に行ってください。誰にも見せないでください。アイデアを読んで検討するためのものです。素数性ツールを機能させる必要があるので、何かを機能させる必要があるたびにここから始めます。

クリーンなコンパイルを1つ取得してから、欠陥のあるものを取り除き始めます。この方法で使用できるコードのキーストロークは1億800万近くありますが、できる限り使用してください。

明日は自分のバージョンで作業します。

package demo;
// This code is a discussion of an opinion in a technical forum.
// It's use as a basis for further work is not prohibited.
import java.util.Arrays;
import java.util.HashSet;
import java.util.ArrayList;
import java.security.GeneralSecurityException;

/**
 * May we start by ignores any numbers divisible by two, three, or five
 * and eliminate from algorithm 3, 5, 7, 11, 13, 17, 19 completely - as
 * these may be done by hand. Then, with some thought we can completely
 * prove to certainty that no number larger than square-root the number
 * can possibly be a candidate prime.
 */

public class PrimeGenerator<T>
{
    //
    Integer HOW_MANY;
    HashSet<Integer>hashSet=new HashSet<Integer>();
    static final java.lang.String LINE_SEPARATOR
       =
       new java.lang.String(java.lang.System.getProperty("line.separator"));//
    //
    PrimeGenerator(Integer howMany) throws GeneralSecurityException
    {
        if(howMany.intValue() < 20)
        {
            throw new GeneralSecurityException("I'm insecure.");
        }
        else
        {
            this.HOW_MANY=howMany;
        }
    }
    // Let us then take from the rich literature readily 
    // available on primes and discount
    // time-wasters to the extent possible, utilizing the modulo operator to obtain some
    // faster operations.
    //
    // Numbers with modulo sixty remainder in these lists are known to be composite.
    //
    final HashSet<Integer> fillArray() throws GeneralSecurityException
    {
        // All numbers with modulo-sixty remainder in this list are not prime.
        int[]list1=new int[]{0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,
        32,34,36,38,40,42,44,46,48,50,52,54,56,58};        //
        for(int nextInt:list1)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list1");//
            }
        }
        // All numbers with modulo-sixty remainder in this list are  are
        // divisible by three and not prime.
        int[]list2=new int[]{3,9,15,21,27,33,39,45,51,57};
        //
        for(int nextInt:list2)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list2");//
            }
        }
        // All numbers with modulo-sixty remainder in this list are
        // divisible by five and not prime. not prime.
        int[]list3=new int[]{5,25,35,55};
        //
        for(int nextInt:list3)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list3");//
            }
        }
        // All numbers with modulo-sixty remainder in
        // this list have a modulo-four remainder of 1.
        // What that means, I have neither clue nor guess - I got all this from
        int[]list4=new int[]{1,13,17,29,37,41,49,53};
        //
        for(int nextInt:list4)
        {
            if(hashSet.add(new Integer(nextInt)))
            {
                continue;
            }
            else
            {
                throw new GeneralSecurityException("list4");//
            }
        }
        Integer lowerBound=new Integer(19);// duh
        Double upperStartingPoint=new Double(Math.ceil(Math.sqrt(Integer.MAX_VALUE)));//
        int upperBound=upperStartingPoint.intValue();//
        HashSet<Integer> resultSet=new HashSet<Integer>();
        // use a loop.
        do
        {
            // One of those one liners, whole program here:
            int aModulo=upperBound % 60;
            if(this.hashSet.contains(new Integer(aModulo)))
            {
                continue;
            }
            else
            {
                resultSet.add(new Integer(aModulo));//
            }
        }
        while(--upperBound > 20);
        // this as an operator here is useful later in your work.
        return resultSet;
    }
    // Test harness ....
    public static void main(java.lang.String[] args)
    {
        return;
    }
}
//eof

0

このコードを試してください。

protected bool isPrimeNubmer(int n)
    {
        if (n % 2 == 0)
            return false;
        else
        {
            int j = 3;
            int k = (n + 1) / 2 ;

            while (j <= k)
            {
                if (n % j == 0)
                    return false;
                j = j + 2;
            }
            return true;
        }
    }
    protected void btn_primeNumbers_Click(object sender, EventArgs e)
    {
        string time = "";
        lbl_message.Text = string.Empty;
        int num;

        StringBuilder builder = new StringBuilder();

        builder.Append("<table><tr>");
        if (int.TryParse(tb_number.Text, out num))
        {
            if (num < 0)
                lbl_message.Text = "Please enter a number greater than or equal to 0.";
            else
            {
                int count = 1;
                int number = 0;
                int cols = 11;

                var watch = Stopwatch.StartNew();

                while (count <= num)
                {
                    if (isPrimeNubmer(number))
                    {
                        if (cols > 0)
                        {
                            builder.Append("<td>" + count + " - " + number + "</td>");
                        }
                        else
                        {
                            builder.Append("</tr><tr><td>" + count + " - " + number + "</td>");
                            cols = 11;
                        }
                        count++;
                        number++;
                        cols--;
                    }
                    else
                        number++;
                }
                builder.Append("</table>");
                watch.Stop();
                var elapsedms = watch.ElapsedMilliseconds;
                double seconds = elapsedms / 1000;
                time = seconds.ToString();
                lbl_message.Text = builder.ToString();
                lbl_time.Text = time;
            }
        }
        else
            lbl_message.Text = "Please enter a numberic number.";

        lbl_time.Text = time;

        tb_number.Text = "";
        tb_number.Focus();
    }

これがaspxコードです。

<form id="form1" runat="server">
    <div>
        <p>Please enter a number: <asp:TextBox ID="tb_number" runat="server"></asp:TextBox></p>

        <p><asp:Button ID="btn_primeNumbers" runat="server" Text="Show Prime Numbers" OnClick="btn_primeNumbers_Click" />
        </p>
        <p><asp:Label ID="lbl_time" runat="server"></asp:Label></p>
        <p><asp:Label ID="lbl_message" runat="server"></asp:Label></p>
    </div>
</form>

結果:1秒未満で10000の素数

63秒で100000の素数

最初の100個の素数のスクリーンショット ここに画像の説明を入力してください


1
それを試してみると、その有効性と結果の提示を測ることができました。その優雅さを主張してください。
greybeard 2015年

結果のスタイリングは単なる追加部分です。素数としてtrue / falseを返すアルゴリズムについて説明します。n%2は、偶数が常に2で割り切れるので、数値の半分を削除します。elseコードでは、奇数のみで除算し、2で割り切れるように増やします(したがって、次の割り切れも奇数です)。か否か。それは私たちに分数で答えを与えるので、なぜ半分、時間を無駄にしないために。
riz 2015年

log10(63)〜= 1.8、つまり、データはn ^ 1.8の成長率示しています。これは非常に遅いです。エラトステネスの実装の最適なふるいexibit〜n ^ 1.01..1.05; 最適試行割り算〜n ^ 1.35..1.45。あなたはisPrimeNubmer確かに次善のトリル除算を実装します。さらに多くの素数を生成しようとすると、その無症状は約n ^ 2(またはそれより上)に悪化します。
2015
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.