This commit is contained in:
Zengtudor 2024-08-09 10:03:38 +08:00
parent c2c24ea8b8
commit adf9c01090
8 changed files with 1500230 additions and 2 deletions

1
day7/inverse/1.in Normal file
View File

@ -0,0 +1 @@
10 13

10
day7/inverse/1.out Normal file
View File

@ -0,0 +1,10 @@
1
7
9
10
8
11
2
5
3
4

1
day7/inverse/2.in Normal file
View File

@ -0,0 +1 @@
1500000 5285237

1500000
day7/inverse/2.out Normal file

File diff suppressed because it is too large Load Diff

55
day7/inverse/inverse.cpp Normal file
View File

@ -0,0 +1,55 @@
#include<bits/stdc++.h>
using namespace std;
#define int long long
int binExp(int b,int e,int m){
int r=1;
while(e>0){
if(e%2==1){
r=(r*b)%m;
}
b=(b*b)%m;
e=e>>1;
}
return r;
}
int inverse(int a,int p){
return binExp(a, p-2, p);
}
#ifdef OITEST
#endif
#ifndef OITEST
#endif
signed main(signed argc ,char* argv[]){
cin.sync_with_stdio(false);
cin.tie(0);
int n,p;
#ifdef OITEST
assert(argc==2);
string snum(argv[1]);
string ifilen=snum+".in";
string ofilen=snum+".out";
cout<<ifilen<<endl;
ifstream ifile(ifilen.c_str());
ifstream ofile(ofilen.c_str());
stringstream ss;
#define cin ifile
#define cout ss
#endif
cin>>n>>p;
for(int i=1;i<=n;i++){
cout<<inverse(i,p)<<endl;
#ifdef OITEST
int o,a;
ss>>o;
ofile>>a;
assert(o==a);
#endif
}
}

View File

@ -257,4 +257,156 @@ int main() {
- 定义 $ a, b, p $ 的值。
- 调用 `modDivide` 计算结果并输出。
通过这种方式,你可以在 C++ 中使用费马小定理和快速幂算法实现分数的模运算。如果有其他问题或需要进一步的解释,随时告诉我!
使用扩展欧几里得算法Extended Euclidean Algorithm, Exgcd来求解模逆元也是一种经典且高效的方法特别是在处理大数时更为优越。接下来我将详细解释如何使用扩展欧几里得算法来实现分数 \(\frac{a}{b} \mod p\) 的计算,并与快速幂进行比较。
### 扩展欧几里得算法的原理
扩展欧几里得算法不仅可以用于求两个数的最大公约数GCD还能求出模逆元。对于两个整数 \(a\) 和 \(b\),扩展欧几里得算法可以找到一组整数 \(x\) 和 \(y\),使得:
\[
ax + by = \gcd(a, b)
\]
当 \(a\) 和 \(b\) 互质时,\(\gcd(a, b) = 1\),因此方程变为:
\[
ax + by = 1
\]
在这种情况下,\(x\) 就是 \(a\) 模 \(b\) 的逆元,即:
\[
a^{-1} \equiv x \pmod{b}
\]
### C++ 实现
下面是使用扩展欧几里得算法计算分数 \(\frac{a}{b} \mod p\) 的 C++ 实现代码:
```cpp
#include <iostream>
using namespace std;
// 扩展欧几里得算法
long long extendedGCD(long long a, long long b, long long &x, long long &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
long long x1, y1;
long long gcd = extendedGCD(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return gcd;
}
// 求 b 的逆元,前提是 b 和 p 互质
long long modInverse(long long b, long long p) {
long long x, y;
long long g = extendedGCD(b, p, x, y);
if (g != 1) {
throw invalid_argument("Inverse doesn't exist.");
} else {
// 确保结果是正数
return (x % p + p) % p;
}
}
// 计算 (a / b) % p
long long modDivide(long long a, long long b, long long p) {
long long inv = modInverse(b, p);
return (a * inv) % p;
}
int main() {
long long a = 10, b = 3, p = 7;
try {
long long result = modDivide(a, b, p);
cout << "Result of " << a << "/" << b << " mod " << p << " is: " << result << endl;
} catch (const invalid_argument &e) {
cout << e.what() << endl;
}
return 0;
}
```
### 逐行解释
1. **扩展欧几里得算法**
```cpp
long long extendedGCD(long long a, long long b, long long &x, long long &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
long long x1, y1;
long long gcd = extendedGCD(b, a % b, x1, y1);
x = y1;
y = x1 - (a / b) * y1;
return gcd;
}
```
- 当 \(b = 0\) 时,直接返回 \(a\) 作为 gcd设 \(x = 1\) 和 \(y = 0\)。
- 递归调用 `extendedGCD(b, a % b, x1, y1)`,计算 gcd 及其对应的系数。
- 通过回溯,计算当前的 \(x\) 和 \(y\)。
2. **求模逆元**
```cpp
long long modInverse(long long b, long long p) {
long long x, y;
long long g = extendedGCD(b, p, x, y);
if (g != 1) {
throw invalid_argument("Inverse doesn't exist.");
} else {
return (x % p + p) % p;
}
}
```
- 使用扩展欧几里得算法计算 \(b\) 的模逆元 \(x\)。
- 如果 gcd 不为 1则 \(b\) 和 \(p\) 不互质,逆元不存在。
- 否则,返回 \(x\) 的正值作为逆元。
3. **计算 \(\frac{a}{b} \mod p\)**
```cpp
long long modDivide(long long a, long long b, long long p) {
long long inv = modInverse(b, p);
return (a * inv) % p;
}
```
- 调用 `modInverse` 求出 \(b\) 的逆元 \(inv\)。
- 返回 \( (a \times inv) \mod p \)。
4. **主函数**
```cpp
int main() {
long long a = 10, b = 3, p = 7;
try {
long long result = modDivide(a, b, p);
cout << "Result of " << a << "/" << b << " mod " << p << " is: " << result << endl;
} catch (const invalid_argument &e) {
cout << e.what() << endl;
}
return 0;
}
```
- 定义 \(a, b, p\) 的值,计算并输出结果。
### 为什么使用 Exgcd
相比于快速幂算法Exgcd 在某些场景下更加灵活和高效:
1. **处理更大范围的数**Exgcd 可以直接处理整数(包括负数),而快速幂需要在计算中额外处理这些情况。
2. **无需质数模**:快速幂算法基于费马小定理,只能在模数为质数的情况下使用。而 Exgcd 不要求模数为质数。
3. **更快的计算速度**对于某些特定的输入Exgcd 在常数时间上可能更快。
通过这种方式,你可以在 C++ 中使用扩展欧几里得算法来实现高效的分数模运算。如果你还有其他问题或需要进一步的解释,随时告诉我!

Binary file not shown.

View File

@ -37,4 +37,13 @@ target("fperfect")
target("pre88")
set_kind("binary")
add_files("./day6/pre88/*.cpp")
add_files("./day6/pre88/*.cpp")
target("inverse")
set_kind("binary")
add_files("./day7/inverse/*.cpp")
set_rundir("./day7/inverse")
for v=1,2 do
local strname = tostring(v)
add_tests(strname,{files="./day7/inverse/*.cpp",defines="OITEST",runargs=strname,run_timeout=1000})
end