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 $ 的值。
|
- 定义 $ a, b, p $ 的值。
|
||||||
- 调用 `modDivide` 计算结果并输出。
|
- 调用 `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.
11
xmake.lua
11
xmake.lua
@ -37,4 +37,13 @@ target("fperfect")
|
|||||||
|
|
||||||
target("pre88")
|
target("pre88")
|
||||||
set_kind("binary")
|
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
|
Loading…
Reference in New Issue
Block a user