探讨关于 整数除法 和 取余 的映射规律

一、引子问题:

        一个简化的日期问题: 假设每一个月都是30天。假设盘古开天辟地的时候,是计算时间的开始(按照习惯,该天成为第1天,但是注意在程序中,表示的数字并不一定是1)。这里要计算的问题是:给定一个是从世界开始的一个总天数,让你计算该天属于从世界开始的第几个月,并且在该月中是第几天?

二、初步分析:

        看了上面的问题,是不是觉得非常简单?是的,确实非常简单,问题就是一种从“总天数”到“月份”或者“日期”的映射关系,使用一个除法,加上一个取余操作就可以了。但是要知道这篇文章就是围绕这个问题展开的,所以这里面一定有一些需要观察和思考的问题。

        上面关于“求解方法”(除法和取余)是正确的,但是当我们具体到写代码时,首先要做的就是确定这些信息如何表示,也就是编码问题。因为只有确定了编码问题,利用上面的方法,结果就一目了然了。下面我们讨论的问题就是有关“编码方式”的。

        在此之前,要先明确一点:整数除法”和“取余”的结果,取值范围都是从0开始的。例如:从0开始的正整数N,除以一个正整数M,那么其结果(N/M)是从0开始的; 取余的结果属于0<= N%M <= (M-1)。

三、编号方式详述:

        首先编码的对象:有两个:一个是对日期的编码;一个是对总天数的编码。

        其次是编码的方法:编码的方法也有两个:① 从0开始编码;② 从1开始编码。

        具体的编码如下描述:
                ① 对于月份:开始的第一个月的编号可以是0,也可以是1;
                ② 对于一个月中的日期编码:取值范围可以是0<=day<=29,也就是第一天的编号是 0;也可以是1<=day<=30,也就是第一天编号是1;
                ③ 对于总天数:第一天的编号是0(也可以认为表示方法中总天数中不包括当前天),也可能第一天的编号为1(总天数中包括当前天);

四、举例:

        为了清楚描述,将“总天数”记为sum,将“月份”记为month,将“一个月中的第几天(即日期)”记为day

        下面讨论第30天和第31天的表示:
             第30天:如果sum从1开始,那么值为30;如果从0开始,值为29;
             第31天:如果sum从1开始,那么值为31;如果从0开始,值为30。 

        计算月份 month:(使用除法) 
            1. 如果month从0开始编码: (总第30天月编号为0,总第31天月编号为1) 
                 1.1 如果sum从0开始(不包括当前天):
                        总第30天(sum==29)属于的月编号为0,计算方法为: 29/30;
                        总第31天(sum==30)月编号为1,计算方法为: 30/30,即公式为 sum/30。 
                 1.2 如果sum从1开始(包括当前天):
                        总第30天(sum==30)属于的月编号为0,计算方法为: (30-1)/30;
                        总 第31天(sum==31)月编号为1,计算方法为: (31-1)/30 ,即公式为(sum-1)/30。(注意:不是sum/30,否则第30天计算错误) ;
            2. 如果month从1开始编码:(总第30天月编号为1,总第31天月编号为2) 
                 2.1 如果sum从0开始(不包括当前天):
                        总第30天(sum==29)属于的月编号为1,计算方法为: 29/30 + 1;
                        总 第31天(sum==30)月编号为2,计算方法为: 30/30 +1,即公式为 sum/30 + 1。 
                 2.2 如果sum从1开始(包括当前天):
                        总第30天(sum==30)属于的月编号为1,计算方法为: (30-1)/30 +1;
                        总第31天(sum==31)月编号为2,计算方法为: (31-1)/30 + 1,,即公式为(sum-1)/30 + 1

        计算日期 day(使用取余) 
            1. 如果day从0开始编码: (总第30天日期为29,总第31天日期为0) 
                 1.1 如果sum从0开始(不包括当前天):
                        总第30天(sum==29)属于的日期为29,计算方法为: 29%30;
                        总第31天(sum==30)日期为0,计算方法为: 30%30,即公式为 sum%30。 
                 1.2 如果sum从1开始(包括当前天):
                        总第30天(sum==30)属于的日期为29,计算方法为: (30-1)%30;
                        总第31天(sum==31)日期为0,计算方法为: (31-1)%30 。即公式为(sum-1)%30。(注意:不是sum%30,否则第30天计算错误) 
            2. 如果day从1开始编码:(总第30天日期为30,总第31天日期为1) 
                 2.1 如果sum从0开始(不包括当前天):
                        总第30天(sum==29)属于的日期为30,计算方法为: 29%30 + 1;
                        总第31天(sum==30)日期为1,计算方法为: 30%30 +1,即公式为 sum%30 + 1。 
                2.2 如果sum从1开始(包括当前天):
                        总第30天(sum==30)属于的日期为30,计算方法为: (30-1)%30 +1;
                        总第31天(sum==31)日期为1,计算方法为: (31-1)%30 +1,即公式为(sum-1)%30 + 1

五、问题推广:

        对于序列中的一个数n,要划入到一个集合中,每个集合中含有元素都是m个。其中n和m都既可以从0开始编号,也可以从1开始编号。要求计算n被分到了第几个集合,以及在该集合中的下标。

六、解法图示:

除法 和 取余 运算方法

七、规律总结:

        1. 对于月份和日期:如果它们从0开始,那么除法或者取余结果就是要求的结果;但是如果从1开始,需要在上面的加1;
                原因:因为除法取余的结果,范围都是从0开始所以,当月份和日期从1开始时,所以要加1。

        2. 如果总天数 n 0开始,只需要直接除以m 或对m取余(当然,还要根据月份和日期是否从1开始,来判断是否需要加1) ;若总天数从1开始,都要先减1,再除以m或者再对m取余;

        3. 巧记公式:是否减1看“总天数”是否从1开始,是否加1看“月份或日期”是否从1开始

八、联想:

        出现本篇文章的直接原因:是因为整数除法取余的结果范围是从0开始的。那么我们是否会联想到一个编程时常见的也是从0开始的东东——数组。那么,可能在我们刚刚学编程时,发现数组从0开始,是那么不习惯。可是到底为什么数组下标从0开始呢,也许你很可能会说因为数组首个元素的地址偏移量为0,将数组的下标从0开始,便于对数组进行按下标访问时的转化。确实是这样的,我无法和 Dennis M Ritchie沟通它设计C语言时,为何要这样设计,甚至或者他也是向当时已经存在的编程语言借鉴的这种做法。
        这里,我想说的是,也许“整数除法和取余结果是从0开始的”也可能是数组下标从0开始,而不是从1开始的一个原因。

本文链接:http://blog.csdn.net/daheiantian/archive/2011/03/14/6534952.aspx

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值