C# 通过增加索引和生成排序组合的有效方法
对于启发式算法,我需要一个接一个地评估特定集合的组合,直到达到停止标准 由于它们很多,目前我使用以下高效内存迭代器块(受python启发)生成它们: 其中,正如您所看到的,组合不是严格按升序和排序的 相反,期望的结果如下:C# 通过增加索引和生成排序组合的有效方法,c#,performance,combinations,C#,Performance,Combinations,对于启发式算法,我需要一个接一个地评估特定集合的组合,直到达到停止标准 由于它们很多,目前我使用以下高效内存迭代器块(受python启发)生成它们: 其中,正如您所看到的,组合不是严格按升序和排序的 相反,期望的结果如下: (具有相同总和的组合顺序并不重要) 一个简单的解决方案是生成所有组合,然后根据它们的总和对它们进行排序;但这并不是真正有效/可行的,因为随着n的增长,组合的数量变得巨大 我还快速查看了组合格雷码,但我找不到适合这个问题的人 你对如何实现这样的东西有什么想法吗 编辑: 这个问题
(具有相同总和的组合顺序并不重要) 一个简单的解决方案是生成所有组合,然后根据它们的总和对它们进行排序;但这并不是真正有效/可行的,因为随着
n
的增长,组合的数量变得巨大
我还快速查看了组合格雷码,但我找不到适合这个问题的人
你对如何实现这样的东西有什么想法吗
编辑:
这个问题有一个替代(不幸的是不容易)公式。给定一组
S
和一个数字r
,所有可能的和都很难找到,因为它们只是从S
的前r
元素和到S
的最后r
元素和的所有数字
这就是说,如果对于每个和T
,我们可以有效地找到所有具有和T
的组合,我们就解决了原始问题,因为我们只是按升序生成它们
1有效地意味着我不想生成所有组合并丢弃具有不同总和的组合
编辑2:
在@EricLippert建议之后,我创建了以下代码:
public static IEnumerable<T[]>
GetCombinationsSortedByIndexSum<T>(this IList<T> pool, int r)
{
int n = pool.Count;
if (r > n)
throw new ArgumentException("r cannot be greater than pool size");
int minSum = ((r - 1) * r) / 2;
int maxSum = (n * (n + 1)) / 2 - ((n - r - 1) * (n - r)) / 2;
for (int sum = minSum; sum <= maxSum; sum++)
{
foreach (var indexes in AllMonotIncrSubseqOfLenMWhichSumToN(0, n - 1, r, sum))
yield return indexes.Select(x => pool[x]).ToArray();
}
}
static IEnumerable<IEnumerable<int>>
AllMonotIncrSubseqOfLenMWhichSumToN(int seqFirstElement, int seqLastElement, int m, int n)
{
for (int i = seqFirstElement; i <= seqLastElement - m + 1; i++)
{
if (m == 1)
{
if (i == n)
yield return new int[] { i };
}
else
{
foreach (var el in AllMonotIncrSubseqOfLenMWhichSumToN(i + 1, seqLastElement, m - 1, n - i))
yield return new int[] { i }.Concat(el);
}
}
}
公共静态IEnumerable
GetCombinationsSortedByIndexSum(此IList池,int r)
{
int n=池计数;
如果(r>n)
抛出新ArgumentException(“r不能大于池大小”);
int minSum=((r-1)*r)/2;
int maxSum=(n*(n+1))/2-((n-r-1)*(n-r))/2;
对于(int-sum=minSum;sum-pool[x]).ToArray();
}
}
静态可数
AllMonotIncrSubseqOfLenMWhichSumToN(int-seqFirstElement,int-seqLastElement,int-m,int-n)
{
对于(int i=seqFirstElement;i我想到的解决方案是:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
// Preconditions:
// * items is a sequence of non-negative monotone increasing integers
// * n is the number of items to be in the subsequence
// * sum is the desired sum of that subsequence.
// Result:
// A sequence of subsequences of the original sequence where each
// subsequence has n items and the given sum.
static IEnumerable<IEnumerable<int>> M(IEnumerable<int> items, int sum, int n)
{
// Let's start by taking some easy outs. If the sum is negative
// then there is no solution. If the number of items in the
// subsequence is negative then there is no solution.
if (sum < 0 || n < 0)
yield break;
// If the number of items in the subsequence is zero then
// the only possible solution is if the sum is zero.
if (n == 0)
{
if (sum == 0)
yield return Enumerable.Empty<int>();
yield break;
}
// If the number of items is less than the required number of
// items, there is no solution.
if (items.Count() < n)
yield break;
// We have at least n items in the sequence, and
// and n is greater than zero, so First() is valid:
int first = items.First();
// We need n items from a monotone increasing subsequence
// that have a particular sum. We might already be too
// large to meet that requirement:
if (n * first > sum)
yield break;
// There might be some solutions that involve the first element.
// Find them all.
foreach(var subsequence in M(items.Skip(1), sum - first, n - 1))
yield return new[]{first}.Concat(subsequence);
// And there might be some solutions that do not involve the first element.
// Find them all.
foreach(var subsequence in M(items.Skip(1), sum, n))
yield return subsequence;
}
static void Main()
{
int[] x = {0, 1, 2, 3, 4, 5};
for (int i = 0; i <= 15; ++i)
foreach(var seq in M(x, i, 4))
Console.WriteLine("({0}) SUM {1}", string.Join(",", seq), i);
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
班级计划
{
//先决条件:
//*项是一个非负单调递增整数序列
//*n是子序列中的项目数
//*总和是该子序列的期望总和。
//结果:
//原始序列的一系列子序列,其中
//子序列有n个项和给定的和。
静态IEnumerable M(IEnumerable项,整数和,整数n)
{
//让我们从简单的例子开始。如果总和是负数
//那么就没有解决方案了。如果
//子序列为负,则没有解。
如果(总和<0 | | n<0)
屈服断裂;
//如果子序列中的项数为零,则
//唯一可能的解决方案是如果和为零。
如果(n==0)
{
如果(总和=0)
产生返回可枚举的.Empty();
屈服断裂;
}
//如果项目数量小于所需数量
//但是,没有解决方案。
if(items.Count()sum)
屈服断裂;
//可能有一些解决方案涉及第一个元素。
//把它们都找出来。
foreach(M中的var子序列(items.Skip(1),sum-first,n-1))
产生返回新[]{first}.Concat(子序列);
//可能有一些解决方案不涉及第一个元素。
//把它们都找出来。
foreach(以M为单位的var子序列(项跳过(1),求和,n))
收益率子序列;
}
静态void Main()
{
int[]x={0,1,2,3,4,5};
对于(int i=0;i和)
屈服断裂;
//可能有一些解决方案涉及第一个元素。
//把它们都找出来。
foreach(var子序列,单位为M(items.Tail,sum-first,n-1))
收益返回子序列推送(第一);
//可能有一些解决方案不涉及第一个元素。
//把它们都找出来。
foreach(var子序列,单位为M(items.Tail,sum,n))
收益率子序列;
}
静态void Main()
{
ImmutableList x=ImmutableList.Empty.Push(5)。
推(4)。推(3)。推(2)。推(1)。推(0);
为了(int i=0;i为了完整性和清晰性,我将发布我的最终代码:
// Given a pool of elements returns all the
// combinations of the groups of lenght r in pool,
// such that the combinations are ordered (ascending) by the sum of
// the indexes of the elements.
// e.g. pool = {A,B,C,D,E} r = 3
// returns
// (A, B, C) indexes: (0, 1, 2) sum: 3
// (A, B, D) indexes: (0, 1, 3) sum: 4
// (A, B, E) indexes: (0, 1, 4) sum: 5
// (A, C, D) indexes: (0, 2, 3) sum: 5
// (A, C, E) indexes: (0, 2, 4) sum: 6
// (B, C, D) indexes: (1, 2, 3) sum: 6
// (A, D, E) indexes: (0, 3, 4) sum: 7
// (B, C, E) indexes: (1, 2, 4) sum: 7
// (B, D, E) indexes: (1, 3, 4) sum: 8
// (C, D, E) indexes: (2, 3, 4) sum: 9
public static IEnumerable<T[]>
GetCombinationsSortedByIndexSum<T>(this IList<T> pool, int r)
{
int n = pool.Count;
if (r > n)
throw new ArgumentException("r cannot be greater than pool size");
int minSum = F(r - 1);
int maxSum = F(n) - F(n - r - 1);
for (int sum = minSum; sum <= maxSum; sum++)
{
foreach (var indexes in AllSubSequencesWithGivenSum(0, n - 1, r, sum))
yield return indexes.Select(x => pool[x]).ToArray();
}
}
// Given a start element and a last element of a sequence of consecutive integers
// returns all the monotonically increasing subsequences of length "m" having sum "sum"
// e.g. seqFirstElement = 1, seqLastElement = 5, m = 3, sum = 8
// returns {1,2,5} and {1,3,4}
static IEnumerable<IEnumerable<int>>
AllSubSequencesWithGivenSum(int seqFirstElement, int seqLastElement, int m, int sum)
{
int lb = sum - F(seqLastElement) + F(seqLastElement - m + 1);
int ub = sum - F(seqFirstElement + m - 1) + F(seqFirstElement);
lb = Math.Max(seqFirstElement, lb);
ub = Math.Min(seqLastElement - m + 1, ub);
for (int i = lb; i <= ub; i++)
{
if (m == 1)
{
if (i == sum) // this check shouldn't be necessary anymore since LB/UB should automatically exclude wrong solutions
yield return new int[] { i };
}
else
{
foreach (var el in AllSubSequencesWithGivenSum(i + 1, seqLastElement, m - 1, sum - i))
yield return new int[] { i }.Concat(el);
}
}
}
// Formula to compute the sum of the numbers from 0 to n
// e.g. F(4) = 0 + 1 + 2 + 3 + 4 = 10
static int F(int n)
{
return (n * (n + 1)) / 2;
}
//给定一个元素池将返回所有
//池中长度r组的组合,
//使组合按以下各项之和排序(升序):
//元素的索引。
//例如pool={A,B,C,D,e}r=3
//返回
//(A,B,C)索引:(0,1,2)和:3
//(A,B,D)索引:(0,1,3)和:4
//(A,B,E)索引:(0,1,4)和:5
//(A,C,D)索引:(0,2,3)和:5
//(A,C,E)索引:(0,2,4)和:6
//(B,C,D)指数:(1,2,3)总和:6
//(A,D,E)索引:(0,3,4)和:7
//(B,C,E)指数:(1,2,4)总和:7
//(B,D,E)指数:(1,3,4)总和:8
//(C,D,E)指数:(2,3,4)总和:9
公共静态IEnumerable
GetCombinationsSortedByIndexSum(此IList池,int r)
{
int n=池计数;
如果(r>n)
抛出新ArgumentException(“r不能大于池大小”);
int minSum=F(r-1);
int maxSum=F(n)-F(n-r-1);
对于(int-sum=minSum;sum-pool[x]).ToArray();
}
}
//给定序列的起始元素和最后一个元素
public static IEnumerable<T[]>
GetCombinationsSortedByIndexSum<T>(this IList<T> pool, int r)
{
int n = pool.Count;
if (r > n)
throw new ArgumentException("r cannot be greater than pool size");
int minSum = ((r - 1) * r) / 2;
int maxSum = (n * (n + 1)) / 2 - ((n - r - 1) * (n - r)) / 2;
for (int sum = minSum; sum <= maxSum; sum++)
{
foreach (var indexes in AllMonotIncrSubseqOfLenMWhichSumToN(0, n - 1, r, sum))
yield return indexes.Select(x => pool[x]).ToArray();
}
}
static IEnumerable<IEnumerable<int>>
AllMonotIncrSubseqOfLenMWhichSumToN(int seqFirstElement, int seqLastElement, int m, int n)
{
for (int i = seqFirstElement; i <= seqLastElement - m + 1; i++)
{
if (m == 1)
{
if (i == n)
yield return new int[] { i };
}
else
{
foreach (var el in AllMonotIncrSubseqOfLenMWhichSumToN(i + 1, seqLastElement, m - 1, n - i))
yield return new int[] { i }.Concat(el);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
// Preconditions:
// * items is a sequence of non-negative monotone increasing integers
// * n is the number of items to be in the subsequence
// * sum is the desired sum of that subsequence.
// Result:
// A sequence of subsequences of the original sequence where each
// subsequence has n items and the given sum.
static IEnumerable<IEnumerable<int>> M(IEnumerable<int> items, int sum, int n)
{
// Let's start by taking some easy outs. If the sum is negative
// then there is no solution. If the number of items in the
// subsequence is negative then there is no solution.
if (sum < 0 || n < 0)
yield break;
// If the number of items in the subsequence is zero then
// the only possible solution is if the sum is zero.
if (n == 0)
{
if (sum == 0)
yield return Enumerable.Empty<int>();
yield break;
}
// If the number of items is less than the required number of
// items, there is no solution.
if (items.Count() < n)
yield break;
// We have at least n items in the sequence, and
// and n is greater than zero, so First() is valid:
int first = items.First();
// We need n items from a monotone increasing subsequence
// that have a particular sum. We might already be too
// large to meet that requirement:
if (n * first > sum)
yield break;
// There might be some solutions that involve the first element.
// Find them all.
foreach(var subsequence in M(items.Skip(1), sum - first, n - 1))
yield return new[]{first}.Concat(subsequence);
// And there might be some solutions that do not involve the first element.
// Find them all.
foreach(var subsequence in M(items.Skip(1), sum, n))
yield return subsequence;
}
static void Main()
{
int[] x = {0, 1, 2, 3, 4, 5};
for (int i = 0; i <= 15; ++i)
foreach(var seq in M(x, i, 4))
Console.WriteLine("({0}) SUM {1}", string.Join(",", seq), i);
}
}
using System;
using System.Collections.Generic;
using System.Linq;
abstract class ImmutableList<T> : IEnumerable<T>
{
public static readonly ImmutableList<T> Empty = new EmptyList();
private ImmutableList() {}
public abstract bool IsEmpty { get; }
public abstract T Head { get; }
public abstract ImmutableList<T> Tail { get; }
public ImmutableList<T> Push(T newHead)
{
return new List(newHead, this);
}
private sealed class EmptyList : ImmutableList<T>
{
public override bool IsEmpty { get { return true; } }
public override T Head { get { throw new InvalidOperationException(); } }
public override ImmutableList<T> Tail { get { throw new InvalidOperationException(); } }
}
private sealed class List : ImmutableList<T>
{
private readonly T head;
private readonly ImmutableList<T> tail;
public override bool IsEmpty { get { return false; } }
public override T Head { get { return head; } }
public override ImmutableList<T> Tail { get { return tail; } }
public List(T head, ImmutableList<T> tail)
{
this.head = head;
this.tail = tail;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
for (ImmutableList<T> current = this; !current.IsEmpty; current = current.Tail)
yield return current.Head;
}
}
class Program
{
// Preconditions:
// * items is a sequence of non-negative monotone increasing integers
// * n is the number of items to be in the subsequence
// * sum is the desired sum of that subsequence.
// Result:
// A sequence of subsequences of the original sequence where each
// subsequence has n items and the given sum.
static IEnumerable<ImmutableList<int>> M(ImmutableList<int> items, int sum, int n)
{
// Let's start by taking some easy outs. If the sum is negative
// then there is no solution. If the number of items in the
// subsequence is negative then there is no solution.
if (sum < 0 || n < 0)
yield break;
// If the number of items in the subsequence is zero then
// the only possible solution is if the sum is zero.
if (n == 0)
{
if (sum == 0)
yield return ImmutableList<int>.Empty;
yield break;
}
// If the number of items is less than the required number of
// items, there is no solution.
if (items.Count() < n)
yield break;
// We have at least n items in the sequence, and
// and n is greater than zero.
int first = items.Head;
// We need n items from a monotone increasing subsequence
// that have a particular sum. We might already be too
// large to meet that requirement:
if (n * first > sum)
yield break;
// There might be some solutions that involve the first element.
// Find them all.
foreach(var subsequence in M(items.Tail, sum - first, n - 1))
yield return subsequence.Push(first);
// And there might be some solutions that do not involve the first element.
// Find them all.
foreach(var subsequence in M(items.Tail, sum, n))
yield return subsequence;
}
static void Main()
{
ImmutableList<int> x = ImmutableList<int>.Empty.Push(5).
Push(4).Push(3).Push(2).Push(1).Push(0);
for (int i = 0; i <= 15; ++i)
foreach(var seq in M(x, i, 4))
Console.WriteLine("({0}) SUM {1}", string.Join(",", seq), i);
}
}
// Given a pool of elements returns all the
// combinations of the groups of lenght r in pool,
// such that the combinations are ordered (ascending) by the sum of
// the indexes of the elements.
// e.g. pool = {A,B,C,D,E} r = 3
// returns
// (A, B, C) indexes: (0, 1, 2) sum: 3
// (A, B, D) indexes: (0, 1, 3) sum: 4
// (A, B, E) indexes: (0, 1, 4) sum: 5
// (A, C, D) indexes: (0, 2, 3) sum: 5
// (A, C, E) indexes: (0, 2, 4) sum: 6
// (B, C, D) indexes: (1, 2, 3) sum: 6
// (A, D, E) indexes: (0, 3, 4) sum: 7
// (B, C, E) indexes: (1, 2, 4) sum: 7
// (B, D, E) indexes: (1, 3, 4) sum: 8
// (C, D, E) indexes: (2, 3, 4) sum: 9
public static IEnumerable<T[]>
GetCombinationsSortedByIndexSum<T>(this IList<T> pool, int r)
{
int n = pool.Count;
if (r > n)
throw new ArgumentException("r cannot be greater than pool size");
int minSum = F(r - 1);
int maxSum = F(n) - F(n - r - 1);
for (int sum = minSum; sum <= maxSum; sum++)
{
foreach (var indexes in AllSubSequencesWithGivenSum(0, n - 1, r, sum))
yield return indexes.Select(x => pool[x]).ToArray();
}
}
// Given a start element and a last element of a sequence of consecutive integers
// returns all the monotonically increasing subsequences of length "m" having sum "sum"
// e.g. seqFirstElement = 1, seqLastElement = 5, m = 3, sum = 8
// returns {1,2,5} and {1,3,4}
static IEnumerable<IEnumerable<int>>
AllSubSequencesWithGivenSum(int seqFirstElement, int seqLastElement, int m, int sum)
{
int lb = sum - F(seqLastElement) + F(seqLastElement - m + 1);
int ub = sum - F(seqFirstElement + m - 1) + F(seqFirstElement);
lb = Math.Max(seqFirstElement, lb);
ub = Math.Min(seqLastElement - m + 1, ub);
for (int i = lb; i <= ub; i++)
{
if (m == 1)
{
if (i == sum) // this check shouldn't be necessary anymore since LB/UB should automatically exclude wrong solutions
yield return new int[] { i };
}
else
{
foreach (var el in AllSubSequencesWithGivenSum(i + 1, seqLastElement, m - 1, sum - i))
yield return new int[] { i }.Concat(el);
}
}
}
// Formula to compute the sum of the numbers from 0 to n
// e.g. F(4) = 0 + 1 + 2 + 3 + 4 = 10
static int F(int n)
{
return (n * (n + 1)) / 2;
}