一、实验内容
若p是奇素数,编程实现:
1)计算整数a模p的指数;
2)计算模p的原根整数g;
要求:分步输出,有中间结果。
二、实验目标
1.在屏幕上输入要输入的整数a和模数p(奇素数)。
2.在屏幕上输出整数a模p的指数。
3.在在屏幕上输出奇素数的原根p,要求按上课讲义的三个步骤分步输出,并计算不同算法的代价。
三、实验原理
1.指数和原根的概念:
2.计算整数a模p的指数:
由于ordm(a) | φ(m),则我们只需要利用一个循环将φ(m)的因数ri(从小到大)代入ari ≡ 1(mod m)中,验证是否成立,一旦成立则找到指数。
3.计算模p的原根整数g:
a)方法一:
题中p为奇素数,则对于整数a = 2、3 …、 p-1 均与p互素,由于第2点我们可以找到整数a的指数ordp(a),且 φ(p)=p-1,故利用一个循环将整数a代入第2点的函数求得指数,验证ordp(a)是否和φ(p)相等即能判断整数a是否为原根。
b)方法二:
按照《信息安全数学基础(第二版)》第179页内容,即上课所讲对于模数p为奇素数的前提下,按照三个步骤验证整数a是否为原根:
- 找出一个原根g:
对于素奇数p,分解p-1得到p-1的素因数分解式,素因数为qi,计算(p-1)/qi,验证对于g=2,3,5,6等,g(p-1)/qi≡1(mod p)是否成立,若均不成立,则g为模p的原根。
- 找出p-1的简化剩余系:
若(d,p-1)=1时,d遍历p-1的简化剩余系。
- 找出模p的所有原根:
原根共φ(p-1)个,且gd遍历模p的所有原根。
四、实验设计
1.验证模数p是否为奇素数:
验证一个数是否为奇素数,则先将其模2得到余数判断是否为奇数,其次再利用一个循环判断p与小于√p的数是否互素,若两个条件均满足则该数为奇素数。
// 判断一个整数a是否为素数
int pd(int a) {
int flag = 1;
int k = (int)sqrt((double)a);
for (int i = 2; i <= k; i++) {
if (a % i == 0) {
flag = 0;
break;
}
}
return flag;
}
2.计算a^b(mod p)
由于需要经常计算a^b(mod p),故单独放为一个函数
// 计算a^b(mod p)
int fmode(int a, int b) {
long long int n = 1;
while (b) {
n = (n * (a % p)) % p;
b--;
}
n = (int)n;
return n;
}
3.利用广义欧几里得除法计算x和y的最大公因数
// 计算x和y的最大公因数
int gcd(int x, int y) {
int t;
// 广义欧几里得除法
while (y) {
t = x;
x = y;
y = t % y;
}
return x;
}
4.计算整数a模p的指数:
利用一个循环将φ(m)的因数ri(从小到大)代入ari 中,验证ari模m是否为1,一旦为1则跳出循环找到指数。
// 计算整数a模p的指数
int ord(int a) {
int e = p - 1; // 记录指数
for (int i = 1; i < p - 1; i++) {
if ((p - 1) % i == 0) { // i是 p-1的因数
int n = fmode(a, i);
if (n == 1 && i < e) {
e = i;
break;
}
}
}
return e;
}
5.计算模p的原根整数g:
a)方法一:直接代入第2步即ord(a)函数,验证ordp(a)是否等于φ(p)即是否与p-1相等:
// 计算模p的原根整数g
void g() {
int cmp = 0;
for (int i = 2; i < p; i++) {
if (ord(i) == p - 1) {
cout << i << "\t";
if (cmp++ == 9) {
cout << endl;
cmp = 0;
}
}
}
}
b)方法二:按照三个步骤验证整数a是否为原根:
// 计算模p的原根整数g
void g() {
vector<int> factor; // p-1的素因子
vector<int> residue; // 简化剩余系
vector<int> root; // 所有原根
// 步骤1.找出一个原根g
for (int i = 2; i < p - 1; i++) {
if (((p - 1) % i == 0) && pd(i) == 1) {//i为p - 1素因子
cout << "p-1素因数q为" << i << ",对应(p-1)/q为:" << (p - 1) / i << endl;
factor.push_back((p - 1) / i); //计算(p-1)/qi
}
}
int yroot = 2; // 最小的原根
for (int i = 2; i < p; i++) {
int flag = 1;
for (int j = 0; j < factor.size(); j++) {
if (fmode(i, factor[j]) == 1) {
flag = 0;
break;
}
}
if (flag) { // 找到最小的原根
yroot = i;
for (int j = 0; j < factor.size(); j++) {
cout << yroot << "^" << factor[j] << "!≡1(mod p)" << endl;
}
cout << "故 g=" << yroot << "为模p的原根" << endl;
break;
}
}
cout << endl;
// 步骤2.找出p-1的简化剩余系
cout << "p-1的简化剩余系为:" << endl;
for (int i = 1; i < p - 1; i++) {
if (gcd(i, p - 1) == 1) {
residue.push_back(i);
cout << i << " ";
}
}
cout << endl;
// 步骤3.找出模p的所有原根
for (int i = 0; i < residue.size(); i++) {
root.push_back(fmode(yroot, residue[i]));
}
sort(root.begin(), root.end());
cout << "\n奇素数" << p << "的所有原根为:\n";
int cmp = 0;
for (int i = 0; i < root.size(); i++)
{
cout << root[i] << "\t";
if (cmp++ == 9) {
cout << endl;
cmp = 0;
}
}
}
6.主函数设计:
int main()
{
cout << "请输入一个奇素数 p:\n";
cin >> p;
if (pd(p) == 0 || p % 2 == 0) cout << "p不是一个奇素数!\n";
else {
int a;
cout << "请输入一个整数a:\n";
cin >> a;
cout << "整数" << a << "模" << p << "的指数为:\n" << ord(a) << endl;
g();
}
return 0;
}
五、实验源码
【方法一】
#include<iostream>
#include<math.h>
#include<vector>
#include<algorithm>
using namespace std;
int p;// 奇素数
// 判断一个整数a是否为素数
int pd(int a) {
int flag = 1;
int k = (int)sqrt((double)a);
for (int i = 2; i <= k; i++) {
if (a % i == 0) {
flag = 0;
break;
}
}
return flag;
}
// 计算a^b(mod p)
int fmode(int a, int b) {
long long int n = 1;
while (b) {
n = (n * (a % p)) % p;
b--;
}
n = (int)n;
return n;
}
// 计算整数a模p的指数
int ord(int a) {
int e = p - 1; // 记录指数
for (int i = 1; i < p - 1; i++) {
if ((p - 1) % i == 0) { // i是 p-1的因数
int n = fmode(a, i);
if (n == 1 && i < e) {
e = i;
break;
}
}
}
return e;
}
// 计算模p的原根整数g
void g() {
int cmp = 0;
for (int i = 2; i < p; i++) {
if (ord(i) == p - 1) {
cout << i << "\t";
if (cmp++ == 9) {
cout << endl;
cmp = 0;
}
}
}
}
int main()
{
cout << "请输入一个奇素数 p:\n";
cin >> p;
if (pd(p) == 0 || p % 2 == 0) cout << "p不是一个奇素数!\n";
else {
int a;
cout << "请输入一个整数a:\n";
cin >> a;
cout << "整数" << a << "模" << p << "的指数为:\n" << ord(a) << endl;
cout << "奇素数" << p << "的所有原根为:\n";
g();
}
return 0;
}
【方法二】
#include<iostream>
#include<math.h>
#include<vector>
#include<algorithm>
using namespace std;
int p;// 奇素数
// 判断一个整数a是否为素数
int pd(int a) {
int flag = 1;
int k = (int)sqrt((double)a);
for (int i = 2; i <= k; i++) {
if (a % i == 0) {
flag = 0;
break;
}
}
return flag;
}
// 计算a^b(mod p)
int fmode(int a, int b) {
long long int n = 1;
while (b) {
n = (n * (a % p)) % p;
b--;
}
n = (int)n;
return n;
}
// 计算x和y的最大公因数
int gcd(int x, int y) {
int t;
// 广义欧几里得除法
while (y) {
t = x;
x = y;
y = t % y;
}
return x;
}
// 计算整数a模p的指数
int ord(int a) {
int e = p - 1; // 记录指数
for (int i = 1; i < p - 1; i++) {
if ((p - 1) % i == 0) { // i是 p-1的因数
int n = fmode(a, i);
if (n == 1 && i < e) {
e = i;
break;
}
}
}
return e;
}
// 计算模p的原根整数g
void g() {
vector<int> factor; // p-1的素因子
vector<int> residue; // 简化剩余系
vector<int> root; // 所有原根
// 步骤1.找出一个原根g
for (int i = 2; i < p - 1; i++) {
if (((p - 1) % i == 0) && pd(i) == 1) {//i为p - 1素因子
cout << "p-1素因数q为" << i << ",对应(p-1)/q为:" << (p - 1) / i << endl;
factor.push_back((p - 1) / i); //计算(p-1)/qi
}
}
int yroot = 2; // 最小的原根
for (int i = 2; i < p; i++) {
int flag = 1;
for (int j = 0; j < factor.size(); j++) {
if (fmode(i, factor[j]) == 1) {
flag = 0;
break;
}
}
if (flag) { // 找到最小的原根
yroot = i;
for (int j = 0; j < factor.size(); j++) {
cout << yroot << "^" << factor[j] << "!≡1(mod p)" << endl;
}
cout << "故 g=" << yroot << "为模p的原根" << endl;
break;
}
}
cout << endl;
// 步骤2.找出p-1的简化剩余系
cout << "p-1的简化剩余系为:" << endl;
for (int i = 1; i < p - 1; i++) {
if (gcd(i, p - 1) == 1) {
residue.push_back(i);
cout << i << " ";
}
}
cout << endl;
// 步骤3.找出模p的所有原根
for (int i = 0; i < residue.size(); i++) {
root.push_back(fmode(yroot, residue[i]));
}
sort(root.begin(), root.end());
cout << "\n奇素数" << p << "的所有原根为:\n";
int cmp = 0;
for (int i = 0; i < root.size(); i++)
{
cout << root[i] << "\t";
if (cmp++ == 9) {
cout << endl;
cmp = 0;
}
}
}
int main()
{
cout << "请输入一个奇素数 p:\n";
cin >> p;
if (pd(p) == 0 || p % 2 == 0) cout << "p不是一个奇素数!\n";
else {
int a;
cout << "请输入一个整数a:\n";
cin >> a;
cout << "整数" << a << "模" << p << "的指数为:\n" << ord(a) << endl;
g();
}
return 0;
}
六
【测试样例一-计算原根采用方法一】
【测试样例二-计算原根采用方法二】
【测试样例三-输入不符合要求】
七
1.算法效率分析比较:
① 对于实现计算模p的原根整数g的两个方式一、二:
方式一:
只利用了一个循环,从i=2开始判断,代入计算整数a模p的指数的函数ord(a)中,时间复杂度为O(p)。但由于ord(a)函数时间复杂度也为O(p),则综合下来时间复杂度为O(p2)。
方式二:
分为三个步骤走,每一步都涉及了循环,故时间复杂度为O(cp),c为循环次数。
方式一代码编写简洁,得出数据直接。方式二实现了分步输出的功能,也更好地理解到求原根在做题时的运用。
② 对于实现计算模p的原根整数g中方式二的三个步骤:
若奇素数p特别大,会导致三个步骤中每个循环都需要消耗过长的时间,特别是最后sort()函数排序后输出原根结果,需要先将乱序的原根序列放至vector数组再进行排序,需要消耗过长时间。
2.心得体会
通过本次实验,对指数、原根有了更深刻的理解,完全掌握了求一个奇素数p的原根的方式。同时,提升了编程能力,对数学原理运用到编程上有了更好的理解与实践。