天天看点

从666666整除7开始的算法思考

    源于一道简单的奥数题,快速判断666666是否能被7整除。参考的答案是前三位的666减去后三位的666,所得结果0能够被7整除,则该数可以被7整除。

原理总结与证明

    阅读了zhoushijingguo的新浪博客,才发现原来整除7的方法其实众多,这里就选取了两个给出自己的证明。

  • 末三位相减法

        对于一个多位数,用高位的数减去末三位的数,所得的结果如果能被7整除,则该数能够被7整除。否则,不能被7整除。

        以7位数6666666为例,高位6666减去末三位666结果为6000,不能被7整除,因此该数不能整除7。

        证明如下:

        假设 x 为任意整数,y为一至三位整数,则任意整数 I 都可以表示成x∗103+y 的形式,记 x 和y的差值的绝对值为 d 。由此,可得:

    I=x∗103+y(1)

    |y−x|=d(2)

        对公式(2)进行等价转换,可得:

    y=x±d(3)

        将(3)式代入(1)式,则有:

    I=x∗103+x±d=1001x±d(4)

        分析(4)式,我们知道,1001可以整除7,因此 1001x 必然也能整除7,若 d 也能整除7,则I整除7;否则不能整除。

        即一个多位数整数能否被7整除取决于高位与末三位的差值是否能被7整除。

  • 三位分组相减法

        可以看做是末三位相减法的拓展,但存在限制。实际手算时对于位数更多的多位数末三位相减法得到的差值仍然巨大,不便于计算。

        它的原理是将一个多位数按三位一节进行分组,从末向前编号1、2、3……,两两相邻奇数组减去偶数组得到的差值绝对值数组若均能整除7,则该多位数整除7。特别注意的是,差值不整除7不能得出该多位数不整除7。

        例如9位数766666666,三位分组则有三组,为666,666,007,000,最低三位666编号为1,则1组减2组的绝对值为0,3组减4组(自动补0)的绝对值为7,7能整除7,因此该9位数能整除7。

        证明如下:

        假设 xi(i=0,1,2...2N+1) 为任意三位整数(不足补0),则任意整数 I 都可以表示成∑2N+1i=0xi∗103i 的形式,记 x2k 和 x2k+1(k=0,1,2...N) 的差值的绝对值为 dk 。由此,可得:

    I=∑i=02N+1xi∗103i(5)

    |x2k+1−x2k|=dk(6)

        对公式(6)进行等价转换,可得:

    x2k=x2k+1±dk(7)

        将(7)式代入(5)式,则有:

    I=∑k=0Nx2k+1∗103∗(2k+1)+(x2k+1±dk)∗103∗2k=∑k=0N103∗2k∗(x2k+1∗103+x2k+1±dk)=∑k=0N103∗2k∗(1001x2k+1±dk)(8)

        分析(8)式,我们知道,1001可以整除7,因此 1001x2k+1 必然也能整除7,若 dk(k=0,1,2...N) 也能整除7,则 I 整除7;然而不能整除7时,也不能断定I不能整除7。

算法设计

    由于三位分组相减法存在限制,故只依据末三位相减法,对任意一个整数 I ,可以设计如下算法步骤判断是否能被7整除:

    step 1 :判断多位数整数I是否为6位数以内。是,转step 2;否,转step 4。

    step 2 :整数 d=| 高位减末三位 | 。

    step 3 :重新赋值,I=d,转step 1。

    step 4 :整数 d=| 高三位减末三位 | 。

    step 5 :若d整除7,则 I 整除7;否则I不整除7。

C语言实现

//整除7算法的C语言实现
#include <stdio.h>
#include <math.h>   
#include <time.h>   //用于计时

//末三位相减法——递归法
int Divide7_recursion(_int64 m){
    if(m < ){
        //判断是否为六位数以内
        if(abs(m%-m/) % == ){
            return ;
        }
        else{
            return ;
        }
    }
    else{ 
        return Divide7_recursion(m%-m/);
    }
}

//末三位相减法——循环
int Divide7_loop(_int64 m){
    //循环降位,直到六位数以内
    for(;m>=;m = abs(m% - m/));
    if(abs(m%-m/) % == ){
        return ;
    }
    else{
        return ;
    }
}

//普通整除7算法
int Divide7_normal(_int64 m){
    if(m% == ){
        return ;
    }
    else{
        return ;
    }
}

int main(void){
    _int64 i=,count=;
    clock_t start,end;  
    start = clock(); 
    //使用十二位数进行测试
    /*末三位相减法——递归
    for(i=999900000000;i<999999999999;i++){
        if(Divide7_recursion(i) == 1){
            count++;
        }
    }
    */
    /*末三位相减法——循环
    for(i=999900000000;i<999999999999;i++){
        if(Divide7_loop(i) == 1){
            count++;
        } 
    }
    */
    /*普通算法
    for(i=999000;i<999999;i++){
        if(Divide7_normal(i) == 1){
            count++;
        } 
    }
    */
    end = clock();  
    printf("time=%f\n,count=%lld\n",(double)(end-start)/CLK_TCK,count); 
    return ;
}
           

总结

    从证明过程可以发现,末三位相减法的真正核心是因为1001正好可以被7整除,因此对应的是三位整数。由此,完全可以进一步拓展该方法。例如,11能够整除11,对于整除11的数可以采用末一位相减法;1001也能够整除13,因此末三位相减法也适用于判断整除13的数……

    C语言代码也仅仅只是为了实现该算法,从汇编指令IDIV可以看出,虽然没有用技巧性的方法,但计算机设计时已经将除法算法统一用硬件实现,而本文写的整除算法建立在应用层,两者速度不存在可比性。

    第一次发文,单纯兴趣使然,若有不足,欢迎指点。

参考博客:

http://blog.sina.com.cn/s/blog_668e6e9d0100m6d5.html

继续阅读