找到可以从数组中的数字之和按升序形成的数字

时间:2015-10-10 03:57:34

标签: c# algorithm

我试图找到所有可能的值,这些值是给定数组的值之和的结果。例如,如果给定的数组是a = [50,100,120,260,360],那么结果将是[0,50,100,120,150,170,200,220,240,250,260,....]。如何实现呢?

我找到了一篇文章但它即将找到使用给定数组无法形成的值。

Find smallest number which can't be formed by values of given array

我发现了另外一个与此相关的讨论,但它完全是关于数学的,我仍然无法理解如何实现它。你可以看看它 Find all possible values which can be formed using some values

C#中的任何算法或任何代码都可以提供帮助。

修改

我们可以多次使用单个值。

更多结果可能是270(50 * 1 + 100 * 1 + 120),300(100 * 3),310(50 * 1 + 260 * 1)等。

4 个答案:

答案 0 :(得分:3)

这就是我使用的:

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) };

然后你可以这样做:

var a = new [] { 50, 100, 120, 260, 360 };

Console.WriteLine(String.Join(", ", getAllSubsets(a).Select(x => x.Sum()).OrderBy(x => x)));

我明白了:

  

0,50,100,120,150,170,220,260,270,310,360,360,380,410,   410,430,460,480,480,510,530,530,580,620,630,670,720,740,   770,790,840,890

知道值可以重复,这是一种方法:

public IEnumerable<int> GenerateAllSums(int[] array)
{
    var buffer = new LinkedList<int>();
    buffer.AddFirst(0);
    while (true)
    {
        var first = buffer.First;
        var nexts = array.Select(a => first.Value + a);
        foreach (var next in nexts)
        {
            var x = buffer.First;
            while (x.Value < next)
            {
                x = x.Next;
                if (x == null)
                {
                    break;
                }
            }
            if (x == null)
            {
                buffer.AddLast(next);
            }
            else if (x.Value != next)
            {
                buffer.AddBefore(x, next);
            }
        }
        buffer.RemoveFirst();
        yield return first.Value;
    }
}

我可以这样称呼它:

var a = new [] { 50, 100, 120, 260, 360, };

Console.WriteLine(String.Join(", ", GenerateAllSums(a).Take(100)));

重要的是要注意.Take(...)现在至关重要,因为序列是无限的。

鉴于.Take(100)我得到了这个结果:

  

0,50,100,120,150,170,200,220,240,250,260,270,290,300,   310,320,340,350,360,370,380,390,400,410,420,430,440,450,   460,470,480,490,500,510,520,530,540,550,560,570,580,590,   600,610,620,630,640,650,660,670,680,690,700,710,720,730,   740,750,760,770,780,790,800,810,820,830,840,850,860,870,   880,890,900,910,920,930,940,950,960,970,980,990,1000,   1010,1020,1030,1040,1050,1060,1070,1080,1090,1100,1110,   1120,1130,1140,1150,1160,1170

答案 1 :(得分:1)

使用类似的东西查找数组的所有子集然后求和,如果不需要重复的数组,则将获得所有可能的值。

 int[] source = new int[] { 50,100,120,260,360 };
 for (int i = 0; i < Math.Pow(2, source.Length); i++)
 {
     int[] combination = new int[source.Length];
     for (int j = 0; j < source.Length; j++)
     {
         if ((i & (1 << (source.Length - j - 1))) != 0)
         {
             combination[j] = source[j];
         }
    }
    Console.WriteLine("[{0}, {1}, {2}]", combination[0], combination[1], combination[2]);
}

答案 2 :(得分:0)

 var repeat = 8;
 int[] source = new int[] {
     50, 100, 120, 260, 360
 };
 List < int > results = new List < int > ();
 for (int i = 0; i < Math.Pow(repeat, source.Length); i++) {
     var sum = 0;
     var bin = Convert.ToString(i, repeat);
     for (var j = 0; j < bin.Length; j++) {
         var pos = int.Parse(bin[j].ToString());
         if (0 < pos) {
             sum += source[j] * pos;
         }
     }
     results.Add(sum);
 }
 Console.WriteLine(results.Union(source).Distinct().OrderBy(x = > x));

答案 3 :(得分:0)

这是最有效的方法:

public static class Algorithms
{
    public static IEnumerable<int> AllSums(this int[] source)
    {
        var indices = new int[source.Length];
        for (int count = 0, sum = 0, next = 0; ; next++)
        {
            if (next < source.Length)
            {
                indices[count++] = next;
                sum += source[next];
                yield return sum;
            }
            else
            {
                if (count == 0) break;
                next = indices[--count];
                sum -= source[next];
            }
        }
    }
}

样本用法:

var source = new[] { 50, 100, 120, 260, 360 };
Console.WriteLine("Source: {" + string.Join(", ", source.Select(n => n.ToString())) + "}");
Console.WriteLine("Sums: {" + string.Join(", ", source.AllSums().Select(n => n.ToString())) + "}");

var source = new[] { 50, 100, 120, 260, 360 };
foreach (var sum in source.AllSums())
{
    // do something with the sum
}