尝试生成指定数字的所有序列,最大总和

时间:2010-04-07 17:08:10

标签: c# linq algorithm sequences

鉴于下面的降序唯一数字列表(3,2,1),我希望生成由这些数字组成的所有序列,最大总和。

让我们说总和应该低于10.然后我所追求的序列是:

3 3 3
3 3 2 1
3 3 2
3 3 1 1 1
3 3 1 1
3 3 1
3 3
3 2 2 2
3 2 2 1 1
3 2 2 1
3 2 2
3 2 1 1 1 1
3 2 1 1 1
3 2 1 1
3 2 1
3 2
3 1 1 1 1 1 1
3 1 1 1 1 1
3 1 1 1 1
3 1 1 1
3 1 1
3 1
3
2 2 2 2 1
2 2 2 2
2 2 2 1 1 1
2 2 2 1 1
2 2 2 1
2 2 2
2 2 1 1 1 1 1
2 2 1 1 1 1
2 2 1 1 1
2 2 1 1
2 2 1
2 2
2 1 1 1 1 1 1 1
2 1 1 1 1 1 1
2 1 1 1 1 1
2 1 1 1 1
2 1 1 1
2 1 1
2 1
2
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1
1 1 1 1
1 1 1
1 1
1

我确信有一种“标准”方式来生成它。

我想过使用linq但是无法弄明白。 此外,我正在尝试基于堆栈的方法,但我仍然没有成功。

有什么想法吗?

5 个答案:

答案 0 :(得分:2)

我认为这是最简单的递归写法,纯粹的LINQ并不是那么好用。所以最好将它作为一个普通的递归函数来实现。将最大总数中剩余的数量作为参数传递,每次添加en元素时,递归调用函数,适当减少总数:

IEnumerable<IEnumerable<int>> sequences(IEnumerable<int> set, int maxTotal)
{
    foreach (int i in set.Where(x => x <= maxTotal))
    {
        yield return new int[] { i };
        foreach (var s in sequences(set.Where(x => x <= i), maxTotal - i))
            yield return (new int[] { i }).Concat(s);
    }
}

void Run()
{
    foreach (var z in sequences(new int[] { 3, 2, 1 }, 10))
    {
        Console.WriteLine(
            string.Join(" ", z.Select(x => x.ToString()).ToArray())
        );
    }
}

结果:

3
3 3
3 3 3
3 3 3 1
3 3 2
3 3 2 2
3 3 2 1
3 3 2 1 1
3 3 1
3 3 1 1
3 3 1 1 1
...
1 1 1 1 1
1 1 1 1 1 1
1 1 1 1 1 1 1
1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1

请注意,这不是最有效的解决方案。

答案 1 :(得分:0)

我认为您可以通过构建树来解决这个问题。 在每个节点上,您都有一个值列表。假设此列表的总和小于所需的总和 - 让我们称之为S - ,您最多可以为此节点构建三个子节点:一个通过向此节点的列表添加1,一个通过添加2和一个通过添加3建立孩子的条件是新列表的总和仍然小于S. 最后,即当您无法生成新节点时,您的所有序列都在树的节点中。

编辑:在C#中,我的糟糕解释就是这样:

第一

public class Node
{
    public Node()
    {
        Children = new List<Node>();
    }

    public static int SumMax { get; set; }

    public List<int> Values { get; set; }

    public List<Node> Children { get; set; }

    public void AddChild(int data)
    {
        if (Values.Sum() + data < SumMax)
        {
            Node child = new Node();
            child.Values = new List<int>(Values);
            child.Values.Add(data);
            Children.Add(child);

            for (int = data; i < 4; i++)
            {
                child.AddChild(i);
            }
        }
    }

    public void FillSequences(List<List<int>> sequences)
    {
        if (Values.Count != 0)
        {
            sequences.Add(Values);
        }

        foreach (Node child in Children)
        {
            child.FillSequences(sequences);
        }
    }
}

然后是主要的:

void Main()
{
    Node.SumMax = 10;
    Node root = new Node();
    root.Values = new List<int>();
    for (int i = 1; i < 4; i++)
        root.AddChild(i);

    List<List<int>> sequences = new List<List<int>>();
    root.FillSequences(sequences);

    //here you've got your sequences results in "sequences" and you can do what you want
}

我不知道它是否足够标准,但它大致完成了工作。我希望它符合你的需要......

编辑:为了避免生成相同的序列,我们可以“命令”树:节点无法生成值低于其值的子节点。因此,在AddChild方法中,我们在“data”而不是1处开始循环。

答案 2 :(得分:0)

我不知道这是否是“标准”,但从底部开始并开始工作并不罕见。

有1种方法可以制作1。

有两种方法可以制作2,分为3类:以1开头的那些,以2开头的那些,以及以3开头的那些。最后一类当然是空的。

要计算制作N的方法,请考虑使N-1从1开始的方法,使N-2以2或1开始的方法,以及使N-3以3,2开始的方法,或者1。

答案 3 :(得分:0)

在我看来,“最简单”(如果不是最有效的)这样做的方法就是只取你的数字设置,然后取出它的所有排列,然后过滤出总和高于截止点。

答案 4 :(得分:0)

好像你对partitions感兴趣,有点麻烦。这个功能起到了作用。基本上,在它们的总和小于目标总和时继续向堆栈添加数字,然后在每一步打印堆栈。

class Program
{
    static void Main()
    {
        GeneratePossibilites(new int[] {3, 2, 1}, 10, 0, new List<int>());
    }

    static void GeneratePossibilites(int[] array, int maxSum, int crSum, List<int> stack)
    {
        for (int i = 0; i < stack.Count; ++i )
            Console.Write(array[ stack[i] ].ToString() + " ");

        int start = 0;
        if (stack.Count > 0)
        {
            start = stack[stack.Count - 1];
            Console.WriteLine();
        }
        for (int i = start; i < array.Length; ++i)
            if (crSum + array[i] < maxSum)
            {
                stack.Add(i);
                GeneratePossibilites(array, maxSum, crSum + array[i], stack);
                stack.RemoveAt(stack.Count - 1);
            }
    }
}

我不确定您是否需要特定格式的输出,但您可以使用此答案或其他答案。