例41 快速冪運算
題目描述
輸入三個整數 b,p,k(0≤b,p,k<231),求 b^p mod k
輸入格式
一行三個整數 b,p,k
輸出格式
輸出 b^p mod k=s (s 為運算結果)
輸入樣例
2 10 9
輸出樣例
2^10 mod 9=7
(1)編程思路。
在實際應用中,我們經常會用到冪運算,例如,an為a的n次冪。求a的n次方通常采用快速冪運算。下面我們來探討快速冪運算的思路。
由於乘法具有結合律,因此 a4 = a*a * a *a = (a*a) * (a*a) = a2 * a2。由此可以得到這樣的結論:當n為偶數時,an = a n/2 * a n/2;當n為奇數時,an = a n/2 * a n/2 * a (其中n/2取整)。這樣,我們可以采用一種類似於二分的思想快速求得a的n次冪。
例如,a9 =a*a*a*a*a*a*a*a*a (一個一個乘,要乘9次)
=a*(a*a)*(a*a)*(a*a)*(a*a)
=a*(a2)4
= a*((a2)2)2 (A平方后,再平方,再平方,再乘上剩下的一個a,只乘4次)
(2)源程序。
#include <stdio.h>
typedef long long LL;
LL quickPower(LL a, LL b,LL k)
{
LL p = 1;
while (b)
{
if (b&1) p = (p*a)% k;
b >>= 1;
a = (a%k*a%k)%k;
}
return p%k;
}
int main()
{
LL a,b,k;
scanf("%lld%lld%lld",&a,&b,&k);
printf("%lld^%lld mod %lld=%lld\n",a,b,k,quickPower(a,b,k));
return 0;
}
習題41
41-1 組合數
本題選自洛谷題庫 (https://www.luogu.org/problem/P3414)
題目描述
小明剛學了組合數。現在他很想知道sigma(C(n,i))是多少;其中C是組合數(即C(n,i)表示n個物品無順序選取i個的方案數),i取從0到n所有偶數。
由於答案可能很大,請輸出答案對6662333的余數。
輸入格式
輸入僅包含一個整數n。
輸出格式
輸出一個整數,即為答案。
輸入樣例
3
輸出樣例
4
(1)編程思路。
由二項式定理可知,
因此本題實質是求輸入整數n的2的(n-1)次冪對6662333的余數。
由於n較大,采用快速冪運算完成。
(2)源程序。
#include <stdio.h>
#define MODNUM 6662333
typedef long long LL;
LL quickPower(LL a, LL b)
{
LL p = 1;
while (b)
{
if (b&1) p = p*a%MODNUM;
b >>= 1;
a = a*a%MODNUM;
}
return p;
}
int main()
{
LL n;
scanf("%lld", &n);
printf("%lld\n", quickPower(2, n-1));
return 0;
}
41-2 漢諾塔V
本題選自杭州電子科技大學OJ題庫 (http://acm.hdu.edu.cn/showproblem.php?pid=1995)
Problem Description
用1,2,...,n表示n個盤子,稱為1號盤,2號盤,...。號數大盤子就大。經典的漢諾塔問題經常作為一個遞歸的經典例題存在。可能有人並不知道漢諾塔問題的典故。漢諾塔來源於印度傳說的一個故事,上帝創造世界時作了三根金剛石柱子,在一根柱子上從下往上按大小順序摞着64片黃金圓盤。上帝命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。並且規定,在小圓盤上不能放大圓盤,在三根柱子之間一回只能移動一個圓盤。我們知道最少需要移動2^64-1次.在移動過程中發現,有的圓盤移動次數多,有的少 。告之盤子總數和盤號,計算該盤子的移動次數.
Input
包含多組數據,首先輸入T,表示有T組數據.每個數據一行,是盤子的數目N(1<=N<=60)和盤
號k(1<=k<=N)。
Output
對於每組數據,輸出一個數,到達目標時k號盤需要的最少移動數。
Sample Input
2
60 1
3 1
Sample Output
576460752303423488
4
(1)編程思路。
一般我們在學習程序設計時,會采用遞歸來解漢諾塔問題,N個圓盤共需要移動2N-1次。為求得N個圓盤中盤號為k的圓盤移動次數,我們通過列舉找出規律。
1個盤 1號:1次
2個盤 1號:2次 2號:1次
3個盤 1號:4次 2號:2次 3號:1次
4個盤 1號:8次 2號:4次 3號:2次 4號:1次
……
N個盤 1號:2N-1次 2號:2N-2次 ...k號:2N-k次 ...N號:1次
因此,本題實質求2N-k,采用快速冪運算完成。
(2)源程序。
#include <stdio.h>
typedef long long LL;
LL quickPower(LL a, LL b)
{
LL p = 1;
while (b)
{
if (b&1) p = p*a;
b >>= 1;
a = a*a;
}
return p;
}
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
LL n,k;
scanf("%lld%lld",&n,&k);
printf("%lld\n",quickPower(2,n-k));
}
return 0;
}
41-3 Pseudoprime numbers
本題選自北京大學OJ題庫(http://poj.org/problem?id=3641)。
Description
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
(1)編程思路。
題目是判斷輸入的p是不是偽素數。偽素數條件:①p不是素數。② ap = a (mod p)。
第①個條件編寫函數int isPrime(LL x),判斷整數x是否是素數,若是返回值1,否則返回值0;第②個條件進行快速冪運算。
(2)源程序。
#include <stdio.h>
typedef long long LL;
int isPrime(LL x)
{
for (LL i=2;i*i<=x;i++)
if (x%i==0)
return 0;
return 1;
}
LL quickPower(LL a, LL b,LL k)
{
LL p = 1;
while (b)
{
if (b&1) p = p*a%k;
b >>= 1;
a = a%k*a%k;
}
return p%k;
}
int main()
{
LL p,a;
while (scanf("%lld%lld",&p,&a))
{
if (a==0&&p==0) break;
if (isPrime(p)==1)
printf("no\n");
else
{
if(quickPower(a,p,p)==a%p)
printf("yes\n");
else
printf("no\n");
}
}
return 0;
}