懒惰地生成所有正数长数的有序数,只使用数字“0”和“数”。 `9`

时间:2014-03-02 01:30:00

标签: c# linq lambda ienumerable generator

当我回答这个问题时 - An algorithm for a number divisible to n - 我提出了一个解决方案,可以生成所有正数长数的有序数,只使用数字0& 9

但是我对我提出的两个选项真的不满意。第一个不是懒惰的,基本上是在返回第一个值之前生成的。

第二个更接近标记,但逻辑相当复杂,包含许多“神奇”数字

我很想知道是否有人可以为我提出快速懒惰的解决方案,最好使用签名Func<IEnumerable<long>> generate = ...;或其他IEnumerable<long> Generate() { ... }

我的第一个选择是:

Func<IEnumerable<long>> generateA = () =>
{
    Func<long, IEnumerable<long>> extend =
        x => new [] { x * 10, x * 10 + 9 };

    Func<long, IEnumerable<long>> generate2 = null;
    generate2 = x =>
        x <= 0L
        ? Enumerable.Empty<long>() 
        : new [] { x }
            .Concat(
                extend(x)
                    .SelectMany(y => generate2(y)))
            .OrderBy(z => z);

    return generate2(9L);
};

第二个是:

Func<IEnumerable<long>> generateB = () =>
{
    var powers =
        Enumerable
            .Range(0, 18)
            .Aggregate(
                new List<long>() { 1L },
                (a, _) => { a.Add(a.Last() * 10); return a; })
            .ToArray();

    return Enumerable
        .Range(1, 393215) //largest expressable value using `0` & `9` only.
        .Select(i => new BitArray(new [] { i })
            .Cast<bool>()
            .Take(19) //largest expressable value using `0` & `9` only.
            .Select((x, n2) => (x ? 9L : 0L) * powers[n2])
            .Sum());
};

两种方法都返回393,215个值,以9开头,以9,099,999,999,999,999,999结尾。

任何想要使方法变得懒惰和简单的想法?

2 个答案:

答案 0 :(得分:3)

我认为使用yield return编写可理解的,懒惰的方法更容易,这在Lambdas中是不可用的。尝试仅使用LINQ执行此操作会让人更难理解。

public class Program
{
    public static void Main(string[] args)
    {
        var items = Generate();

        var first = items.Take(8);
        var count = items.Count();
        var last = items.Skip(count - 4);
        Console.WriteLine("First items: {0}", string.Join(", ", first));
        Console.WriteLine("Last items: {0}", string.Join(", ", last));
        Console.WriteLine("Count: {0}", count);
    }

    private static IEnumerable<long> Generate()
    {
        return GenerateAll().TakeWhile(i => i >= 0);            
    }

    // generates an infinite sequence using GenerateNext
    private static IEnumerable<long> GenerateAll()
    {
        IEnumerable<long> items = new[] { 9L };

        while(true)
        {
            foreach(var item in items)
            {
                yield return item;
            }
            items = GenerateNext(items);
        }
    }

    // generates the next items in the sequence.
    private static IEnumerable<long> GenerateNext(IEnumerable<long> xs)
    {
        foreach(var x in xs)
        {
            long x2 = 10 * x;
            yield return x2;
            yield return x2 + 9;
        }            
    }
}

输出:

  

第一项:9,90,99,900,909,990,999,9000
  最后项目:9099999999999999900,9099999999999999909,9099999999999999990,9099999999999999999
  数:393215

答案 1 :(得分:0)

仅仅为了记录,我的最终答案受到了“Mike Z”的启发 - 我将给出答案 - 它是:

Func<IEnumerable<long>> generate = () =>
{
    Func<long, IEnumerable<long>> extend =
        x => new [] { x * 10, x * 10 + 9 };

    Func<IEnumerable<long>, IEnumerable<long>> generate2 = null;
    generate2 = ns =>
    {
        var clean = ns.Where(n => n > 0).ToArray();
        return clean.Any()
            ? clean.Concat(generate2(clean.SelectMany(extend)))
            : Enumerable.Empty<long>();
    };

    return generate2(new[] { 9L, });
};

事实证明这是懒惰的,而且非常快。