实验四 求指数和原根

一、实验内容

若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的原根的方式。同时,提升了编程能力,对数学原理运用到编程上有了更好的理解与实践。

  • 0
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是蒸饺吖~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值