【问题标题】:Compile-time LCM / GCD in CC中的编译时LCM / GCD
【发布时间】:2010-09-09 17:35:11
【问题描述】:

有谁知道在编译时计算 C不是 C++)中至少两个数字的 LCM(最小公倍数)和/或 GCD(最大公分母)的机制,我知道那里有模板魔法)?

我通常使用 GCC 并记得它可以在所有输入已知的编译时计算某些值(例如:sin、cos 等...)。

我正在寻找如何在 GCC 中执行此操作(最好以其他编译器可以处理的方式)并希望相同的机制在 Visual Studio 中也能工作。

【问题讨论】:

    标签: c visual-c++ gcc


    【解决方案1】:

    我终于明白了……

    #define GCD(a,b) ((a>=b)*GCD_1(a,b)+(a<b)*GCD_1(b,a))
    #define GCD_1(a,b) ((((!(b)))*(a)) + (!!(b))*GCD_2((b), (a)%((b)+!(b))))
    #define GCD_2(a,b) ((((!(b)))*(a)) + (!!(b))*GCD_3((b), (a)%((b)+!(b))))
    #define GCD_3(a,b) ((((!(b)))*(a)) + (!!(b))*GCD_4((b), (a)%((b)+!(b))))
    #define GCD_4(a,b) ((((!(b)))*(a)) + (!!(b))*GCD_5((b), (a)%((b)+!(b))))
    #define GCD_5(a,b) ((((!(b)))*(a)) + (!!(b))*GCD_6((b), (a)%((b)+!(b))))
    #define GCD_6(a,b) ((((!(b)))*(a)) + (!!(b))*GCD_7((b), (a)%((b)+!(b))))
    #define GCD_7(a,b) ((((!(b)))*(a)) + (!!(b))*GCD_8((b), (a)%((b)+!(b))))
    #define GCD_8(a,b) ((((!(b)))*(a)) + (!!(b))*GCD_last((b), (a)%((b)+!(b))))
    #define GCD_last(a,b) (a)
    
    #define LCM(a,b) (((a)*(b))/GCD(a,b))
    
    
    int main()
    {
        printf("%d, %d\n", GCD(21,6), LCM(21,6));
        return 0;
    }
    

    请注意,根据整数的大小,您可能需要包含更多中间步骤(即 GCD_9、GCD_10 等...)。

    我希望这会有所帮助!

    【讨论】:

    • 这看起来是一个有趣的解决方案...您知道在需要添加更多递归之前需要多少参数吗?今晚我必须测试一下...
    • 最坏的情况是两个连续的斐波那契数(参见en.wikipedia.org/wiki/Euclidean_algorithm 的“运行时间”部分)。对于 32 位无符号整数,这会产生 45 个中间步骤。不幸的是,MSVC 在我的机器上运行了 13 步。
    • 如果您的构建机器上有大量内存,或者如果更改 MSVC 中的设置以添加更多堆空间,则可以将其扩展到 45 步。
    • 最后要记住的是,事情通常进展得更快。对于我所有的随机示例,8 个步骤就足够了。根据您需要的数字,8 个步骤可能就足够了。
    • 将其更改为使用类似以下内容的序列:#define GCD_1(a,b) ( ((b) != 0) ? GCD_2((b), (a) % (b)) : (a) ) 可以帮助 MSVC,因为操作堆栈减少。我还怀疑 GCD_last 应该抛出某种错误......
    【解决方案2】:

    我知道您只对 C 实现感兴趣,但我想我还是会对 C++ 和模板元编程发表评论。我不完全相信在 C++ 中这是可能的,因为您需要明确定义的初始条件才能终止递归扩展。

    template<int A, int B>
    struct GCD {
        enum { value = GCD<B, A % B>::value };
    };
    
    /*
    Because GCD terminates when only one of the values is zero it is impossible to define a base condition to satisfy all GCD<N, 0>::value conditions
    */
    template<>
    struct GCD<A, 0> { // This is obviously not legal
        enum { value = A };
    };
    
    int main(void)
    {
        ::printf("gcd(%d, %d) = %d", 7, 35, GCD<7, 35>::value);
    }
    

    这可能在 C++0x 中是可能的,但不是 %100。

    【讨论】:

    【解决方案3】:

    部分基于 Kevin 的回答,这是一个宏序列,它对常量值有编译时错误,否则会出现运行时错误。

    如果不能选择失败,它也可以配置为引入非编译时函数。

    #define GCD(a,b) ( ((a) > (b)) ? ( GCD_1((a), (b)) ) : ( GCD_1((b), (a)) ) )
    
    #define GCD_1(a,b) ( ((b) == 0) ? (a) : GCD_2((b), (a) % (b) ) )
    #define GCD_2(a,b) ( ((b) == 0) ? (a) : GCD_3((b), (a) % (b) ) )
    #define GCD_3(a,b) ( ((b) == 0) ? (a) : GCD_4((b), (a) % (b) ) )
    #define GCD_4(a,b) ( ((b) == 0) ? (a) : GCD_5((b), (a) % (b) ) )
    #define GCD_5(a,b) ( ((b) == 0) ? (a) : GCD_6((b), (a) % (b) ) )
    #define GCD_6(a,b) ( ((b) == 0) ? (a) : GCD_7((b), (a) % (b) ) )
    #define GCD_7(a,b) ( ((b) == 0) ? (a) : GCD_8((b), (a) % (b) ) )
    #define GCD_8(a,b) ( ((b) == 0) ? (a) : GCD_9((b), (a) % (b) ) )
    #define GCD_9(a,b) (assert(0),-1)
    

    请注意扩展太大,即使它会提前终止,因为编译器必须在评估之前完全插入所有内容。

    【讨论】:

    • Err... 这给了我最初遇到的问题:错误 C2124:除以或除以零 :-(。你在什么编译器上测试过它?
    • 在用 '(a) % ((b)+!(b))' 替换 '(a) % (b)' 后,我能够编译它。但是,断言函数调用的存在不允许 MSVC 2005 优化计算。反汇编充满了测试和跳转调用。 :( [但是如果您删除断言调用,一切正常。]
    【解决方案4】:
     int gcd(int n1,int n2){
           while(n1!=n2){
            if(n1 > n2) n1 -= n2;
            else n2 -= n1;
        }
        return n1;
    }
    int lcm(int n1, int n2){
        int total =n1*n2;
        return total/gcd(n1,n2);
    }
    

    【讨论】:

    • @markSchulman 这没有回答问题。他们要求编译时解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多