【问题标题】:What is the problem with my code Longest Collatz Sequence?我的代码最长 Collat​​z 序列有什么问题?
【发布时间】:2020-04-11 12:24:20
【问题描述】:

我一直在努力解决这个问题:

下面的迭代序列是为正数的集合定义的 整数:

n → n/2(n 为偶数)
n → 3n + 1(n 为奇数)

使用上面的规则并从 13 开始,我们生成以下内容 顺序:
13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1

可以看出这个序列(从13开始到1结束) 包含 10 个术语。虽然还没有被证明(Collat​​z 问题),认为所有的起始数字都以 1 结束。

100 万以下的哪个起始数字产生最长的链?

注意:一旦链开始,条款就可以超过一个 百万。

我实现了下面的代码,但这似乎没有给我正确的答案。它计算 910107 作为给出最长链的起始数字,但答案应该是 837799。它有什么问题?

#include<stdio.h>

int main(void)
{

    int count=1;
    int last_count =0;
    int num=13;
    int temp;
    int Largest_Num=0;
    for(int i=num;i<1000000;i++)
    {
        temp = i;
        while(temp>1)
        {
            if(temp % 2 == 0)
            {
                temp/=2;
            }
            else
            {
                temp =(3*temp)+1;
            }
            count++;   

        }

        if(last_count < count)
        {
            last_count = count;
            Largest_Num = i;
        }

        count =1;
    }
    printf("%d\n",last_count);
    printf("%d",Largest_Num);

    return 0;
}

【问题讨论】:

    标签: c algorithm brute-force collatz


    【解决方案1】:

    我得到 910107 作为给出最长链的起始数字,但答案应该是 837799

    int 太短,无法管理 temp 所需的大量数字,您有溢出

    这意味着你的 int 是 32 位,在这种情况下 int 只使用 31 位作为正数,而你需要 32

    您可以将 temp 声明为 unsigned int(比 int 多 1 位,因为您只需要正数),或使用一个 long 如果它对你来说是 64 位,或者一个 long long 确保至少有 64 位

    【讨论】:

    • long long 保证在符合要求的实现中至少为 64 位宽。说有“很大的机会”并没有错,但有点误导。
    • 通过指出如何增强 OP 的代码以检测溢出,也可以改进这个答案。人们并不总是事先知道一个数据类型实际需要多宽,因此检测代码中溢出的想法和执行此操作的机制都是有用的信息。
    • @JohnBollinger temp 的最大值是 i 的值为 704511,我在想例如对于较高的素数 i 可以赋值,但不能。我在数学上还不够好,无法解释为什么当再乘以 3 个 1 时,会产生一长串奇数的数字
    • 这不是数学问题,@bruno,而是一个简单的计算问题。 temp 仅在此处被分配一个比当前值更大的值:temp =(3*temp)+1。只需要在执行之前检查它是否会溢出。
    • @JohnBollinger 对我来说这不是那么简单,因为在同一个循环中,数字可以除以 2。当然,有一个高达 1000000 的循环可能会产生疑问,但它只是 1000000 而不是 1000000000,在这里我们非常接近没有错误,只有 1 位丢失。
    【解决方案2】:

    正如@bruno 在他的回答中所观察到的,您超出了系统 int 数据类型的容量。该问题甚至暗示这是您可能会遇到的问题:

    注意:一旦链启动,条款允许超过一百万。

    多少?从问题中并不清楚,所以“我怎样才能确定什么数据类型就足够了?”是一个应该立即浮现在脑海中的问题。当然,前面的问题“我应该选择什么数据类型?”是一个应该总是得到至少片刻真正关注的人。

    假设您对要计算的序列没有先验知识,并且在一般情况下没有明确的方法来确定其元素的可靠上限,那么确定哪种数据类型就足够需要实际执行(尝试)计算和观察为溢出。或者,您可以将其描述为选择您认为足够的数据类型,并在进行过程中验证它实际上是否足够。如果不是,则使用不同的数据类型重试。

    在这种情况下,防止溢出很容易。唯一需要观察的变量是temp,它的值只有在计算temp =(3*temp)+1 时才会增加。你知道int 类型的最大值是多少(INT_MAX),并且很容易用代数方式确定temp 的最大值,计算不超过INT_MAX:它是(INT_MAX - 1) / 3。因此,您可以这样做:

    #include <assert.h>
    #define TEMP_BOUND ((INT_MAX - 1) / 3)
    
    // ...
    
                if(temp % 2 == 0) {
                    temp /= 2;
                } else {
                    assert(temp <= TEMP_BOUND);
                    temp = (3 * temp) + 1;
                }
    

    如果最终出现断言失败(就像使用类型 int 时一样),您可以使用支持更大最大值的数据类型重试;然后确保为新数据类型适当地更新 TEMP BOUND 宏。

    我还应该注意到,可以选择使用任意精度的数据类型执行计算,例如各种第三方库(例如 GMP)提供的数据类型。不过,这会慢得多,而且代码会更复杂,所以对于这样的情况,我确实建议使用上面概述的试错法,至少到这样的程度您确定没有内置数据类型足以解决问题。

    【讨论】:

    • 问题是当你有很多表达式时,它是容易的,因为你需要在几个更简单的表达式中剪切其中一些(以免丢失中间溢出)并插入所有需要的断言。如有疑问,使用 typedefint (假设该类型)并使用它来键入元素会更简单,然后更改 typedeflong long 或其他,如果可能的话,看看结果是否与返回到 int 相同(出于性能原因)。这就是我对 OP 代码所做的
    • 当然,@bruno,这是尝试不同数据类型的合理机制,除非您需要单独重新计算边界。但这里的重点是,如果您还不知道正在执行的计算的正确结果,那么您所描述的内容还不够
    • 是的,没有保证,那样希望不同类型的溢出效果不一样,以检测是否有问题,甚至如果不能工作long long 我遇到了麻烦,该怎么办? ;-)
    【解决方案3】:

    试试这个

    #include <stdio.h>
    #include <stdlib.h>
    
    int checklongestchain(unsigned int number){
            int count = 0;
            while(number > 1){
                    //printf(" %d  for %d\n",number,count);
                    if(number % 2 == 0){
                            number = number / 2;
                            count++;
                    }else{
                            number = (3 * number) +1;
                            count++;
                    }
            }
            return count;
    }
    
    int main(int argc, char **argv) {
            unsigned int NUMBER, s;
            unsigned int i;
            int no = 0;
            int maxcount = 0;
            NUMBER = atoi(argv[1]);
            for (i = 1; i < NUMBER; i++) {
                    no = checklongestchain(i);
                    printf("Chain Length For : %d  for %d\n",no,i);
                    if(no > maxcount){
                            maxcount = no;
                            s=i;
                            printf("Found New Largest Chain: %d  for %d\n",maxcount+1,s);
                    }
            }
            printf("Found Largest Chain: %d  for %d\n",maxcount+1,s);
            return 0;
    }
    

    gcc prog.c -o prog

    时间./prog 1000000

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-12-14
      • 1970-01-01
      • 2016-05-29
      • 2016-01-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多