C# 函数式快速排序失败

发布于 2024-08-30 06:52:43 字数 1419 浏览 3 评论 0原文

我正在尝试使用 C# 使用 linq 以函数式风格实现快速排序,此代码随机工作/不工作,我不明白为什么。
值得一提的是:当我在数组或列表上调用它时,它工作得很好。但在未知的 IEnumerable 上,它会变得疯狂(通常会丢失值或崩溃。有时会起作用。)
代码:

 public static IEnumerable; Quicksort(此 IEnumerable 来源)
        其中T:IComparable
    {
        if (!source.Any())
            产量突破;
        var hub = source.First();
        var SortedQuery = source.Skip(1).Where(a => a.CompareTo(source.First()) <= 0).Quicksort()
                          .Concat(new[] { 枢轴 })
                          .Concat(source.Skip(1).Where(a => a.CompareTo(source.First()) > 0).Quicksort());
        foreach(sortedQuery 中的 T 键)
            产量返回键;
    }

您能在这里找到导致此失败的任何错误吗?

编辑:一些更好的测试代码:

 var rand = new Random();
        var ienum = Enumerable.Range(1, 100).Select(a => rand.Next());
        var array = ienum.ToArray();
        尝试
        {
            array.Quicksort().Count();
            Console.WriteLine("数组运行良好。");
        }
        catch(异常前)
        {
            Console.WriteLine("数组运行不正常 ({0})。", ex.Message);
        }
        尝试
        {
            ienum.Quicksort().Count();
            Console.WriteLine("IEnumerable 一切顺利。");
        }
        catch(异常前)
        {
            Console.WriteLine("IEnumerable 运行不正常 ({0})。", ex.Message);
        }

I'm trying to implement quicksort in a functional style using C# using linq, and this code randomly works/doesn't work, and I can't figure out why.
Important to mention: When I call this on an array or list, it works fine. But on an unknown-what-it-really-is IEnumerable, it goes insane (loses values or crashes, usually. sometimes works.)
The code:

   public static IEnumerable<T> Quicksort<T>(this IEnumerable<T> source)
        where T : IComparable<T>
    {
        if (!source.Any())
            yield break;
        var pivot = source.First();
        var sortedQuery = source.Skip(1).Where(a => a.CompareTo(source.First()) <= 0).Quicksort()
                          .Concat(new[] { pivot })
                          .Concat(source.Skip(1).Where(a => a.CompareTo(source.First()) > 0).Quicksort());
        foreach (T key in sortedQuery)
            yield return key;
    }

Can you find any faults here that would cause this to fail?

Edit: Some better test code:

        var rand = new Random();
        var ienum = Enumerable.Range(1, 100).Select(a => rand.Next());
        var array = ienum.ToArray();
        try
        {
            array.Quicksort().Count();
            Console.WriteLine("Array went fine.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("Array did not go fine ({0}).", ex.Message);
        }
        try
        {
            ienum.Quicksort().Count();
            Console.WriteLine("IEnumerable went fine.");
        }
        catch (Exception ex)
        {
            Console.WriteLine("IEnumerable did not go fine ({0}).", ex.Message);
        }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

南薇 2024-09-06 06:52:43

某些可枚举实例(例如 Linq to SQL 或实体框架查询返回的实例)仅设计为迭代一次。您的代码需要多次迭代,并且在这些类型的实例上会崩溃或表现奇怪。您必须首先使用 ToArray() 或类似方法具体化这些枚举。

您还应该重用该pivot,这样您就不必继续迭代第一个和剩余的元素。这可能无法完全解决问题,但在某些情况下会有所帮助:(

public static IEnumerable<T> Quicksort<T>(this IEnumerable<T> source)
    where T : IComparable<T>
{
    if (!source.Any())
        return source;
    var pivot = source.First();
    var remaining = source.Skip(1);
    return remaining
        .Where(a => a.CompareTo(pivot) <= 0).Quicksort()
        .Concat(new[] { pivot })
        .Concat(remaining.Where(a => a.CompareTo(pivot) > 0).Quicksort());
}

您也不需要遍历 sortedQuery - 只需返回它,它已经IEnumerable。)

在相关说明中,您为什么觉得需要重新实现此功能? Enumerable.OrderBy 已经为您完成了。


对更新的响应:

您的测试失败是因为您的测试错误,而不是算法。

Random 是一个不确定的输入源,正如我上面所解释的,排序方法需要对同一序列执行多次迭代。如果序列是完全随机的,那么它将在后续迭代中获得不同的值。本质上,您正在尝试对元素不断变化的序列进行快速排序!

如果您希望测试成功,则需要使输入一致。使用种子作为随机数生成器:

static IEnumerable<int> GetRandomInput(int seed, int length)
{
    Random rand = new Random(seed);
    for (int i = 0; i < length; i++)
    {
        yield return rand.Next();
    }
}

然后:

static void Main(string[] args)
{
    var sequence = GetRandomInput(248917, 100);
    int lastNum = 0;
    bool isSorted = true;
    foreach (int num in sequence.Quicksort())
    {
        if (num < lastNum)
        {
            isSorted = false;
            break;
        }
        lastNum = num;
    }
    Console.WriteLine(isSorted ? "Sorted" : "Not sorted");
    Console.ReadLine();
}

它将返回排序。

Some enumerable instances, such as those returned by Linq to SQL or Entity Framework queries, are only designed to be iterated once. Your code requires multiple iterations and will crash or behave strangely on these types of instances. You'd have to materialize those enumerables with ToArray() or a similar method first.

You should also be reusing that pivot so that you don't have to keep iterating for the first and remaining elements. This may not completely solve the problem, but it'll help in some cases:

public static IEnumerable<T> Quicksort<T>(this IEnumerable<T> source)
    where T : IComparable<T>
{
    if (!source.Any())
        return source;
    var pivot = source.First();
    var remaining = source.Skip(1);
    return remaining
        .Where(a => a.CompareTo(pivot) <= 0).Quicksort()
        .Concat(new[] { pivot })
        .Concat(remaining.Where(a => a.CompareTo(pivot) > 0).Quicksort());
}

(You also don't need to iterate through the sortedQuery - just return it, it's already an IEnumerable<T>.)

On a related note, why do you feel the need to re-implement this functionality? Enumerable.OrderBy already does it for you.


Response to update:

Your tests are failing because your test is wrong, not the algorithm.

Random is a non-deterministic input source and, as I have explained above, the sort method needs to perform multiple iterations over the same sequence. If the sequence is totally random, then it is going to get different values on subsequent iterations. Essentially, you are trying to quicksort a sequence whose elements keep changing!

If you want the test to succeed, you need to make the input consistent. Use a seed for the random number generator:

static IEnumerable<int> GetRandomInput(int seed, int length)
{
    Random rand = new Random(seed);
    for (int i = 0; i < length; i++)
    {
        yield return rand.Next();
    }
}

Then:

static void Main(string[] args)
{
    var sequence = GetRandomInput(248917, 100);
    int lastNum = 0;
    bool isSorted = true;
    foreach (int num in sequence.Quicksort())
    {
        if (num < lastNum)
        {
            isSorted = false;
            break;
        }
        lastNum = num;
    }
    Console.WriteLine(isSorted ? "Sorted" : "Not sorted");
    Console.ReadLine();
}

It will come back sorted.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文