整数的幂(Integer’sPower)
(10102510352)
问题描述
有一些整数,能够用较小的整数的幂来表示,比如9=3^2, 64=2^6, 1000=10^3, 对于一个给定的正整数y,如果我们能找到一个最大的整数k和一个最小的整数x,使得x^k=y,那么k被称为y的幂(power)。题目要求计算从整数a到整数b之间所有数的幂之和。
已知:两个整数a,b,其中(2 <= a <= b <= 10^18);
求解:a,b两数之间所有数(包括a,b)的幂之和
解题思路
根据已知条件和题目要求,经过具体分析,这道题应该为数学计算题,需要我们编程去实现
从题目给出的条件,我们可以将a到b之间的数分为两大部分:能被开方的数和不能被开发的数。不能被开方的数其幂为1,能开方的数的幂值我们只需找到开方的次数(例如9能开2次方根和1次方根,2最大,所以9的幂为2)最大的值k,则k为该数的幂。因此,我们需要一个算法,首先判断该数是否能被开方,从而可以将这些数字区分开;其次,我们得需要一个算法去获取该数能开方的最大次数;写出这两个算法,这两个问题也就迎刃而解了。当然,在这些数中,还有一些数是不需要去划分的—素数,素数的幂肯定为1,所以素数可以直接使它的幂等于1即可,不需要划分和计算。
算法分析与设计
从最简单的问题入手,首先判断这个数是否为素数,我所使用的编程语言里面没有判断素数的API,于是必须自己实现这个判断算法。
素数的判断方法很简单。例如一个数a(a>2),只需要判断从2开始一直到之间的所有数都不能整除a,则a便为素数了。
伪代码如下:
isPrime(a)
b = √a
for i=2….b
if a/i 的余数为0
return true
return false;
其次,再分析判断一个数是否能被开方这个算法。对于一个整数a,最简单的方式就是判断a是否能开k(k=2……a)次方,从k=2开始遍历a-1次,便可判断a是否能被开方,并且同时可以得出k的最大值(遍历的时候可以获取得到)。暂时先不讨论怎么判断a是否能开方,是否有必要从2到a都遍历才能判断a是否被开方呢?根据经验判断,一般情况下,a不能开a-1次方,a-2次方等,怎样缩小k的取值范围将大大提到判断开方这个算法的性能。结合数学知识,我顺利的缩小了k的范围:
对于x^k = a
x = a^(1/k)
我们知道x>=2
因此
a^(1/k)>=2
所以 k<=log2a
所以我们只需要遍历从2到log2a判断a是否能够开方。
在讨论判断开方的算法,在我使用的编程语言里(java)是提供了开方的方法,即Math.pow(double a,double b)函数,但是函数返回的是一个double类型,在计算机中浮点运算永远不能精确的得到数值,总是一个很精确的近似值。但是判断一个数能否被开方,如果能够被开方,其结果一定为整数,不能得到近似值。因此判断开方必须得自己实现。
我想到的是一个比较简单的方法,比如要判断9是否能被3开方,只需要遍历从2到9的数,分别进行3次方运算,如果某个数三次方得到的结果正好等于9,则9便能开3次方根。一开始我的程序就是这么实现的,对已比较小的数据,程序运行时间还是很短,但是一旦测试的数据为百万级别的时候,程序便慢的不行,我输出了4到1000000这两数之间所有幂之和的计算时间(我的机器芯片是Inter Core i7 2.8GHz),结果竟然要170s,这简直是难以接受的。最后确定这种判断是否被开方的方法必须得抛弃掉。
后来,我想到了另外一种办法。我先用java里面的开方方法Math.pow(a,b)计算出一个近似值,并将其强制转化为整形k,然后我再计算从k-1到k+1这三个数之间是否有数进行b次相乘能够等于a,如果有,则a能被b开方,否则不能。伪代码如下:
canRoot(a,b)
c = (integer)power(a,1/b)
for c-1…c+1
muti :=1
for 1….b
muti := muti*c
if muti = a
return true
return false;
至此,所有关键的算法都已经讨论并且实现完毕。
程序说明
1、boolean isPrime(long a)
判断一个数是否为素数,由于题目要求,因此参数为长整形,如果该数为素数,则返回true,否则返回false。
2、booean canRoot(long a, long b)
判断一个数a是否能被b开方,如果能够开方,则返回true,否则返回false。
3、void main(String args[])
程序的主函数,也是程序的入口,负责整个程序的逻辑控制,以及核心算法的实现。
程序清单
package sei.zwf.main;
import java.math.BigDecimal;
import java.util.Scanner;
public class IntegerPower {
public static void main(Stringargs[])
{
long a=0,b=0;
Scanner cin =new Scanner(System.in);/*input from keyboard*/
print(“请输入一个数值a,值的范围为2~1*10^18>”);
if(cin.hasNextLong())
a =cin.nextLong();
print(“请再输入一个数值b,值的范围为2~1*10^18>”);
if(cin.hasNextLong())
b = cin.nextLong();
cin.close();
long powerSum = 0;/*init the sum of power*/
long startTime = System.currentTimeMillis();
for(long i=a;i<=b;i++)
{
if(isPrime(i))
powerSum +=1;
else{
boolean isRootable =false;/*judge the sum is rootable ,if rootbale,break*/
long log2value = Math.round(Math.log(i)/Math.log(2))+1;
for(long j=log2value;j>=1;j–)
{
if(canRoot(i,j)){
powerSum += j;
isRootable = true;
break;
}
}
if(!isRootable)
powerSum +=1;
}
}
long endTime= System.currentTimeMillis();
print(“The sum of the power between a and b is “+powerSum);
print(“The caculating time is “+(endTime-startTime)/1000+“s”);
}
public static void print(String str)
{
System.out.println(str);
}
/*judge whether a can be rooted by b*/
public static boolean canRoot(long a,long b)
{
long k = (long)Math.pow(a, 1d/b);
for(long i=k-1;i<=k+1;i++)
{
long muti = 1;
for(long j=0;j<b;j++)
muti *=i;
if(muti==a)
return true;
}
return false;
}
/*judge whether a is a prime*/
public static boolean isPrime(long a)
{
long sqrt = (long)Math.sqrt(a);
for(long i=2;i<=sqrt;i++)
{
if(a%i==0)
return false;
}
return true;
}
}
注:转载请注明出处,做一个文明的IT人。