【问题标题】:Is it possible to use LINQ to check if all numbers in a list are increasing monotonically?是否可以使用 LINQ 检查列表中的所有数字是否单调增加?
【发布时间】:2014-07-02 12:22:20
【问题描述】:

我很想知道在 LINQ 中是否有一种方法可以检查列表中的所有数字是否都单调递增?

示例

List<double> list1 = new List<double>() { 1, 2, 3, 4 };
Debug.Assert(list1.IsIncreasingMonotonically() == true);

List<double> list2 = new List<double>() { 1, 2, 100, -5 };
Debug.Assert(list2.IsIncreasingMonotonically() == false);

我问的原因是我想知道将列表中的元素与其前一个元素进行比较的技术,这是我在使用 LINQ 时从未理解过的。

完成的 C# 示例类

根据下面@Servy 的官方回答,这是我现在正在使用的完整课程。它向您的项目添加扩展方法,以检查列表是单调增加/减少,还是严格单调减少。我正在努力适应函数式编程风格,这是一种很好的学习方式。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyHelper
{
    /// <summary>
    /// Classes to check if a list is increasing or decreasing monotonically. See:
    /// http://stackoverflow.com/questions/14815356/is-it-possible-to-use-linq-to-check-if-all-numbers-in-a-list-are-increasing-mono#14815511
    /// Note the difference between strictly monotonic and monotonic, see:
    /// http://en.wikipedia.org/wiki/Monotonic_function
    /// </summary>
    public static class IsMonotonic
    {
        /// <summary>
        /// Returns true if the elements in the are increasing monotonically.
        /// </summary>
        /// <typeparam name="T">Type of elements in the list.</typeparam>
        /// <param name="list">List we are interested in.</param>
        /// <returns>True if all of the the elements in the list are increasing monotonically.</returns>
        public static bool IsIncreasingMonotonically<T>(this List<T> list) where T : IComparable
        {
            return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0).All(b => b);
        }

        /// <summary>
        /// Returns true if the elements in the are increasing strictly monotonically.
        /// </summary>
        /// <typeparam name="T">Type of elements in the list.</typeparam>
        /// <param name="list">List we are interested in.</param>
        /// <returns>True if all of the the elements in the list are increasing monotonically.</returns>
        public static bool IsIncreasingStrictlyMonotonically<T>(this List<T> list) where T : IComparable
        {
            return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) < 0).All(b => b);
        }

        /// <summary>
        /// Returns true if the elements in the are decreasing monotonically.
        /// </summary>
        /// <typeparam name="T">Type of elements in the list.</typeparam>
        /// <param name="list">List we are interested in.</param>
        /// <returns>True if all of the the elements in the list are decreasing monotonically.</returns>
        public static bool IsDecreasingMonotonically<T>(this List<T> list) where T : IComparable
        {
            return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) >= 0).All(b => b);
        }

        /// <summary>
        /// Returns true if the elements in the are decreasing strictly monotonically.
        /// </summary>
        /// <typeparam name="T">Type of elements in the list.</typeparam>
        /// <param name="list">List we are interested in.</param>
        /// <returns>True if all of the the elements in the list are decreasing strictly monotonically.</returns>
        public static bool IsDecreasingStrictlyMonotonically<T>(this List<T> list) where T : IComparable
        {
            return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) > 0).All(b => b);
        }

        /// <summary>
        /// Returns true if the elements in the are increasing monotonically.
        /// </summary>
        /// <typeparam name="T">Type of elements in the list.</typeparam>
        /// <param name="list">List we are interested in.</param>
        /// <returns>True if all of the the elements in the list are increasing monotonically.</returns>
        public static bool IsIncreasingMonotonicallyBy<T>(this List<T> list, Func<T> x) where T : IComparable
        {
            return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0).All(b => b);
        }

        public static void UnitTest()
        {
            {
                List<double> list = new List<double>() { 1, 2, 3, 4 };
                Debug.Assert(list.IsIncreasingMonotonically<double>() == true);
                Debug.Assert(list.IsIncreasingStrictlyMonotonically<double>() == true);
                Debug.Assert(list.IsDecreasingMonotonically<double>() == false);
                Debug.Assert(list.IsDecreasingStrictlyMonotonically<double>() == false);
            }

            {
                List<double> list = new List<double>() { 1, 2, 100, -5 };
                Debug.Assert(list.IsIncreasingMonotonically() == false);
                Debug.Assert(list.IsIncreasingStrictlyMonotonically() == false);
                Debug.Assert(list.IsDecreasingMonotonically() == false);
                Debug.Assert(list.IsDecreasingStrictlyMonotonically() == false);
            }

            {
                List<double> list = new List<double>() {1, 1, 2, 2, 3, 3, 4, 4};
                Debug.Assert(list.IsIncreasingMonotonically() == true);
                Debug.Assert(list.IsIncreasingStrictlyMonotonically<double>() == false);
                Debug.Assert(list.IsDecreasingMonotonically() == false);
                Debug.Assert(list.IsDecreasingStrictlyMonotonically() == false);
            }

            {
                List<double> list = new List<double>() { 4, 3, 2, 1 };
                Debug.Assert(list.IsIncreasingMonotonically() == false);
                Debug.Assert(list.IsIncreasingStrictlyMonotonically<double>() == false);
                Debug.Assert(list.IsDecreasingMonotonically() == true);
                Debug.Assert(list.IsDecreasingStrictlyMonotonically() == true);
            }

            {
                List<double> list = new List<double>() { 4, 4, 3, 3, 2, 2, 1, 1 };
                Debug.Assert(list.IsIncreasingMonotonically() == false);
                Debug.Assert(list.IsIncreasingStrictlyMonotonically<double>() == false);
                Debug.Assert(list.IsDecreasingMonotonically() == true);
                Debug.Assert(list.IsDecreasingStrictlyMonotonically() == false);
            }
        }
    }
}

