【问题标题】:C# arrays minimal value in specific rangeC# 数组在特定范围内的最小值
【发布时间】:2011-05-23 16:37:27
【问题描述】:

大家! 如何在 C# 中获取特定范围内的 int 数组的最小值? 例如: 整数 [] 数组 = 新整数 {1,2,3,4,5,6,7,8,76,45}; 我想在第 3 和第 8 个元素之间获得一个最小值。 也许可以通过 LINQ 查询获得?

【问题讨论】:

  • 是否总是至少有 8 个元素?

标签: c# .net arrays min


【解决方案1】:
int min = array.Where((value, index) => index >= 2 && index <= 7).Min(); 

编辑

实际上,上面的方法效率很低,因为它枚举了整个序列,即使我们对索引高于 7 的项目不感兴趣。更好的解决方案是使用TakeWhile

int min = array.TakeWhile((value, index) => index <= 7).Skip(2).Min();

不幸的是,它的可读性不是很好......让它变得更好的最佳选择可能是创建一个自定义扩展方法,如 Jon 的回答所示。

【讨论】:

    【解决方案2】:
    array.Skip(2).Take(5).Min();
    

    【讨论】:

    • @nozim:不,这不是因为目前还不清楚它在做什么。它很聪明,看起来也很聪明,但它不是最易读的解决方案,因此应该被拒绝。对不起。
    • @Jason:跳过 2 个项目,拿 5 个项目,然后得到最小值。什么不清楚?
    • @Jon Skeet:我觉得这里忽略了重点,特别是如果你不同意的话。是的,这是可以理解的,它跳过了两个,取了五个并找到了最小值。尚不清楚的是它正在解决手头的问题。需要花点时间思考(尽管很小)才能看到这一点,而我之前指出的解决方案却没有。我觉得这是因为聪明因素而被投票赞成的(聪明的代码很糟糕!)。
    • @Jason:我觉得这比 Thomas 的方法更容易理解。使用SkipTake 可以立即清楚地表明元素的 与过滤无关......而更一般的Where 子句迫使读者考虑仔细看看到底发生了什么。请注意,对于整数数组,也很容易意外地以错误的方式获取 valueindex 参数。如果你想让它更“开始”和“结束”相关,那么 Eric 的方法是一个不错的选择。
    • @Jason:最后,在这里使用Where 子句会强制评估每一对 - 这使得它的效率可能远低于Skip/Take 解决方案。如果数组中有一百万个项目,它需要考虑所有项目......而不是只是跳过一些,取走需要的东西,然后继续。
    【解决方案3】:
    int[] arr = {0,1,2,3,4,5,6,7,8};
    int start = 3;
    int end = 8;
    int min = arr.Skip(start - 1).Take(end - start).Min();
    

    【讨论】:

      【解决方案4】:
      array.Skip(3).Take(4).Min();
      

      【讨论】:

        【解决方案5】:

        只是添加另一个选项:

        int start = 3;
        int end = 8;
        var min = Enumerable.Range(start - 1,end - start).Select(idx => array[idx]).Min();
        

        AFAIK,如果你必须在一个接近末尾的范围内取一个范围,这“理论上”会更快,而且你的数组真的很长。

        那是因为(再次 AFAIK)Skip() 没有考虑到它是一个数组(即可以在 O(1) 中随机访问)并且无论如何都会枚举它。

        【讨论】:

        • 是的,在使用数组(或其他IList&lt;T&gt;)的特定情况下它更快 - 但在一般序列情况下它当然会崩溃。当然,Skip 也可能在未来的版本中得到修复 :)
        • 当然,我真的希望它会得到修复。与此同时,我的只是一种警告:)
        • +1 表示警告,尽管这不是我通常选择的解决方案:)
        【解决方案6】:

        我想我不妨在这上面加上我的 tuppence。由于 Jason 反对我们说的是跳过了多少而不是结束索引,因此我们可以添加一个简单的扩展方法:

        public static IEnumerable<T> WithIndexBetween<T>(this IEnumerable<T> source,
            int startInclusive, int endExclusive)
        {
            // The two values can be the same, yielding no results... but they must
            // indicate a reasonable range
            if (endExclusive < startInclusive)
            {
                throw new ArgumentOutOfRangeException("endExclusive");
            }
            return source.Skip(startInclusive).Take(endExclusive - startInclusive);
        }
        

        然后:

        int min = array.WithIndexBetween(2, 7).Min();
        

        根据口味调整扩展方法名称。 (命名很难,我不会花很多时间在这里想出一个好名字:)

        【讨论】:

        • 是的,这很好,因为现在它读起来就像问题陈述。请注意,它读作“在索引在 2 到 7 之间(含)的序列中取那些值,并取最小值”,这就是 Thomas 对我的理解,这就是我首先喜欢它的原因。所以,我喜欢这个。
        • 我正准备用类似的东西更新我的答案,然后我看到了你的答案......
        • 乔恩,这太棒了!我喜欢扩展方法。还有杰森,我喜欢你的坚持,也喜欢你对乔恩·斯基特的坚持! :)
        【解决方案7】:

        就个人而言,我更喜欢这个:

        public static class ArrayExtensions {
            public static bool ArrayAndIndexesAreValid(
                T[] array,
                int startInclusive,
                int endExclusive
            ) {
            return array != null &&
                   array.Length > 0 &&
                   startInclusive >= 0 && startInclusive < array.Length &&
                   endExclusive >= 1 && endExclusive <= array.Length &&
                   startInclusive < endExclusive;
            }
            public static IEnumerable<T> Slice<T>(
                this T[] array,
                int startInclusive,
                int endExclusive
            ) {
                Contract.Requires<ArgumentException>(ArrayAndIndexesAreValid(
                    array,
                    startInclusive,
                    endExclusive)
                );
                for (int index = startInclusive; index < endExclusive; index++) {
                    yield return array[index];
                }
            }
            public static T MinimumInIndexRange<T>(
                this T[] array,
                int startInclusive,
                int endExclusive
            ) where T : IComparable {
                Contract.Requires<ArgumentException>(ArrayAndIndexesAreValid(
                    array,
                    startInclusive,
                    endExclusive)
                );
                return array.Slice(startInclusive, endExclusive).Min();
            }
        
            public static T MaximumInIndexRange<T>(
                this T[] array,
                int startInclusive,
                int endExclusive
            ) where T : IComparable {
                Contract.Requires<ArgumentException>(ArrayAndIndexesAreValid(
                    array,
                    startInclusive,
                    endExclusive)
                );
                return array.Slice(startInclusive, endExclusive).Max();
            }
        }
        

        【讨论】:

        • 如果切片不包含任何元素,这将返回int.MaxValue,这可能不是一个好主意。它还混合了“找到最小值”和“考虑数组的一部分”的逻辑,而基于 LINQ 的解决方案没有。如果您需要找到数组切片的 最大值 值,您将重复所有切片逻辑。
        • 通过不使其通用,如果您需要来自long[] 等的最小值,您还强迫自己重新实现它。使此操作通用是一个简单的 IMO。当然,当你要返回一个空数组切片的值时,很难做到这一点......
        • @Jon Skeet:它抛出空(这可能不是首选;参见第二个Contract.Requires)。我会在一分钟内回复你的第二条评论。
        • @Jason:啊,endIndex 是包容性的。我没想到会这样。 (这就是为什么我的方法的参数明确表示的原因:)一般来说,表示最小值和最大值的方法(例如Random.Next)使用包含的下限和独占的上限。
        • @Jason:当它so 很容易将切片和 max/min 的关注点分开,并且 很容易使其通用时,我不要认为 YAGNI 真的适用。在显然是通用算法的情况下使用通用方法几乎不会过度设计。我仍然不相信将这两个方面放在同一个方法调用中是一个好主意。也许如果你一遍又一遍地这样做......但除此之外,在客户端使用简单操作的组合。
        猜你喜欢
        • 1970-01-01
        • 2020-09-20
        • 2021-05-11
        • 2021-07-04
        • 2013-11-14
        • 2015-03-31
        • 2014-03-22
        • 2020-10-16
        • 2020-09-01
        相关资源
        最近更新 更多