使用基于整数除法的例程计数 - 是否存在公式化方法?

时间:2009-03-04 15:51:40

标签: algorithm language-agnostic math puzzle

考虑一个按连续除数w /余数运算计算的例程。

从64位被除数开始,例程除以常数除数 如果余数为0,则例程返回 否则,通过将余数乘以2 ^ 32并加上整数商来构造新的红利。

在代码中:

/// ULong - 64 bit, unsigned 
/// UInt  - 32 bit, unsigned 
const UInt Divisor; 
int TrickyCounter( ULong Dividend)
{
    int count = 0;
    Ulong Quotient;
    UInt Remainder;

    do {
        Quotient = Dividend/Divisor;
        Remainder = Dividend%Divisor;
        assert((Quotient >> 32) == 0);
        count = count + 1;
        Dividend = ((ULong)Remainder << 32) + Quotient;
    } while (Remainder != 0);
    return count;
}

使用任意除数,是否有一种优选的非迭代方法来计算必要的红利以获得所需的计数?
对于许多初始股息,这似乎很快就达到了“断言”状态。一些红利会导致这种情况永远循环吗?

<小时/> 如果例程返回商,而不是计数,我可以计算股息以产生我想要返回的数字吗?

Uint TrickyNumber( ULong Dividend, int count)
{
    Ulong Quotient = 0;
    UInt Remainder;

    while (count > 0)
        Quotient = Dividend/Divisor;
        Remainder = Dividend%Divisor;
        assert((Quotient >> 32) == 0);
        count = count - 1;
        Dividend = ((ULong)Remainder << 32) + Quotient;
    } 
    return (UInt)Quotient;
}

1 个答案:

答案 0 :(得分:1)

  

一些红利会导致这种情况永远循环吗?

Dividend = 0x1ffffffffL,Divisor = 2是一个相当明显的例子,整个家族(Divisor&lt;&lt; 32)-1,Divisor是固定点。

通过这些工作,可以找到许多初始红利和除数的循环组合,我相信还有更多:

#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>


size_t tricky_counter( uint64_t dividend, const uint32_t divisor )
{
    const size_t cycle_buffer_size = 1024;
    size_t count = 0;
    uint64_t quotient;
    uint32_t remainder;

    uint64_t pre[cycle_buffer_size];

    do {
        pre[ count % cycle_buffer_size ] = dividend;

        quotient = dividend/divisor;
        remainder = dividend%divisor;

        if ( (quotient >> 32) != 0) {
           printf("quotient: 0x%" PRIx64 "\n", quotient);
        }

        count = count + 1;

        dividend = ((uint64_t)remainder << 32) + quotient;

        for (size_t i = 0; i < count && i<cycle_buffer_size;++i) {
            if (pre[i] == dividend) {
                size_t cycle = 0;

                printf("dividend repeats: \n");

                while (i != count % cycle_buffer_size) {
                    //~ printf("  0x%" PRIx64 " / %" PRId32 " \n", pre[i], divisor);
                    i = (i + 1) % cycle_buffer_size;
                    ++cycle;
                }

                printf("  0x%" PRIx64 " / %" PRId32 "  cycle size %zd \n", dividend, divisor, cycle);

                return 0;
            }
        }

    } while (remainder != 0);

    return count;
}


int main ( void )
{
    for (uint64_t k = 1; k < 256; ++k) 
        for (uint64_t x = 2; x < 1024; ++x) 
            tricky_counter( (x-1 << 32) + 0x01010101L * k, x);    
}