【问题讨论】:

  • 样本数据和期望的结果会很棒。
  • 您为什么想要在 LINQ 中执行此操作?似乎循环会更简单。
  • 基本上这应该是可能的......虽然不确定LINQ是否是正确的工具......但既然我们在SO:你试过什么帽子?到底是什么不工作?
  • (我能想到一个涉及递归的解决方案,但如果没有模式匹配,它可能会有点可怕和低效。)
  • @millimoose 第一个为真,第二个为真。

标签: c# .net linq


【解决方案1】:
public static bool IsIncreasingMontonically<T>(List<T> list) 
    where T : IComparable
{
    return list.Zip(list.Skip(1), (a, b) => a.CompareTo(b) <= 0)
        .All(b => b);
}

请注意,这会重复序列两次。对于List,这根本不是问题,对于IEnumerableIQueryable,这可能很糟糕,所以在将List&lt;T&gt; 更改为IEnumerable&lt;T&gt; 之前要小心。

【讨论】:

  • 不错!要添加附加功能“IsIncreasingStrictlyMonotonically”,请将“en.wikipedia.org/wiki/Monotonic_function。
【解决方案2】:

您不会使用OrderBy() 对列表进行排序并将它们与原始列表进行比较吗?如果它们相同,那么它会给你的答案是伪口语:

var increasing = orignalList.OrderBy(m=>m.value1).ToList();
var decreasing = orignalList.OrderByDescending(m=>m.value1).ToList();

var mono = (originalList == increasing || originalList == decreasing)

【讨论】:

  • 这只会确定它们是否在增加,而不是单调增加(假设我理解 OP 的单调含义)。
  • @JLRishe 我提交您(或 OP)不理解“单调”与“严格单调”的含义。 (只是“单调”意味着序列是非递减的。)
  • 即增加或减少模式没有异常
  • @millimoose 是的,你是对的,我不理解单调性,但是,上面的方法仍然行不通。 originalList == increasing 只是比较两个引用,并且总是错误的,因为 originalListincreasing 是独立的对象。或者这里的== 只是compare the two lists against each other 的简写?
  • @CR41G14 谢谢。这会很好地工作,但是,我正在寻求一种更有效、更低级别的方法来实现这一点,因为我正在处理大量庞大的列表。
【解决方案3】:

这是一个可以工作的单行:

var isIncreasing = list.OrderBy(x => x).SequenceEqual(list);

或者,如果您要提高性能,这里有一个单行代码,它只会遍历列表一次,并在到达乱序元素时立即退出:

var isIncreasing = !list.SkipWhile((x, i) => i == 0 || list[i - 1] <= x).Any();

【讨论】:

  • 很好,所以那就是在使用 LINQ 时如何引用列表中的前一个元素!
  • @Gravitas 通常我会避免这样做;这有点违背 LINQ 的设计,但肯定不是一个糟糕的做法。
  • @Matt 两次迭代 List 几乎没有负面影响。它只是迭代您需要担心的任意IEnumerable,在这种情况下您将无法使用您提到的方法。
  • 是的,这不适用于任意的IEnumerable。您需要具有快速索引查找的列表类型。另外 - 许多 linq 扩展都有索引过载,但很多没有。
【解决方案4】:

通过使用Enumerable.Aggregate 方法:

