创建所有可能的数组而不使用嵌套for循环

时间:2017-01-16 20:01:46

标签: c# arrays for-loop recursion

我想生成长度为fp2 = fopen(tmp, "w"); 的所有可能数字,并且我的数字的每个数字都具有集合n中的值,作为数组。换句话说,我想列出所有不包含{1,2,...,n-1}的{​​{1}}基数n

现在,我能想到的唯一方法就是嵌套n n个循环,并将0分配给第(i + 1)个循环,即

for

然后在最里面的循环中打印每个数组。显而易见的问题是,对于每个n,我需要手动编写myArray[i] int n; int[] myArray = new int[n]; for (int i1 = 1; i1 < n; i1++) myArray[0]=i1; for (int i2 = 1; i2 < n; i2++) myArray[1]=i2; // and so on.... for (int in = 1; in < n; in++) { myArray[n]=in; foreach (var item in myArray) Console.Write(item.ToString()); Console.Write(Environment.NewLine); } 个循环。

根据我的阅读,递归似乎是替换嵌套n循环的最佳方法,但我似乎无法弄清楚如何为递归制作一般方法。

修改

例如,如果for,我想写出forn=31 1 11 1 21 2 1,{ {1}},1 2 22 1 1

我们不仅限于2 1 2。例如,如果2 2 1,我们会输出

2 2 2

因此,数字的数字可以是n<11n=11之间的任何值。数组1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 3 ... 1 1 1 1 1 1 1 1 1 1 10 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 2 3 ... 1 1 1 1 1 1 1 1 1 9 10 1 1 1 1 1 1 1 1 1 10 1 1 1 1 1 1 1 1 1 1 10 2 1 1 1 1 1 1 1 1 1 10 3 ... 10 10 10 10 10 10 10 10 10 9 10 10 10 10 10 10 10 10 10 10 10 1 10 10 10 10 10 10 10 10 10 10 2 ... 10 10 10 10 10 10 10 10 10 10 10 只是用于获取其中一个数字,然后我们打印它,然后继续下一个数字并重复。

1 个答案:

答案 0 :(得分:3)

与往常一样,在考虑递归解决方案时,尝试使用不可变结构解决问题;一切都更容易理解。

首先,让我们自己构建一个快速的小不可变堆栈,它将帮助我们跟踪我们当前生成的数量(同时不担心在递归调用中生成的其他数字...记住,不可变数据无法改变!):

public class ImmutableStack<T>: IEnumerable<T>
{
    public static readonly ImmutableStack<T> Empty = new ImmutableStack<T>();
    private readonly T first;
    private readonly ImmutableStack<T> rest;

    public int Count { get; }

    private ImmutableStack()
    {
        Count = 0;
    }

    private ImmutableStack(T first, ImmutableStack<T> rest)
    {
        Debug.Assert(rest != null);

        this.first = first;
        this.rest = rest;
        Count = rest.Count + 1;
    }

    public IEnumerator<T> GetEnumerator()
    {
        var current = this;

        while (current != Empty)
        {
            yield return current.first;
            current = current.rest;
        }
    }

    public T Peek()
    {
        if (this == Empty)
            throw new InvalidOperationException("Can not peek an empty stack.");

        return first;
    }

    public ImmutableStack<T> Pop()
    {
        if (this == Empty)
            throw new InvalidOperationException("Can not pop an empty stack.");

        return rest;
    }

    public ImmutableStack<T> Push(T item) => new ImmutableStack<T>(item, this);

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

这很容易。请注意堆栈如何重用数据。在我们的小程序中会有多少个空的不可变结构?只有一个。包含序列1->2->4的堆栈?是的,只有一个。

现在,我们实现了一个递归函数,它不断向堆栈添加数字,直到我们达到“纾困”状态。哪个是?当堆栈包含n个元素时。容易腻:

private static IEnumerable<int> generateNumbers(ImmutableStack<string> digits, IEnumerable<string> set, int length)
{
    if (digits.Count == length)
    {
        yield return int.Parse(string.Concat(digits));
    }
    else
    {
        foreach (var digit in set)
        {
            var newDigits = digits.Push(digit);

            foreach (var newNumber in generateNumbers(newDigits, set, length))
            {
                yield return newNumber;
            }
        }
    }
}

好的,现在我们只需要将它们与我们的公共方法联系在一起:

 public static IEnumerable<int> GenerateNumbers(int length)
 {
     if (length < 1)
         throw new ArgumentOutOfRangeException(nameof(length));

     return generateNumbers(ImmutableStack<string>.Empty,
                            Enumerable.Range(1, length - 1).Select(d => d.ToString(CultureInfo.InvariantCulture)),
                            length);
 }

果然,如果我们称之为:

 var ns = GenerateNumbers(3);
 Console.WriteLine(string.Join(Environment.NewLine, 
                               ns.Select((n, index) => $"[{index + 1}]\t: {n}")));

我们得到预期的输出:

[1]     : 111
[2]     : 211
[3]     : 121
[4]     : 221
[5]     : 112
[6]     : 212
[7]     : 122
[8]     : 222

请注意,指定长度n生成的总数量为(n - 1) ^ n,这意味着对于相对较小的length值,您将获得相当多的数字生成的; n = 10生成3 486 784 401 ...