2012-04-21 76 views
6

我的代码如下,通过创建素数列表并检查下列潜在素数是否可以被列表中的任何素数均匀整除,从而找到所有素数在number以下的素数。您是否可以访问IEnumerable,因为您正在返回它?

我在努力学习yield return的来龙去脉。现在我有一个List<int> primes,我在函数内使用。但我通过yield return返回相同的数据。所以我的问题是

我可以从函数内部访问IEnumerable < int>,因为我正在创建它?所以我可以删除列表< int>素数。

/// <summary> 
/// Finds all primes below <paramref name="number"/> 
/// </summary> 
/// <param name="number">The number to stop at</param> 
/// <returns>All primes below <paramref name="number"/></returns> 
private static IEnumerable<long> PrimeNumbers(long number) 
{ 
    yield return 2; 
    List<long> primes = new List<long>(2);   

    for(long num = 3; num < number; num += 2) 
    { 

     //if any prime lower then num divides evenly into num, it isn't a prime 
     //what I'm doing now 
     if(!primes.TakeWhile(x => x < num).Any(x => num % x == 0)) 
     { 
      primes.Add(num); 
      yield return num; 
     } 

     //made-up syntax for what I'd like to do 
     if(!this.IEnumerable<long> 
       .TakeWhile(x => x < num).Any(x => num % x == 0)) 
     { 
      yield return num; 
     } 
    } 
} 
+0

简答题:不会。长答案:有更好的方法可以让素数达到某个数。检查Sundaram的筛网:http://en.wikipedia.org/wiki/Sieve_of_Sundaram – SimpleVar 2012-04-21 03:44:51

+0

您也可以通过参考Eratosthenes筛选来改进:http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes,甚至更多Atkin的筛选器:http://en.wikipedia.org/wiki/Sieve_of_Atkin我认为这是目前已知的用于生成素数(如果不是最快的)的最快方法之一,并且wiki中还有伪代码来帮助您获得开始。 – SimpleVar 2012-04-21 03:47:51

+0

@YoryeNathan我在维基百科上看到了这些筛子,如果我将这些代码保留在任何重要的地方,我可能会使用它们。这段代码更有助于培养自己如何思考“收益回报”的思维方式。 – 2012-04-21 03:49:58

回答

4

不,你不能这样做。编译器建立一个状态机来实现yield return,通过你的枚举枚举的调用代码与你的代码一样是工作的一部分。编译器构建一个隐藏对象,该对象存储代码的当前状态,包括其调用堆栈和本地代码,并调用不同的方法,因为调用者调用CurrentMoveNext。试图枚举你的对象从一开始,而另一个枚举正在进行会搅乱正在进行的枚举,这将不好。

在这种特殊情况下,您不希望它发生:yield return的实施不存储您生成的值,因此如果即使您在枚举时可以访问自己的IEnumerable,它也会递归地回调本身多次产生每个新物品,所以如果要产生中等数量的素数,你需要花费很长的时间。

+0

当然可以。递归或嵌套枚举应该被自动处理,这是它们之美的一部分。这将做到这一点:'如果(!PrimeNumbers(num).Any(x => num%x == 0))yield return num'。只是在LINQPad中测试过。然而,你的第二点是,它是非常低效的。你可以通过简单地在函数内部做一个'Console.WriteLine(..)'来看看它枚举了多少次相同的结果。 – mellamokb 2012-04-21 03:38:08

+1

@mellamokb这不是从创建序列的函数中访问*本身*。它使用自己的状态,状态机和其他所有内容访问自己的相同副本*。这就是为什么它不会中断,这就是为什么它很慢:) – dasblinkenlight 2012-04-21 03:41:37

+0

啊!我现在在同一页面上:)是的,您不能从同一个实例中获取先前的条目,因为之前的状态不再存在。 – mellamokb 2012-04-21 03:42:33

2

整体枚举器不是像List<int> primes这样的容器。这是一个寻找素数的“过程”。如果你递归地使用你自己的过程来生成素数列表来反对寻找下一个素数,那么你将会递归地枚举相同的结果,这将会非常低效。考虑会发生什么(如果你能真正做到这一点),用于查找素数最多为10

yield return 2 
num = 3 
IEnumerable<long>.TakeWhile(x => x < 3).Any(x => num % x == 0) 
    new enumerator 
    yield return 2 
yield return 3 
num = 4 
IEnumerable<long>.TakeWhile(x => x < 4).Any(x => num % x == 0) 
    new enumerator 
    yield return 2 
    num = 3 
    IEnumerable<long>.TakeWhile(x => x < 3).Any(x => num % x == 0) 
     new enumerator 
     yield return 2 
    yield return 3 
num = 5 
IEnumerable<long>.TakeWhile(x => x < 5).Any(x => num % x == 0) 
    new enumerator 
    yield return 2 
    num = 3 
    IEnumerable<long>.TakeWhile(x => x < 4).Any(x => num % x == 0) 
     new enumerator 
     yield return 2 
     num = 3 
     IEnumerable<long>.TakeWhile(x => x < 3).Any(x => num % x == 0) 
      new enumerator 
      yield return 2 
     yield return 3 
     num = 4 
etc. 

这是一个指数级增长enumartion。这类似于通过计算f(n-1) + f(n-2)天真地找到斐波纳契数字。你一遍又一遍地做很多相同的工作,甚至更高的数字。内部的素数列表充当一种缓存,使您的枚举非常高效。

相关问题