【问题标题】:Generate prime number sequences, Loop Break vs LINQ TakeWhile生成素数序列,Loop Break vs LINQ TakeWhile
【发布时间】:2020-09-22 18:02:11
【问题描述】:

我尝试用 C# 生成素数序列。 到目前为止,代码似乎运行良好。

List<int> _primes = new List<int>();

bool IsPrime(int num)
{
    if (num < 2)
        return false;
    int chkEnd = (int)Math.Sqrt(num);

    foreach (var p in _primes)
    {
        if (p > chkEnd)
            break;
        if (num % p == 0)
            return false;
    }
    return true;
}

for (int i=2; i<1000000; i++)
{
    if (IsPrime(i))
        _primes.Add(i);
}

然后我将 IsPrime() 更改为使用 LINQ TakeWhile(), 我认为这两种方法的时间复杂度相同, 但执行时间明显高于前者。

bool IsPrime(int num)
{
    if (num < 2)
        return false;
    int chkEnd = (int)Math.Sqrt(num);

    foreach (var p in _primes.TakeWhile(x => x <= chkEnd))
    {
        if (num % p == 0)
            return false;
    }
    return true;
}

bool IsPrime(int num)
{
    if (num < 2)
        return false;
    int chkEnd = (int)Math.Sqrt(num);

    return _primes.TakeWhile(x => x <= chkEnd).All(p => num % p != 0);
}

有人知道吗?


编辑: 我将循环迭代增加到5000000,在consoleApplication中运行代码并输出执行时间

00:00:01.1841593 -- IsPrime_PureLoopBreak

00:00:03.2560654 -- IsPrime_TakeWhileAndLoop

00:00:03.4178782 -- IsPrime_TakeWhileAndAll

    static void Main(string[] args)
    {
        List<int> _primes;

        bool IsPrime_PureLoopBreak(int num)
        {
            if (num < 2)
                return false;
            int chkEnd = (int)Math.Sqrt(num);

            foreach (var p in _primes)
            {
                if (p > chkEnd)
                    break;
                if (num % p == 0)
                    return false;
            }
            return true;
        }

        bool IsPrime_TakeWhileAndLoop(int num)
        {
            if (num < 2)
                return false;
            int chkEnd = (int)Math.Sqrt(num);

            foreach (var p in _primes.TakeWhile(x => x <= chkEnd))
            {
                if (num % p == 0)
                    return false;
            }
            return true;
        }

        bool IsPrime_TakeWhileAndAll(int num)
        {
            if (num < 2)
                return false;
            int chkEnd = (int)Math.Sqrt(num);

            return _primes.TakeWhile(x => x <= chkEnd).All(p => num % p != 0);
        }

        var t1 = Measure(() => 
        {
            _primes = new List<int>();
            for (int i = 2; i < 5000000; i++)
            {
                if (IsPrime_PureLoopBreak(i))
                    _primes.Add(i);
            }
        });
        Console.WriteLine($"{t1} -- IsPrime_PureLoopBreak");

        var t2 = Measure(() =>
        {
            _primes = new List<int>();
            for (int i = 2; i < 5000000; i++)
            {
                if (IsPrime_TakeWhileAndLoop(i))
                    _primes.Add(i);
            }
        });
        Console.WriteLine($"{t2} -- IsPrime_TakeWhileAndLoop");

        var t3 = Measure(() =>
        {
            _primes = new List<int>();
            for (int i = 2; i < 5000000; i++)
            {
                if (IsPrime_TakeWhileAndAll(i))
                    _primes.Add(i);
            }
        });
        Console.WriteLine($"{t3} -- IsPrime_TakeWhileAndAll");

        Console.ReadLine();
    }

    public static TimeSpan Measure(Action action)
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        action?.Invoke();
        stopwatch.Stop();
        return stopwatch.Elapsed;
    }

【问题讨论】:

  • 你有什么问题?
  • “执行时间明显变长了”

标签: c# linq primes


【解决方案1】:

考虑编译器需要生成的代码。像 Linq 这样的“高级”代码简单易写,但编译器通常更难优化。

您之前的示例可以进一步简化为使用普通的 for 循环而不是 foreach。所以每次迭代应该只需要少量指令。

当使用 linq 时,编译器将为迭代器生成一个代理对象。这将需要调用您的 lambda 以检查它是否需要继续迭代,并且编译器可能无法内联方法调用。方法调用很便宜,但仍然比简单的算术指令贵几倍。

因此,根据经验,使用 Linq 和其他高级模式来提高可读性。如果您发现代码的某些部分没有达到性能目标,请使用分析器准确识别哪些部分需要时间,并重写该部分,直到它足够快或您已尽可能快。

【讨论】:

    【解决方案2】:

    使用 LINQ 有一些额外的开销。您正在创建和调用 lambda 表达式。

    但是,请务必在得出结论之前尝试发布版本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-03
      • 2015-02-09
      • 1970-01-01
      • 2019-11-08
      • 2019-05-20
      • 2016-07-28
      • 2014-06-10
      • 1970-01-01
      相关资源
      最近更新 更多