update
This commit is contained in:
parent
c2c24ea8b8
commit
adf9c01090
1
day7/inverse/1.in
Normal file
1
day7/inverse/1.in
Normal file
@ -0,0 +1 @@
|
||||
10 13
|
10
day7/inverse/1.out
Normal file
10
day7/inverse/1.out
Normal file
@ -0,0 +1,10 @@
|
||||
1
|
||||
7
|
||||
9
|
||||
10
|
||||
8
|
||||
11
|
||||
2
|
||||
5
|
||||
3
|
||||
4
|
1
day7/inverse/2.in
Normal file
1
day7/inverse/2.in
Normal file
@ -0,0 +1 @@
|
||||
1500000 5285237
|
1500000
day7/inverse/2.out
Normal file
1500000
day7/inverse/2.out
Normal file
File diff suppressed because it is too large
Load Diff
55
day7/inverse/inverse.cpp
Normal file
55
day7/inverse/inverse.cpp
Normal 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
|
||||
}
|
||||
}
|
@ -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.
@ -38,3 +38,12 @@ target("fperfect")
|
||||
target("pre88")
|
||||
set_kind("binary")
|
||||
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
|
Loading…
Reference in New Issue
Block a user