某年的6月1日是星期日。那么,同一年的6月30日是星期几?
星期是7天一个循环。所以说,这一天是星期几,7天之后同样也是星期几。而6月30日是在6月1日的29天之后:29 ÷ 7 = 4 ... 1用29除以7,可以得出余数为1。而6月1日那一天是星期日,那么,6月30日就是星期日的后一天,也就是星期一。
某年的3月,Wendy想制定一个实习计划,它想知道当年的7月和11月有哪几天是星期天。但月历坏了,5月后面的几页看不到了。Wendy却轻松找到了想要的日期,难道Wendy是天才喵?
不是,因为它知道,每一年的3月1日到3月30日,与11月1日到11月30日,在星期上面是完全相同的。同样,4月1日到4月30日,与7月1日到7月30日,在星期上面也是完全相同的(31日除外)。
这到底是怎么一回事呀?
每一年过了3月之后,在天数上,4月、6月、9月、11月都是30天,而其他的月份都是31天。
把3月到10月的天数相加,得出:31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 = 245。
245 ÷ 7= 35,用245除以一周的天数7天,就可以得出,3月1日~3月30日,与11月1日~11月30日,在星期上面是完全相同的。
同样,把4月到6月的天数相加,得出:30 + 31 + 30 = 91。
91 ÷ 7 = 13,就可以得出,4月1日到4月30日,与7月1日到7月30日,在星期上面也是完全相同的。
原来,数字有规律性和周期性。高斯于是提出了“同余式”的理论。
当a除以m所得余数,和b除以m所得余数相同的时候,就将它写成:a ≡ b(mod m)。称为整数a,b对模m同余。而上面的这个算式就是同余式。在这里,mod表示整除后取余的意思。同C语言的%运算符的功能是一样的。
比如3 ≡ 1 (mod 2),27 ≡ 2 (mod 5)。
同余式可以相加。
比如,当除以7的时候,10和17的余数为3,9和16的余数为2:10 ≡ 17(mod 7),9 ≡ 16 (mod 7)。
将10 + 9和17 + 16分别再除以7,得出的余数都是3 + 2 (5),也就是说:10 + 9 ≡ 17 + 16 (mod 7)。
不光是相加,相减,或者相乘(乘方),同余式依然成立。

同余式和其他的等式一样,可以进行加法、减法、乘法(乘方)的运算。
求:13的2000次方除以12
13 ≡ 1 (mod12),根据同余式的性质④,能够得出:13^2000 ≡ 1^2000 ≡ 1 (mod 12)。余数为1。
求:斐波那契数列中是3的倍数的项
如果用通项求:

可能不够优雅。
不妨根据这个数列的递推方程式,求出一开始几项的具体数字,然后将这些数字除以3,得出余数。

我们想要找到整数除法运算当中“余数”的周期和规律。
假设:
a n + 1 = 3 p + k a_{n+1}=3p+k an+1=3p+k
a n = 3 q + l a_{n}=3q+l an=3q+l
k和l都是0,1,2三个数字当中的某个整数。
an+1除以3的余数为k,而k除以3的余数也为k,可以得出: a n + 1 ≡ k ( m o d 3 ) a_{n+1} ≡ k (mod3) an+1≡k(mod3)。
同样,an除以3的余数为l,而l除以3的余数也为l,可以得出: a n ≡ l ( m o d 3 ) a_{n} ≡ l (mod3) an≡l(mod3)。
根据递推方程式: a n + 2 = a n + 1 + a n a_{n+2} = a_{n+1} + a_n an+2=an+1+an
根据同余式的性质①,得出: a n + 2 ≡ a n + 1 + a n ≡ k + l ( m o d 3 ) a_{n+2} ≡ a_{n+1} + a_n ≡ k+l (mod 3) an+2≡an+1+an≡k+l(mod3)。
也就是说,想要求出an+2除以3的余数,就必须先求出an+1和an除以3的余数,然后将两个余数相加。
通过先前的表格,我们可以看出,如果连续2个余数和前面的某2个余数相同的话,那么,此后的余数都按照这个形式循环下去。
比如,第9项和第10项的余数与第1项和第2项的余数相同,所以当这个数列除以3的时候,所得出的余数的周期为8。
一开始的8项当中,a4和a8能够被3整除。
所以,n的条件为,n是4的倍数:4,8,12,16,20,24,28,32,···。
线性同余法获取伪随机数
在C语言中,只要调用rand()函数,就可以得到随机数。不过,由于借助公式产生的随机数具有一定的规律性,因此并不是真正的随机数,通常称为伪随机数。
线性同余法:如果把Ri作为当前随机数的话,那么下一个出现的随机数Ri + 1就是, R i + 1 = ( a × R i + b ) m o d c R_{i+1}=(a × R_i+b) mod c Ri+1=(a×Ri+b)modc
对a、b、c各参数设定合适的整数后,可以从该公式获得的随机数的范围就是0到c(不包含)。例如,把a设定为5,b设定为3,c设定为8,Ri的初始值定为了1。

这些随机数确实很像是无规则随机出现的数值。不过,产生8次随机数后,下8次产生的随机数就和前面的数值相同了。这种周期性是伪随机数的特征,也是为什么不是真随机数的原因。
srand(time(NULL));可以提前设定Ri、a、b、c的数值。srand()函数中的参数time(NULL),是用来获取当前时间的参数。由于每次启动程序时的当前时间都是变化的,因此Ri、a、b、c的数值也会随之发生变化。Ri、a、b、c的数值就称为随机数的种子。
而重复调用rand()函数的话,因为Ri、a、b、c的数值都有默认值,因此每次都会生成以相同方式出现的随机数。
参考
[1] 写给全人类的数学魔法书
[2] 程序是怎样跑起来的