【问题标题】:Algorithm guidance with 3n+1 Conjecture3n+1猜想的算法指导
【发布时间】:2012-09-10 23:04:03
【问题描述】:

好的,我知道这听起来像是家庭作业;但无论如何。我正在尝试使用 C# 解决这个problem。问题描述摘录如下:

给定一个输入n,可以确定数字的个数 打印(包括1)。对于给定的 n,这称为 n的周期长度。在上面的例子中,循环长度 22 是 16。 对于任何两个数字 i 和 j,你要确定 i 和 j 之间所有数字的最大循环长度。

问题

除了一件事,周期长度,我什么都懂。我只是不完全理解。我发现文本对它的定义模棱两可。我假设,循环长度是序列中有多少个数字,所以可以说输入为 10,循环长度为 8。但我不确定。您不需要任何代码,但我只需要指导。

此外,我已经知道如何从标准输入和输出中读取。由于问题在于编程竞赛格式。

显示以 n 作为输入的数字序列的实现

static void collatz(ref int n)
{    
     if (n % 2 == 0)
     {
          n /= 2;
     }
     else
     {
          n = (3 * n) + 1;
     }
     Console.WriteLine(n);
}

static int GetCycleLength(int n)
{
     if (n > 0)
     {
          int count = 1;
          while (n != 1)
          {
               collatz(ref n);
               count++;
          }
          return count;
     }
     else
     {
          return -1;
     }
}

注意事项

虽然不是作业,但我想当作作业,所以我把它作为标签之一。

【问题讨论】:

  • 该页面明确定义了“周期长度”的含义。
  • 带有记忆的递归解决方案应该可以很好地工作。

标签: c# algorithm math collatz


【解决方案1】:

您应该使用动态编程。也就是说,如果您为数字 j 工作,并且在为 j 工作时遇到 k。那么你不应该再次为 k 重复整个工作。

所以从 j 开始,一直到 i。假设您正在寻找数字 n 的循环长度。在计算 n 的循环长度时,假设您遇到以下数字:n、n8、n7、n6、...、n1。然后,n 的循环长度为 9,n8 的循环长度为 8,n7 的循环长度为 7。将所有此类数字的循环长度存储在数组或映射中。当您遇到相同的数字以查找任何不同数字的周期长度时,并在任何可能的地方重复使用它们。这将为您提供解决此问题的最佳解决方案。

http://en.wikipedia.org/wiki/Dynamic_programming#Fibonacci_sequence查看动态编程的使用示例

【讨论】:

  • 是的,这绝对是个好主意。不过,对于这个特定问题中的n 值范围,以“幼稚”的方式进行处理应该不是什么大问题。编辑:从头开始,它实际上可能需要相当长的时间,周期长度不是均匀分布的,所以当你遍历你的数字范围时,你可能会遇到“粗糙点”。
  • 嗯..这是一个编程竞赛问题,解决方案的性能在那里很重要。另外,在我看来,这更像是一个算法问题,而不是一个编码问题。
  • 是的..它有时会特别在19岁之后遇到困难,下一个数字是58,然后是29,然后是88!
  • 是的。 en.wikipedia.org/wiki/Dynamic_programming#Fibonacci_sequence 表示这种技术也称为记忆化。
  • 一个解决方案只有 2^500
【解决方案2】:

循环长度是算法必须应用于原始自然数/整数才能最终达到 1 的次数。

例如,如果你从 7 开始:

  1. 7 是奇数,所以我们使用算法 3(7) + 1 = 22
  2. 22 是偶数所以我们使用 22/2 = 11
  3. 11 是奇数,所以我们使用算法 3(11) + 1 = 34
  4. 34 是偶数所以我们使用算法 34/2 = 17
  5. 17 是奇数,所以我们使用算法 3(17) + 1 = 52
  6. 52 是偶数所以我们使用算法 52/2 = 26
  7. 26 是偶数所以我们使用算法 26/2 = 13
  8. 13 是奇数,所以我们使用算法 13(3) + 1 = 40
  9. 40 是偶数所以我们使用算法 40/2 = 20
  10. 20 是偶数,所以我们使用算法 20/2 = 10
  11. 10 是偶数所以我们使用算法 10/2 = 5
  12. 5 是奇数,所以我们使用算法 5(3) + 1 = 16
  13. 16 是偶数所以我们使用算法 16/2 = 8
  14. 8 是偶数所以我们使用算法 8/2 = 4
  15. 4 是偶数所以我们使用算法 4/2 = 2
  16. 2 是偶数所以我们使用算法 2/2 = 1

我们对数字 7 应用了 16 次算法,得到了 1。所以,16 是循环长度。

代码性能提示 - 您可以使用按位运算,而不是在您的 collatz(ref int n) 中进行乘法和除法。这将大大提高性能。

【讨论】:

    【解决方案3】:

    对于特定的起始值 (n),循环长度 就是达到 1 所需的数字(包括末尾的 1)。对于10

    10 5 16 8 4 2 1

    所以循环长度是7。

    因此,对于这个问题,您必须从i 循环到j,确定您迭代的每个整数的循环长度,并返回您遇到的最大循环长度。您可能想查看另一个答案中提到的dynamic programming(即存储先前计算的周期长度,然后在将来的计算中使用这些存储的值)。

    【讨论】:

      【解决方案4】:

      所引用的文本对周期长度的定义并不含糊;它说:“给定一个输入 n,可以确定打印数字的数量……对于给定的 n,这称为 n 的循环长度”。例如,给定输入 20,引用过程打印出20 10 5 16 8 4 2 1,因此“20 的循环长度”为 8。您的程序在某种意义上模拟了 n 值范围的引用过程,查找并打印遇到的最大循环长度。

      请注意,使用动态编程的建议可能会有些误导。您实际可以使用的是memoization,即记录先前处理的输入的全部或部分结果。记录结果很麻烦,所以如果你的程序在没有记忆的情况下足够快,那就不要费心了。问题是假设“没有操作溢出 32 位整数”。如果您不做进一步的假设并记录所有中间结果,您需要一个能够处理数十亿不同值的字典数据结构。如果您对最大周期长度做出假设,您的程序可能会失败。无论如何,一种方法是选择一些大数 K 并分配一个整数数组 M;将数组初始化为全零,M[1] = 1 除外。在您的 Collat​​z 例程中,每次执行第 4 步 (ie n ← 3n+1) 时,都会将 1 推入位堆栈。 (堆栈中的位数应不小于最大循环长度。)每次执行步骤 5 (ie n ← n/2) 将 0 压入堆栈。

      如果你在任何一步找到M[n] > 0,那么你就知道你已经计算出n的循环长度为c = L = M[n]。此时,您可以展开位堆栈并返回 L。在展开时,每次弹出后,计算 n(如果弹出 0,则计算 n = 2*n,如果弹出 1,则计算 n = (n-1)/3),执行++c,如果n<K 设置M[n] = c

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-28
        相关资源
        最近更新 更多