list1.Aggregate((a, i) => a > i ? double.MaxValue : i) != double.MaxValue;

【讨论】:

  • 我喜欢聚合,这里有另一个解决方案:list.Aggregate(new { res = true, last = double.MinValue }, (el, agg) => new { res = agg.res && el > agg.last, last = el }).res
  • @ionoy 这两种解决方案都不能接受最小值和最大值集合中的项目。
  • @servy 好的,那么这应该可以工作: l.Skip(1).Aggregate(new { res = true, last = l.FirstOrDefault() }, (agg, el) => new { res = agg.res && el > agg.last, last = el }).res
【解决方案5】:

使用循环!它简短、快速且易读。除了 Servy 的回答之外,这个线程中的大多数解决方案都非常慢(排序需要 'n log n' 时间)。

// Test whether a sequence is strictly increasing.
public bool IsIncreasing(IEnumerable<double> list)
{
    bool initial = true;
    double last = Double.MinValue;
    foreach(var x in list)
    {
        if (!initial && x <= last)
            return false;

        initial = false;
        last = x;
    }

    return true;
}

示例

  1. IsIncreasing(new List&lt;double&gt;{1,2,3}) 返回真
  2. IsIncreasing(new List&lt;double&gt;{1,3,2}) 返回 False

【讨论】:

  • 我会将此作为扩展方法(通过在静态类中使其成为静态,并在参数前添加“this”关键字)。这将使使用代码更加流畅。
【解决方案6】:

如果你想检查一个列表是否总是从一个索引增加到另一个索引:

IEnumerable<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 10 };
bool allIncreasing = !list
    .Where((i, index) => index > 0 && list.ElementAt(index - 1) >= i)
    .Any();

Demo

但在我看来,在这种情况下,一个简单的循环会更具可读性。

【讨论】:

  • 由于ElementAt 调用,这不是 O(n2) 吗?
  • @millimoose 这是一个List,而不是IEnumerable,所以ElementAt 是O(1)。如果它是任意的IEnumerable,那么这将是非常低效的。
  • @Servy 哦,ElementAt 是针对IList 优化的方法之一?
  • @millimoose:不,ElementAt 会将其转换为 IList&lt;int&gt; 并使用索引器。
【解决方案7】:

考虑如下的实现,它只枚举给定的 IEnumerable 一次。枚举可能会产生副作用,如果可能的话,调用者通常会期望一次传递。

public static bool IsIncreasingMonotonically<T>(
    this IEnumerable<T> _this)
    where T : IComparable<T>
{
    using (var e = _this.GetEnumerator())
    {
        if (!e.MoveNext())
            return true;
        T prev = e.Current;
        while (e.MoveNext())
        {
            if (prev.CompareTo(e.Current) > 0)
                return false;
            prev = e.Current;
        }
        return true;
    }
}

【讨论】:

    【解决方案8】:
    public static class EnumerableExtensions
    {
        private static bool CompareAdjacentElements<TSource>(this IEnumerable<TSource> source,
            Func<TSource, TSource, bool> comparison)
        {
            using (var iterator = source.GetEnumerator())
            {
                if (!iterator.MoveNext())
                    throw new ArgumentException("The input sequence is empty", "source");
                var previous = iterator.Current;
                while (iterator.MoveNext())
                {
                    var next = iterator.Current;
                    if (comparison(previous, next)) return false;
                    previous = next;
                }
                return true;
            }
        }
    
        public static bool IsSorted<TSource>(this IEnumerable<TSource> source)
            where TSource : IComparable<TSource>
        {
            return CompareAdjacentElements(source, (previous, next) => previous.CompareTo(next) > 0);
        }
    
        public static bool IsSorted<TSource>(this IEnumerable<TSource> source, Comparison<TSource> comparison)
        {
            return CompareAdjacentElements(source, (previous, next) => comparison(previous, next) > 0);
        }
    
        public static bool IsStrictSorted<TSource>(this IEnumerable<TSource> source)
            where TSource : IComparable<TSource>
        {
            return CompareAdjacentElements(source, (previous, next) => previous.CompareTo(next) >= 0);
        }
    
        public static bool IsStrictSorted<TSource>(this IEnumerable<TSource> source, Comparison<TSource> comparison)
        {
            return CompareAdjacentElements(source, (previous, next) => comparison(previous, next) >= 0);
        }
    }
    

    【讨论】:

    • 欢迎来到本站。感谢您的代码,这很有趣!
    猜你喜欢
    • 1970-01-01
    • 2017-02-12
    • 1970-01-01
    • 1970-01-01
    • 2010-09-07
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多