数论笔记Ⅱ
数论笔记Ⅱ
前言
数论Day2,全程划水 此处数论1
隔了俩月了快,转眼年也过了一个半月了。
发现U盘之前还没写完的 Day3,一直咕着也没把不会的写了,想了想还是发出来吧,毕竟还有些写了的。
01分数规划
基本01分数规划问题:给定一个二元组 \((value_i,cost_i)\),\(value_i\) 是选择二元组获得的价值,\(cost_i\) 是选择此二元组所付出的代价,设 \(x_i(x_i \in {0,1})\) 代表第 \(i\) 个二元组选与不选,最大(小)化该式:\(maximize(minimize) r= \frac{\sum value_i \cdot x_i}{\sum cost_i \cdot x_i}\)
采用二分法,以最大化举例。
设 \(r\) 最大值为 \(r^*\),则 \(r^*= \frac{\sum value_i \cdot x_i}{\sum cost_i \cdot x_i}\),\(\displaystyle \sum value_i \cdot x_i -r^* \cdot \sum csot_i \cdot x_i=0\),设函数 \(f(r)=\displaystyle \sum value_i \cdot x_i -r^* \cdot \sum csot_i \cdot x_i\),观察这个函数,形如 \(y=b-kx\) 的形式,每组 \(x_i\) 对应一组直线,直线斜率非正( \(-k=-\sum cost_i \cdot x_i \le 0\) ),纵截距非负 ( \(b=\sum value_i \cdot x_i \ge 0\) ),对于每条直线,当 \(f(r)=0\) 时,横截距就是这组的 \(r\) ,我们要使这些横截距最大且保证 \(f(0)=0\),明显二分这个\(r^*\),如果点 \(f(r)\) 之和为负数,将区间向左改小,反之向右,我们怎么其 \(f(r)\)?对式子变形即可: \(f(r)=\displaystyle \sum value_i \cdot x_i -r^* \cdot \sum csot_i \cdot x_i\)等价于: \(f(r)=\displaystyle \sum (value_i - r \cdot cost_i) \cdot x_i\)。
[POJ2976] Dropping tests 题目&题解,自行领会。
Miller-Rabin素数测试法
有时候我们想快速知道一个数是不是素数,而这个数又很大导致 \(\mathcal{O(\sqrt{n})}\) 算法内不能通过,而目前又没有完美的算法解决这个问题,所以我们用 \(Miller-Rabin\) 素数测试,可以大概率测出其是否为素数。
理论基础:
费马小定理:\(a^{p-1} \equiv 1(\bmod p)\),\(a,p\)互质
二次探测:如果 \(p\) 是一个素数, $0 <x <p $,则方程 \(x^2 \equiv 1(\bmod p)\) 的解为 \(x=1\) 或者 \(x=p-1\)
费马小定理有 \(a^{p-1} \equiv 1(\bmod p)\),那是不是满足这个条件的 \(p\) 都是素数呢?当然在费马小定理被证明的很长时间里都是这么认为的,但是有一天,一位快递小哥发现了卡迈克尔数_百度百科推翻了这个结论,即存在合数也满足 \(a^{p-1} \equiv 1(\bmod p)\),那是否意味着利用费马小定理判断素数是错误的呢?没错就是错误的,但是如果我们人为把出错几率降到非常小呢?例如对于一个数,我们有 \(99.9999 \%\) 的几率做出正确判断,那这种算法也是优越的。
再说二次探测定理,证明过程如下:\(x^2 \equiv 1(\bmod p) \rightarrow x^2-1 \equiv 0(\bmod p) \rightarrow (x-1) \cdot (x+1) \equiv 0(\bmod p) \rightarrow p \mid (x-1) \cdot (x+1)\),即\(x+1=p\) 或 \(x-1=p\) ,所以 \(x=p-1\) 或 \(x=(p+1) \bmod p=1\)。
测试原理:除 \(2\) 以外,所有的 \(p-1\) 都可以表示为 \(2^t \cdot u\),我们构造出一个 \(b\) 数组,令 \(b[0]=a^u\),\(b[i]=b[i-1]^2\),则 \(b[t]=a^{2^t \cdot u}\)。构造出 \(b[i]\) 后根据二次检测法进行判断,若 \(b[i]=1\) ,则 \(b[i-1]=1\) 或 \(b[i-1]=p-1\),如果不满足上述条件,\(p\) 一定是合数,如果满足了,则 \(p\) 很可能是素数。
怎么保证正确性?统计表明,在前 \(10\) 亿个自然数中共有 \(50847534\) 个素数,而满足\(2^{n-1} \equiv 1 (\bmod n)\) 的合数 \(n\) 有 \(5597\) 个,如果用费马小定理的逆命题来判断一个正整数 \(n\) 是不是素数,在前 \(10\) 亿个自然数中出错的可能性为 \(0.011\%\),概率仍很高,但是前 \(10\) 亿个自然数中同时以 \(2\) 和 \(3\) 为底的伪素数只有 \(1272\) 个,这个数目不到仅仅以 \(2\) 为底的伪素数的 \(\frac{1}{4}\)。这告诉我们如果同时验证 \(a=2\) 和 \(a=3\) 两种情况,出错的概率降到了 \(0.0025\%\),那么以此类推,测试的数越多,出错的可能性越小(不能降低到 \(0\) )。
用 \(2,3,5,7,11,13,17,19\),这几个数,在 \(OI\) 范围(\(2^{64}\),不带高精,\(\_\_\ int128\))内出错概率为 \(0\)。
U148828 素数判断(Miller-Rabin模板),是道私题,\(have \ a \ try \ ?\)
int Fast_pow(int a,int p,int mod){
int res=1;
for(;p;p>>=1,a=((a*a)%mod)){
if(p&1) res=(res*a)%mod;
}
return res%mod;
}
int Test[]={0,2,3,5,7,11,13,17};//这些就够了,想提高准确度可以再加些质数
bool Miller_Rabin(int P){
if(P==1) return false;
int t=P-1,k=0;
while(!(t&1)) k++,t>>=1;
for(int i=1;i<=4;i++){//可以更改i的范围,枚举的质数越多,出错几率越小
if(P==Test[i]) return true;
int a=Fast_pow(Test[i],t,P),nxt=a;
for(int j=0;j<=k;j++){
nxt=(a*a)%mod;
if(nxt==1&&a^1&&a^P-1) return false;
a=nxt;
}if(a^1) return false;
}
return true;
}
生日悖论
在一个 \(60\) 人的班级中,至少有两个小朋友生日相同的概率竟然高达 \(99\%\) 也就是在 \(60\) 人的群体中,很容易能找到 \(2\) 人是相同的生日.
那么在多少人的群体中,至少有两人生日相同的概率会大于 \(50\%\) 呢?答案是 \(23\) 人. 这个数字也是超乎我们的直觉的.
我们称在不少于 \(23\) 个人中至少有两人生日相同的概率大于为 \(50\%\) 生日悖论.
\(23,60\) 是如何来的?
证明:设一群人生日都不同的概率为 \(p(n)\),不妨设一年 \(365\) 天,第一个人生日可以是 \(365\) 天中任意一天,第二个人生日在 \(365\) 可以选 \(364\) 天,也就是不跟前一个人生日相同的概率是 \(\frac{364}{365}\) ,第三个人跟第二个人生日概率不相同是 \(\frac{363}{365}\),以此类推,第 \(n\) 个人跟前面 \(n-1\) 个人有相同的生日的概率是 \(\frac{365-n+1}{365}\)。根据独立事件积的概率公式,可得 \(n\) 个人生日不相同的概率是:\(p(n)=\frac{365}{365} \times\frac{364}{365} \times \frac{363}{365} …\times \frac{365-n+1}{365}\),用阶乘可以写成如下形式:\(p(n)=\frac{1}{365^n} \times \frac{365!}{(365-n)!}\),用 \(P(n)\) 表示 \(n\) 个人中至少两人生日相同的概率:\(P(n)=1-p(n)=\frac{365!}{365^n(365-n)!},n \le 365\)。当 \(n > 365\) 时概率为 \(1\) ,当 \(n=23\) 时概率大概是 \(0.507297234\),证毕。
Pollard-rho大数分解法
欢迎大家学会它并教教这个蒟蒻。
概率与数学期望
高中数学选修2-3,自行解决
矩阵快速幂
感谢 AgOH の算法胡扯
矩阵乘法:矩阵与矩阵之间或者数之间可以进行运算,矩阵与矩阵之间的运算叫做矩阵乘法。
两个矩阵的乘法当且仅当第一个矩阵的列数和另一个矩阵的行数相等时才能定义。例如:
\(\begin{vmatrix}1 & 5 & 3 \\ -2 &0& 1 \end{vmatrix}\) 和 \(\begin{vmatrix}-2 & 5 \\ 3 &5 \\-1 &1\end{vmatrix}\) 是一个正确的矩阵乘法,而 \(\begin{vmatrix} 2 & 2 \\ 3 &4 \end{vmatrix}\) 和 \(\begin{vmatrix} 3&2 \\ 3 & 4\\1&0 \end{vmatrix}\) 是个错误的矩阵乘法。
矩阵也满足一定的运算律,设三个矩阵为 \(A,B,C\)
-
结合律:\((AB)C=A(BC)\)
-
分配律:\((A+B)C=AC+BC\),\(C(A+B)=CA+CB\)
为啥分配律有两个?因为两个矩阵的乘法当且仅当第一个矩阵的列数和另一个矩阵的行数相等时才能定义,但你不知道两个矩阵的顺序,我们将矩阵乘法分为左乘和右乘。
如何运算矩阵乘法?矩阵乘法满足 \(C_{i,j}=\displaystyle \sum_{r=1}^{n}A_{i,r}B_{r,j}\),并将此乘积记为 \(C=AB\).
举个例子体会一下?
\(\begin{vmatrix}1 & 2 \\ 3 & 1 \end{vmatrix} \times \begin{vmatrix}0 & 3 \\ 2 & 1 \end{vmatrix}= \begin{vmatrix} 4 & 5 \\ 2 & 10 \end{vmatrix}\)
当然代码也很好写
for(int i=1;i<=n;i++)
for(int k=1;k<=n;k++)
for(int j=1;j<=n;j++)
C[i][j]=(C[i][j]+A[i][k]*B[k][j]);
对于这个枚举顺序,\(ikj\) 和 \(ijk\) 对结果是没有影响的,但是前者比后者更快,因为计算机在访问二维数组的时候是当成一维数组储存的,调用两个数组的时间肯定比一个的要慢,当 \(j\) 为最内层时,只调用 \(B[k][j]\) ,而如果 \(k\) 为最外层要同时调用 \(A[i][k]\) 和 \(B[k][j]\) ,效率肯定会慢,据 \(AgOH\) 大佬说会快两倍。
矩阵快速幂取模,因为矩阵乘法有结合律,所以幂次数可以拆,所以和普通快速幂一样,我们把矩阵用一个结构体封装起来重载一下运算符套上普通的快速幂就OK了。
可是矩阵快速幂能干啥呢?仅做个模板吗?\(OI\) 中用处不多,但是很重要:加速数列的递推。
拿加速斐波那契数列递推来举个例子。
\(f[i]=f[i-1]+f[i-2]\) 势必大家都很熟悉,但是这个式子是 \(\mathcal{O(n)}\) 的势必大家也都知道,但是我开大范围,问:\(Fib(x) \bmod 1e9+7 ,x \le 10^{12}\),显然线性的做法无法通过。我们需要用矩阵加速递推。
矩阵加速递推的核心是矩阵的构造,所以这里要说的如何构造一个矩阵,以斐波那契数列为例,对其推到作用的只有 \(Fib[i-1]\) 和 \(Fib[i-2]\) ,我们把 \(Fib[i-1]\) 和 \(Fib[i-2]\) 放到列向量里:$ \begin{vmatrix} Fib[i-1] \ Fib[i-2] \end{vmatrix}$,顺序无所谓,那么现在我们就有了 \(Fib[i-1],Fib[i-2]\) 以及可以推出来的 \(Fib[i]\) 三个信息了。
我们可以有这样的想法:
\(\begin{vmatrix}Fib[n-2] \\ Fib[n-1] \end{vmatrix} → \begin{vmatrix} Fib[n-1] \\ Fib[n] \end{vmatrix} → \begin{vmatrix} Fib[n] \\ Fib[n+1] \end{vmatrix} →\begin{vmatrix}Fib[n+1] \\ Fib[n+2] \end{vmatrix}\)
每个矩阵都能独立的推出后面的所有矩阵,因为矩阵里包含了所有有用的状态。所以我们来关注下第一步:\(\begin{vmatrix}Fib[n-2] \\ Fib[n-1] \end{vmatrix} → \begin{vmatrix} Fib[i-1] \\ Fib[n] \end{vmatrix}\),其中将 \(Fib[n]\) 拆成 \(Fib[i-1]+Fib[i-2]\) 的形式。
则:\(\begin{vmatrix}Fib[n-2] \\ Fib[n-1] \end{vmatrix} → \begin{vmatrix} Fib[n-1] \\ Fib[n-1]+Fib[n-2] \end{vmatrix}\),为了更方便观察,我们再拆:
\(\begin{vmatrix}Fib[n-2] \\ Fib[n-1] \end{vmatrix} → \begin{vmatrix} Fib[i-1] \times 1+Fib[n-2] \times 0 \\ Fib[n-1] \times 1 +Fib[n-2] \times 1\end{vmatrix}\).
第二个矩阵似曾相识,没错就是矩阵乘法,写出来是这个样子的:
\(\begin{vmatrix} 0&1 \\ 1&1 \end{vmatrix} \times \begin{vmatrix} Fib[n-2] \\ Fib[n-1] \end{vmatrix} = \begin{vmatrix} Fib[n-1] \times 1+Fib[n-2] \times 0 \\ Fib[n-1] \times 1 +Fib[n-2] \times 1\end{vmatrix}=\begin{vmatrix} Fib[n-1]\\Fib[n]\end{vmatrix}\)
据此原理,得到了:
$ \begin{vmatrix} Fib[n-1]\ Fib[n] \end{vmatrix} = \begin{vmatrix} 0&1 \ 1&1 \end{vmatrix}^{n-2} \times \begin{vmatrix} Fib[1]\ Fib[2]\end{vmatrix}$
套矩阵快速幂即可。
以此类推,可解决不同的加速递推。
后记
金牌就是金牌,以为我们能ak ioi