素数-简单题

1.素数筛选法

模板:

View Code
/*
*@brief 初始化素数数组
*/
void init_prime()
{
memset(is_prime, true, sizeof(is_prime));
//筛掉偶数
for(int k = 4; k < maxn; k +=2)
{
is_prime[k] = false;
}
int sq = (int)sqrt((double)maxn);
//筛掉素数的倍数
for(int i = 3; i <= sq; ++i)
{
if(is_prime[i])
{
for(int j = i*i; j < maxn; j += 2*i)
{
is_prime[j] = false;
}
}
}
}

2.区间筛选法

利用小区间素数筛选结果,筛掉大区间内的素数

方法:见poj2689
下面是几个相关的简单素数题目。

poj1365

/*
 *@brief题目大意:已知任意一个大于1的数可以表示成一些素数的乘积,即x=p1^e1*p2^e2,pn^en (pi 为素数,ei 为对应素数的个数)
 *现给你x的表示,要你求x-1的质因数表示
 *例如:例:输入:5 1 2 1 则x=5^1*2^1=10,所以x-1=9,9可以表示成:9=3^2 输出:3 2
 */

View Code
*
*@brief 因式分解
*@param 要分解的整数
*@return 返回因式分解的结果数组的长度
*/
__int64 _get_factorization(__int64 param)
{
if( (0 == param) || (1 == param))
return -1;
__int64 num = param;
__int64 _base = 2;
__int64 _base_cnt = 0;
__int64 _ret_cnt = 0;
while(1 != num)
{
if(0 == num%_base)
{
_base_cnt = 0;
while(0 == num%_base)
{
++_base_cnt;
num /= _base;
}
_ret_p[_ret_cnt] = _base;
_ret_e[_ret_cnt] = _base_cnt;
++_ret_cnt;
}
++_base;
}
return _ret_cnt;
}

poj1142
/*
 *题目大意:如果一个数各个位上的加和=所有质因数的各个位上的加和,这个数为smith数,任意给出一个数x,求大于x的最小smith数
 *题目的关键是质因数分解,采用最原始的方法超时,看到一个新的质因数分解的方法:
 求解一个数x的质因数分解,最开始使素数k从2开始,如果能被k整除,那么j/=k,k不变,如果不能被k整除,k++;
 对于当前的k,不用判断是否为素数,因为如果k是合数,肯定不能被j整除,理由如下:设k=p*i,那么j肯定能被p整除,
 由于我们的k是从小到大遍历的,在这之前肯定计算过j/p了,矛盾,所以k不可能为合数。
 */

View Code
原始方法:
_base = 2;
while(1 != num)
{
if(0 == num%_base)
{
_base_cnt = 0;
while(0 == num%_base)
{
++_base_cnt;
num /= _base;
}
_ret_p[_ret_cnt] = _base;
_ret_e[_ret_cnt] = _base_cnt;
++_ret_cnt;
}
++_base;
}
超时原因是我们需要遍历到num为1的情况,如果此时num已经是一个非常大的素数,我们需要不断循环
使base增加到num为止,这时我们完全可以终止循环。
//判断一个数是否是smith数
bool is_smith_num(int param)
{
int j = param;
int k = 2;
int cnt = 0;
int sq = (int)sqrt((float)j);
int cur_sum = 0;
int factors_sum = 0;
cur_sum = get_digits_sum(j);
//求j的质因数分解
for(k = 2; k <= sq; ++k)
{
if(j%k)
continue;
int t = get_digits_sum(k);
while(0 == j%k)
{
factors_sum += t;
j /= k;
}
if(1 == j)
return cur_sum == factors_sum;
if(factors_sum > cur_sum)
return 0;
sq = (int)sqrt((float)j);
}
if(factors_sum && (factors_sum + get_digits_sum(j) == cur_sum))
return 1;
return 0;
}

poj2034
这个题磨叽了半天,用递归一步出来了,开始只想着用全排列了,真的是长时间不做题,什么都生疏了,效率好低。
/*
 *@brief 题目大意:给出一个数字范围 n--m,及d,让找出一个满足连续d个数的和都是和数的排列,如果存在多个,输出字典顺序最小的。
 *这个题,首先打表,然后递归找出第一个满足要求的序列即可。path记录递归路径,used是否访问标记,is_prime素数表,下标就表示
 *对应的数,如果为素数,is_prime为true,否则为false。
 */

