【问题标题】:How to iterate through all subcollections of size K, in ascending order of their sums.如何按总和的升序遍历所有大小为 K 的子集合。
【发布时间】:2016-07-31 19:31:45
【问题描述】:

假设我有一个排序数组,例如

int[] sarr = new int[] { 0, 1, 3, 5 }; 

我想以总和的升序遍历所有大小 K 的组合。例如,如果K=2,则按顺序组合是

{0, 1} (sum = 1)
{1, 0} (sum = 1)
{0, 3} (sum = 3)
{3, 0} (sum = 3)
{3, 1} (sum = 4)
{1, 3} (sum = 4)
{5, 0} (sum = 5)
.
.
.

我想在不首先获取所有组合的情况下执行此操作,因为我想在找到满足条件 Func<int[],bool> cond 的组合后立即停止。

有已知的方法吗?

【问题讨论】:

  • 只是很挑剔,看到{0, 1}{1, 0} 都出现了,看起来您正在寻找安排而不是组合。

标签: c# algorithm performance sorting complexity-theory


【解决方案1】:

我会使用yield return 来描述您想要生成的所有组合、排列或任何子集合,然后在结果上使用FirstOrDefault

这样,只有在没有您要查找的子集合或您要查找的子集合是最后一个时,您才会生成所有子集合。

关于让它们按元素总和升序排列,对初始集合进行排序,然后从头到尾选择 k 元素。您甚至可以生成组合,并从中生成所有可能的排列,从而获得所有安排。

获取所有组合的快速方法:

class Program
{
    static void Main(string[] args)
    {
        var initialArray = new[] { 0, 1, 3, 5 };
        var subArrayLength = 2;

        foreach (var subArray in GetSubArrays(initialArray, subArrayLength))
            Console.WriteLine($"[{string.Join(", ", subArray)}]");

        Console.WriteLine("Searching for array that contains both 1 and 5.");
        var arrayFulfillingCriteria = GetSubArrays(initialArray, subArrayLength).FirstOrDefault(array => array.Contains(1) && array.Contains(5));
        if (arrayFulfillingCriteria != null)
            Console.WriteLine($"[{string.Join(", ", arrayFulfillingCriteria)}]");
        else
            Console.WriteLine("No array found.");
    }

    static IEnumerable<int[]> GetSubArrays(int[] initialArray, int subArrayLength)
    {
        var indexStack = new Stack<int>(Enumerable.Range(0, subArrayLength));

        do
        {
            var subArray = indexStack.Select(i => initialArray[i]).Reverse().ToArray();
            yield return subArray;

            var index = indexStack.Pop();
            while (indexStack.Count != 0 && indexStack.Count < subArrayLength && index == initialArray.Length - (subArrayLength - indexStack.Count))
                index = indexStack.Pop();

            while (indexStack.Count < subArrayLength && index < initialArray.Length - (subArrayLength - indexStack.Count))
            {
                index++;
                indexStack.Push(index);
            }
        }
        while (indexStack.Count != 0);
    }
}

我能想到您需要安排的唯一原因(按总和排序)是子数组中的项目需要按特定顺序排列。

【讨论】:

    【解决方案2】:

    这对你有用吗?

    Func<IEnumerable<int>, IEnumerable<IEnumerable<int>>> getAllSubsets = null;
    getAllSubsets = xs =>
        (xs == null || !xs.Any())
            ? Enumerable.Empty<IEnumerable<int>>()
            :  xs.Skip(1).Any()
                ? getAllSubsets(xs.Skip(1))
                    .SelectMany(ys => new [] { ys, xs.Take(1).Concat(ys) })
                : new [] { Enumerable.Empty<int>(), xs.Take(1) };
    

    现在,鉴于此:

    Func<int[],bool> cond = xs => true;
    
    int[] sarr = new int[] { 0, 1, 3, 5, }; 
    
    var result =
        getAllSubsets(sarr)
            .Where(xs => xs.Count() == 2)
            .Where(xs => cond(xs.ToArray()));
    

    结果如下:

    {0, 1} {0, 3} {1, 3} {0, 5} {1, 5} {3, 5}

    【讨论】:

      猜你喜欢
      • 2012-11-20
      • 2013-08-06
      • 1970-01-01
      • 2019-02-26
      • 2015-08-14
      • 2020-01-26
      • 2012-03-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多