整数的幂

整数的幂(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人。

点赞