【问题标题】:Double linear sequence gives odd results双线性序列给出奇怪的结果
【发布时间】:2016-05-29 23:56:02
【问题描述】:

我正在努力使我的表演技能(不存在)达到标准,但在将公式写入代码时遇到了问题。这是我要尝试的公式 - 引用 unquote - “转换”为代码。

考虑一个序列 u,其中 u 定义如下:

号码u(0) = 1u 中的第一个号码。 对于u 中的每个x,那么y = 2 * x + 1z = 3 * x + 1 也必须在u 中。 u 中没有其他号码。 例如:u = [1, 3, 4, 7, 9, 10, 13, 15, 19, 21, 22, 27, ...]

1 给出34,然后3 给出7104 给出913,然后7@0 和@89 987654341@等等……

这就是我目前所拥有的:

using System;
using System.Collections.Generic;

public class Program
{
    public static void Main()
    {
        Console.WriteLine(DblLinear(10));
        //Expected result: 22
        Console.WriteLine(DblLinear(20));
        //Expected result: 57
        Console.WriteLine(DblLinear(30));
        //Expected result: 91
        Console.WriteLine(DblLinear(50));
        //Expected result: 175
    }
    public static int DblLinear (int n) 
    {
        List<int> linArr = new List<int>();
        linArr.Add(1);

        int i = 0;
        while(linArr.Count < n)
        {
            linArr.Add((2 * linArr[i]) + 1);
            linArr.Add((3 * linArr[i]) + 1);
            linArr.Sort();
            i++;
        }
        return linArr[n - 1];
    }
}

在达到 27 之前,计算都是正确的。在那之后,它就疯狂了,我不知道它做错了什么。

【问题讨论】:

  • 您是否使用调试器单步执行了代码?
  • 在添加之前,您没有检查号码是否已经存在。你会有重复的。

标签: c# performance sequence formula


【解决方案1】:

您从序列中取出一项并产生两项。所以你真的需要一些数据结构来存储它们,因为它们的数量会增加。堆将是最好的。如果您想直接使用 .net 中的内容,可以使用 SortedSet

public static IEnumerable<int> DblLinear2()
{
    SortedSet<int> seq = new SortedSet<int> { 1 };

    while (true)
    {
        int min = seq.First();
        seq.Remove(min);
        yield return min;
        seq.Add(min * 2 + 1);
        seq.Add(min * 3 + 1);
    }
}

结果:

1、3、4、7、9、10、13、15、19、21、22、27、28、31、39、40、43、45、46、 55、57、58、63、64、67、79、81、82、85、87...

【讨论】:

  • 这是完美的。一个简单的ElementAt() 调用,它会返回我扔给它的任何值。除此之外,也不用担心没有基于 null 的返回,这必须是解决所有问题的答案。也从来不知道SortedSet&lt;T&gt; 存在。一定要看看那个。
【解决方案2】:

问题在于您使用单个索引来构建您的值。

当您到达i == 27 时,根据您的示例,第一个系列产生55,第二个系列产生82。但是第一个也应该在第二个产生82之前产生57, 59, 61, 63, 65, 67, 69, 71, 73, 75, 77, 79, 81,以便正确计算您的序列。

您需要运行两个单独的索引,并且仅在它们各自系列产生的值最低(或等于另一个)时才增加每个索引。

我会这样做:

public static IEnumerable<int> DblLinear()
{
    Func<int, int> fxy = x => 2 * x + 1;
    Func<int, int> fxz = x => 3 * x + 1;

    var intermediate = new System.Collections.Generic.SortedSet<int>();

    Action<int> safeAdd = x =>
    {
        if (!intermediate.Contains(x))
        {
            intermediate.Add(x);
        }
    };

    safeAdd(1);

    while (true)
    {
        var x = intermediate.First();
        safeAdd(fxy(x));
        safeAdd(fxz(x));
        intermediate.Remove(x);
        yield return x;
    }
}

现在这可用于生成整个序列,因此最好使用 LINQ .Take(...) 运算符。

你可以这样使用它:

Console.WriteLine(String.Join(", ", DblLinear().Take(20)));

...这给了我:

1、3、4、7、9、10、13、15、19、21、22、27、28、31、39、40、43、45、46、55

您的测试代码,一旦修改以处理列表,就可以正常工作:

    Console.WriteLine(DblLinear().Skip(10).First());
    //Expected result: 22
    Console.WriteLine(DblLinear().Skip(20).First());
    //Expected result: 57
    Console.WriteLine(DblLinear().Skip(30).First());
    //Expected result: 91
    Console.WriteLine(DblLinear().Skip(50).First());
    //Expected result: 175

【讨论】:

  • 啊...这让他们相信我只是没有让 while 循环运行足够长的时间来在请求的索引处获得正确的值。我明白了,我明白了。
  • @sxbrentxs - 也许,但是您需要运行它直到第一个序列产生足够的值,然后您需要在返回值之前删除重复项并截断您的列表。使用我的方法会更有效率。
  • yi++ 方法是错误的,您应该将函数的输出作为下一个源,而不是简单的 1、2、3、... 序列
  • yi++ 产生序列 0, 1, 2, 3, 4,... 并由此计算 2*x+1 => 1, 3, 5, 7, 9,...但这与任务不对应。公式说 - 如果序列中有某个数字n,那么序列中也有2*n+13*n+1。并且没有其他数字。例如 5 不应该出现在结果中,因为 2 也不存在。
  • @sxbrentxs - 我已经修复了我的解决方案。它现在按预期工作。
猜你喜欢
  • 1970-01-01
  • 2017-01-07
  • 1970-01-01
  • 2013-08-20
  • 2019-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多