View Code
#include <iostream>
#include <cmath>
using namespace std;
const int maxn = 10000;
const int max_size = 1001;
int path[max_size];
bool is_prime[maxn];
bool used[max_size];
int n = 0;
int m = 0;
int d = 0;
int len = 0;
/*
*@brief 初始化素数数组
*/
void init_prime()
{
memset(is_prime, true, sizeof(is_prime));
//筛掉偶数
for(int k = 4; k < maxn; k +=2)
{
is_prime[k] = false;
}
int sq = (int)sqrt((double)maxn);
//筛掉素数的倍数
for(int i = 3; i <= sq; ++i)
{
if(is_prime[i])
{
for(int j = i*i; j < maxn; j += 2*i)
{
is_prime[j] = false;
}
}
}
}
/*
*@brief 判断数组下标为x的数前d个数是否满足连续和都是和数
*需要注意的是长度为2 - d的连续子序列都要检查,
*并不是长度正好=d的连续子序列
*/
bool _is_composite_num(int cnt)
{
int sum = 0;
int sub_d = 0;
int j = 0;
for(j = cnt - 1; j >= 0; --j)
{
sum += path[j];
++sub_d;
if(1 == sub_d)
continue;
if(is_prime[sum])
return false;
if(d <= sub_d)
return true;
}
return true;
}
/*
*@brief 递归查找
*/
bool _fun(int cnt)
{
//提前判断是否已经不满足要求
if(2 <= cnt)
{
if(!_is_composite_num(cnt))
return false;
}
if(cnt == len)
{
return true;
}
for(int i = n; i <= m; ++i)
{
if(!used[i])
{
path[cnt++] = i;
used[i] = true;
if(_fun(cnt))
return true;
cnt--;
used[i] = false;
}
}20:40 2012-3-4
return false;
}
int main()
{
init_prime();
while(cin>>n>>m>>d)
{
if(0 == (n + m + d))
{
break;
}
len = m - n + 1;
memset(used, 0, sizeof(used));
memset(path, 0, sizeof(path));
if(!_fun(0))
{
cout<<"No anti-prime sequence exists."<<endl;
}
else
{
int i = 0;
for(i = 0; i < len - 1; ++i)
{
cout<<path[i]<<',';
}
cout<<path[i]<<endl;
}
}
system("pause");
return 0;
}

poj2739 水题,打表

poj2689 (推荐,区间筛选)
题目大意:给出一个区间[L,U],让你求出这个区间内最小距离的两个素数,及最大距离的两个素数。
例如给出[2,17],这个区间内的素数为:2,3,5,7,11,13,17 那么2,3就是最小距离(1),7,11就是最大距离(4)
由于这个题目中L,U都比较大,我们不可能说利用筛选法求出所有的素数,但是题目中有个条件是L和U的距离不超过1000000,
我们可以将[L,U]内的素数过滤掉,由于U<= 2147483647,质因数最大为sqrt(2147483647)=46341,我们可以对区间[1,46341]用
原始方法筛选出素数,然后对区间[L,U]进行筛选,rst数组中存放1-46341内的素数,利用这个数组将[L,U]内的合数筛掉,并将该区间的每个数映射到小区间[0,L-U]内。

View Code
/*
*@brief 筛出1-46341内的素数
*/
void _init_prime()
{
int i = 0;
int j = 0;
int sq = (int)sqrt((float)maxn);
memset(prime, true, sizeof(prime));
prime[0] = false;
prime[1] = false;
for(i = 4; i < maxn; i +=2)
prime[i] = false;
for(i = 3 ; i <= sq; ++i)
{
if(prime[i])
{
for(j = i*i; j < maxn; j += (i<<1))
{
prime[j] = false;
}
}
}
p_cnt = 0;
for(i = 2; i < maxn; ++i)
{
if(prime[i])
{
rst[p_cnt++] = i;
}
}
}
/*
*@brief 筛选出[L,U]内的素数
*/
void init_big_prime(int L, int U)
{
ans_cnt = 0;
if(U < maxn)
{
// 直接将rst内素数cp到ans内
//i需要定义为__int64,因为U为2147483647时,i进行++操作会超出int范围
__int64 i = 0;
for(i = L; i <= U; ++i)
{
if(prime[i])
{
ans[ans_cnt++] = i;
}
}
}
else
{
//将区间[L,U]内合数筛掉,并且将区间平移到[0,U-L]
__int64 s = 0;
__int64 e = U - L;
__int64 i = 0;
__int64 j = 0;
//将L - U内合数过滤掉
memset(b_prime, true, sizeof(b_prime));
for(i = (1 == L%2); i <= e; i += 2)
{
b_prime[i] = false;
}
//将所有是rst倍数的合数去掉
for(i = 1; i < p_cnt; ++i)
{
//找出[L,U]内被rst[i]整除的第一个数s
s = L/rst[i]*rst[i];
if(s < L)
s += rst[i];
s -= L;
for(j = s; j <= e; j += rst[i])
{
b_prime[j] = false;
}
}
for(i = L; i <= U; ++i)
{
if(b_prime[i - L])
{
ans[ans_cnt++] = i;
}
}
}
}






你可能感兴趣的:(素数)