Pseudoprime numbers POJ - 3641

Fermat's theorem states that for any prime number p and for any integer a > 1, ap = a (mod p). That is, if we raise a to the pth power and divide by p, the remainder is a. Some (but not very many) non-prime values of p, known as base-a pseudoprimes, have this property for some a. (And some, known as Carmichael Numbers, are base-a pseudoprimes for all a.)

Given 2 < p ≤ 1000000000 and 1 < a < p, determine whether or not p is a base-a pseudoprime.

Input

Input contains several test cases followed by a line containing "0 0". Each test case consists of a line containing p and a.

Output

For each test case, output "yes" if p is a base-a pseudoprime; otherwise output "no".

Sample Input
3 2
10 3
341 2
341 3
1105 2
1105 3
0 0
Sample Output
no
no
yes
no
yes
yes
 

题意:给你两个数p和a,如果a的p次方对p取余等于a并且p不是素数,输出yes,否则输出no。

思路:因为数据过大,直接写for循环求次方会超时.这里就要用到快速幂这个技巧了.简单说就是降幂,比如说a的b次方,如果b是偶数,该数就可以表示为(a^2)^(b/2),如果b是奇数,可以 另写一个数保存此时的底数然后幂数减1,又可以继续降幂了,知道最后。优化代码。注意__int64输入防止爆int

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

bool is_prime(long long n)
{
    for(long long i=2;i<=sqrt(n);i++)
    {
        if(n%i==0)
        {
            return 0;
        }
    }
    return 1;
}

long long mod_exp(long long p,long long a)
{
    long long ans=1;
    long long b=p;//b为变化的指数
    while(b)
    {
        if(b%2==1)
        {
            ans=(ans*a)%p;
        }
        a=(a*a)%p;//此处为ans才是快速幂(更新底数才对
        b=b/2;
    }
    return ans%p;
}

int main()
{
    long long a,p;
    while(~scanf("%I64d %I64d",&p,&a))
    {
        if(p==0&&a==0)
            break;
        if(is_prime(p)==0&&mod_exp(p,a)==a)//不是质数
           printf("yes\n");
        else
            printf("no\n");
    }
    return 0;
}

附上经典解析

2.6 数学问题的解题窍门

快速幂运算

用反复平方法做快速幂运算判断条件②,条件①嘛,朴素的素性测试就行了。

有读者问我为什么特意写个函数LL mod_mult(LL a, LL b, LL m)来求(a * b) % m,因为这样不容易溢出。如果直接用运算符先*后%的话,哪怕是unsigned long long也可能溢出。

这个求余乘法的思想是,先将一个数用2进制表示:

bn表示b的二进制的第n个bit,当然,首个比特是从0开始算的。将a乘入括号中,得到:

由于bn要么是0要么是1,所以只需计算为1的部分就可以了,比如3*5:

每加一次就求一次余,这样每次加上去的都是小于m的余数,这样就不怕溢出了。由于每个bit都需要计算一次,所以复杂度是O(log(N))。


    
    
  1. #ifndef ONLINE_JUDGE
  2. #pragma warning(disable : 4996)
  3. #endif
  4. #include <iostream>
  5. using namespace std;
  6.  
  7. typedef long long LL;
  8.  
  9. // return (a * b) % m
  10. LL mod_mult(LL a, LL b, LL m)
  11. {
  12. LL res = 0;
  13. LL exp = a % m;
  14. while (b)
  15. {
  16. if (& 1)
  17. {
  18. res += exp;
  19. if (res > m) res -= m;
  20. }
  21. exp <<= 1;
  22. if (exp > m) exp -= m;
  23. >>= 1;
  24. }
  25. return res;
  26. }
  27.  
  28. // return (a ^ b) % m
  29. LL mod_exp(LL a, LL b, LL m) 
  30. {
  31. LL res = 1;
  32. LL exp = a % m;
  33. while (b)
  34. {
  35. if (& 1) res = mod_mult(res, exp, m);
  36. exp = mod_mult(exp, exp, m);
  37. >>= 1;
  38. }
  39. return res;
  40. }
  41.  
  42. //************************************
  43. // Method:    is_prime
  44. // FullName:  is_prime
  45. // Access:    public 
  46. // Returns:   bool
  47. // Qualifier: 素性测试
  48. // Parameter: const int & n
  49. //************************************
  50. bool is_prime(const int& n)
  51. {
  52. for (int i = 2; i * i <= n; ++i)
  53. {
  54. if (% i == 0)
  55. {
  56. return false;
  57. }
  58. }
  59.  
  60. return n != 1; // 1是例外
  61. }
  62.  
  63. ///SubMain//
  64. int main(int argc, char *argv[])
  65. {
  66. #ifndef ONLINE_JUDGE
  67. freopen("in.txt", "r", stdin);
  68. freopen("out.txt", "w", stdout);
  69. #endif
  70. int p, a;
  71. while (cin >> p >> a && p && a)
  72. {
  73. if (!is_prime(p) && (mod_exp(a, p, p) == a))
  74. {
  75. cout << "yes" << endl;
  76. }
  77. else
  78. {
  79. cout << "no" << endl;
  80. }
  81. }
  82. #ifndef ONLINE_JUDGE
  83. fclose(stdin);
  84. fclose(stdout);
  85. system("out.txt");
  86. #endif
  87. return 0;
  88. }
  89. ///End Sub//



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值