zoukankan      html  css  js  c++  java
  • 素数简单题

    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;
    }
    }
    }
    }






  • 相关阅读:
    向量空间模型 词袋模型
    python 小点
    python list的append()和extend()方法
    numpy 数组运算
    内置函数和numpy中的min(),max()函数
    python index()函数
    Python支持的正则表达式元字符和语法
    Python多线程
    查看linux机器配置&内核版本
    Shell获取文件的文件名和扩展名的例子
  • 原文地址:https://www.cnblogs.com/buptLizer/p/2389945.html
Copyright © 2011-2022 走看看