【问题标题】:Most elegant way to generate prime numbers [closed]生成素数的最优雅方法[关闭]
【发布时间】:2010-11-05 18:53:28
【问题描述】:

实现这个功能最优雅的方式是什么:

ArrayList generatePrimes(int n)

此函数生成第一个n 素数(编辑:n>1),因此generatePrimes(5) 将返回一个ArrayList{2, 3, 5, 7, 11}。 (我在 C# 中执行此操作,但我对 Java 实现或任何其他类似语言(因此不是 Haskell)感到满意。

我确实知道如何编写这个函数,但是当我昨晚完成时,它并没有像我希望的那样好。这是我想出的:

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

我不太关心速度,尽管我不希望它明显低效。我不介意使用哪种方法(naive 或 sieve 或其他任何方法),但我确实希望它相当简短并且很明显它是如何工作的。

编辑:感谢所有回复的人,尽管很多人没有回答我的实际问题。重申一下,我想要一段漂亮干净的代码来生成素数列表。我已经知道如何以多种不同的方式来完成它,但是我很容易编写不太清晰的代码。在此线程中,提出了一些不错的选择:

  • 我最初拥有的更好的版本(Peter Smit、jmservera 和 Rekreativc)
  • 非常干净的 Eratosthenes (starblue) 筛子实现
  • 使用 Java 的 BigIntegers 和 nextProbablePrime 编写非常简单的代码,尽管我无法想象它会特别高效 (dfa)
  • 使用 LINQ 延迟生成素数列表 (Maghis)
  • 将大量素数放入一个文本文件中,并在必要时读取它们 (darin)

编辑 2:我有implemented in C# 这里给出的几个方法,还有一个这里没有提到的方法。他们都有效地找到了前 n 个素数(我有一个 decent method 来寻找提供给筛子的极限)。

【问题讨论】:

  • 不,它也不适合 Project Euler :-)
  • 最好是让我重新调用 ienumerable 并一个一个地产生
  • 我想知道的是生成素数的最少优雅的方法是什么。我在想一些涉及 Access 数据库的事情?
  • 为了比较,2008 Haskell code by BMeph:nubBy (((&gt;1).).gcd) [2..]。它只留下自然数中的非重复项,从 2 开始,同时将 gcd 与任何先前找到的数字大于 1 的任何数字视为重复。它的效率非常低,产生的素数是二次的。但它是优雅
  • 优雅,IMO,是Haskell的import Data.List.Ordered ; let { _Y g = g (_Y g) ; primes = 2 : _Y( (3:) . minus [5,7..] . unionAll . map (\p-&gt; [p*p, p*p+p*2..]) ) },但这当然完全基于意见

标签: c# java algorithm primes


【解决方案1】:

使用估计

pi(n) = n / log(n)

对于最多n个素数求一个极限,然后用筛子。估计会稍微低估质数到 n 的数量,因此筛子会比必要的稍大,这没关系。

这是我的标准 Java 筛子,在普通笔记本电脑上大约一秒钟内计算出前一百万个素数:

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

【讨论】:

  • Eratosthenes 筛子的一个非常好的实现
  • i &lt;= Math.sqrt(limit) 在外循环中循环难道不够吗?
  • @David Johnstone 不,pi(n) = n / log(n) 低估了质数到 n 的数量,这是相反的方向。不过,我很高兴您找到了更好的近似值。
  • 如果你愿意在它自己的循环中删除所有 2 的倍数,你可以使用 j+= 2 * i 作为你的循环增量来节省一些额外的运行时间,你可以计算一次使用有点偏移
  • 通过将 BitSet 替换为实现 2、3 和 5 的车轮分解的类,它几乎快了 3 倍。
【解决方案2】:

非常感谢所有提供有用答案的人。这是我在 C# 中查找第一个 n 素数的几种不同方法的实现。前两种方法几乎就是这里发布的内容。 (海报名称在标题旁边。)我计划在某个时候做 Atkin 的筛子,尽管我怀疑它不会像目前这里的方法那么简单。如果有人能看到任何改进这些方法的方法,我很想知道:-)

标准方法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]so on 形式的数字来进一步优化。

埃拉托色尼筛法 (starblue)

This finds all the primes to k。要列出前 n 个素数,我们首先需要估计第 n 个素数的近似值。以下方法as described here 执行此操作。

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

桑达拉姆筛

我最近才发现this sieve,但是实现起来很简单。我的实现不如 Eratosthenes 的筛子快,但比朴素的方法快得多。

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 0; i++)”以防止溢出。
  • 这个 Sundaram 筛子的实现是极少数正确的实现之一。他们中的大多数在计算i+j+2*i*j 时对 i 和 j 使用了错误的边界,导致输出不正确。
【解决方案3】:

恢复一个老问题,但我在玩 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();
}

【讨论】:

  • 为什么这不是公认的答案?这里的代码比公认答案中的代码更短、更优雅、更快。希望我能多次投票!
【解决方案4】:

你走在正确的道路上。

一些cmets

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

【讨论】:

  • 在循环中测试 prime*prime
  • 为什么使用平方根?这种选择的数学背景是什么?我,可能很无聊,只会除以 2。
  • 因为如果一个数有质因数,至少有一个质因数必须小于或等于平方根。如果 a * b = c 且 a
【解决方案5】:

你应该看看probable primes。特别要看看Randomized AlgorithmsMiller–Rabin primality test

为了完整起见,您可以使用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);
    }
}

【讨论】:

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

实际上只是这里一些帖子的变体,格式更好。

【讨论】:

    【解决方案7】:

    版权所有 2009 年 St.Wittum 13189 Berlin GERMANY 根据 CC-BY-SA 许可 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)
    

    【讨论】:

      【解决方案8】:

      使用素数 numbers generator 创建 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 可能会大得多。

      【讨论】:

      • 引用 OP:“我不介意使用哪种方法(naive 或 sieve 或其他任何方法),但我确实希望它相当短且明显它是如何工作的”。我认为我的回答是完全相关的。这也是最快的方法。
      • 即使他说“我不介意哪种方法......”我认为这不包括“打开素数列表”。这就像通过“购买计算机”来回答“如何构建计算机”的问题。 -1
      • 如果你真的在源代码中写了质数,而不是从文件中读取它们会更快。
      • 消耗内存?不仅仅是将完整的素数列表作为文本读入...内存?你知道字符串在 .net 中是如何工作的吗?
      • 素数列表是一个无限但不可变的列表,因此使用预先计算的列表直到应用程序的可能上限非常有意义。当有一个正确的可用公共列表可用于满足要求时,为什么还要浪费时间编写可能有错误的代码。
      【解决方案9】:

      我可以提供以下 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;
      }
      

      我省略了任何检查 - 如果限制为负数或小于二(目前该方法将始终至少返回二作为素数)。但这很容易解决。

      更新

      具有以下两种扩展方法

      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 可以减慢您的代码。
      【解决方案10】:

      下面是 埃拉托色尼筛法 在 C# 中的实现:

          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
          }
      

      【讨论】:

      • 我会用 bool 而不是 enum...
      【解决方案11】:

      使用相同的算法可以缩短一点:

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

      【讨论】:

        【解决方案12】:

        我知道您要求非 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 的忠实粉丝(我只是希望我更了解它)
        【解决方案13】:

        我使用一些 LINQ 在 c# 中编写了一个简单的 Eratosthenes 实现。

        不幸的是,LINQ 不提供无限的整数序列,因此您必须使用 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;
            }
        }
        

        另一个优化是避免检查偶数并在创建列表之前只返回 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;
            }
        }
        

        【讨论】:

        • 我希望我对 LINQ 有足够的了解,能够更好地欣赏和理解这个答案 :-) 另外,我觉得这不是 Eratosthenes 筛子的实现,它在概念上的工作原理是一样的作为我的原始函数(找到下一个不能被任何先前找到的素数整除的数字)。
        • 是的,但是“找到下一个不能被任何先前找到的素数(小于数字)整除的数字”在概念上类似于埃拉托色尼筛。如果您愿意,即使您不熟悉 LINQ,我也可以对其进行一些重构以使其更具可读性。你熟悉迭代器吗?
        • 我喜欢这种方法的一点是,下一个素数是在调用者请求时计算的,所以像“取前 n 个素数”或“取小于 n 的素数” "变得微不足道
        • 谢谢,但我可以理解这足以或多或少知道它在做什么 :-) 我喜欢惰性评估,但我仍然不会称其为 Eratosthenes 筛子的实现。
        【解决方案14】:

        为了使其更优雅,您应该将 IsPrime 测试重构为一个单独的方法,并在此之外处理循环和增量。

        【讨论】:

          【解决方案15】:

          我使用自己编写的函数库在 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;
                      }
                  });
              }
          });
          

          【讨论】:

            【解决方案16】:

            这是我能在短时间内想到的最优雅的。

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

            希望这有助于给您一个想法。我确信这可以优化,但是它应该让您了解如何使您的版本更优雅。

            编辑: 如 cmets 所述,该算法确实返回了 numberToGenerate

            【讨论】:

            • 这个返回错误的结果 numberToGenerate
            • 这是真的,但是我不是在设计算法,我只是向他展示如何使他的方法更优雅。所以这个版本和开题的版本一样错误。
            • 我没有想到它在 n=1 时被破坏了。我稍微改变了这个问题,以便该功能只适用于 n>1 :-)
            • 这承认素数的平方是素数。
            【解决方案17】:

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

            【讨论】:

              【解决方案18】:

              我个人认为这是一个非常简短且干净的 (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;
              }
              

              【讨论】:

                【解决方案19】:

                试试这个 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();
                

                【讨论】:

                  【解决方案20】:
                  // 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("");
                  

                  【讨论】:

                    【解决方案21】:

                    这是一个 Python 代码示例,它打印出所有低于 200 万的素数之和:

                    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
                    

                    【讨论】:

                      【解决方案22】:

                      最简单的方法是反复试验:如果 2 和 n-1 之间的任何数字除以候选素数 n,则尝试。
                      第一个快捷方式当然是 a) 你只需要检查奇数,b) 你只需要检查直到 sqrt(n) 的分隔符。

                      在您的情况下,您也在该过程中生成所有先前的素数,您只需检查列表中的任何素数(直到 sqrt(n))是否除以 n。
                      应该是你能用钱买到的最快的:-)

                      编辑
                      好的,代码,你要求的。但我警告你 :-),这是 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;
                      

                      【讨论】:

                      • 你如何在代码中表达这一点? :-)
                      【解决方案23】:

                      要找出前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);
                      

                      【讨论】:

                        【解决方案24】:

                        我是通过第一次阅读 Wikki 上的“阿特金筛子”以及我之前对此的一些想法得到的——我花了很多时间从头开始编码,并完全关注那些批评我的编译器的人,非常密集的编码风格 + 我什至没有第一次尝试运行代码......我学会使用的许多范例都在这里,只是阅读和哭泣,尽你所能。

                        在任何使用之前绝对和完全确保真正测试所有这些,确保不要向任何人展示 - 这是为了阅读和考虑这些想法。我需要让素数工具工作,所以每次我必须让一些东西工作时,我都会从这里开始。

                        进行一次干净的编译,然后开始删除有缺陷的部分 - 我有近 1.08 亿次可用代码的击键以这种方式执行此操作,......尽你所能。

                        我明天将继续编写我的版本。

                        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
                        

                        【讨论】:

                          【解决方案25】:

                          试试这个代码。

                          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>
                          

                          结果: 10000 个质数在一秒内完成

                          100000 个质数在 63 秒内

                          前 100 个质数的屏幕截图

                          【讨论】:

                          • 试一试,我可以评估它的有效性和结果的呈现方式:请论证它的优雅。
                          • 结果的样式只是一个附加的部分。让我讨论将真/假作为素数返回的算法。 n%2 将消除一半的数字,因为偶数总是可以被 2 整除。在 else 代码中,我只除以奇数,增加可被 2 整除(因此下一个可整除数也是奇数)最多为素数的一半或不。为什么一半,不要浪费时间,因为它会给我们答案。
                          • log10(63)~=1.8,即 n^1.8 的 your data shows growth rate。这很慢; Eratosthenes 实施方案的最佳筛分 ~ n^1.01..1.05;最佳试验划分 ~ n^1.35..1.45。您的isPrimeNubmer 确实实现了次优的三除法;当您尝试生成更多素数时,它的渐近线将恶化到大约 n^2(甚至高于它)。
                          猜你喜欢
                          • 1970-01-01
                          • 2010-11-04
                          • 1970-01-01
                          • 1970-01-01
                          • 2011-04-06
                          • 1970-01-01
                          • 1970-01-01
                          • 2014-06-11
                          相关资源
                          最近更新 更多