怎样判断一个整数是不是幂的形式

bacmoz 2006-08-06 10:34:47
如9 = 3^2
125 = 5^3
即可以表示成a的b次方的形式(a,b为整数,b>1)
...全文
1430 48 打赏 收藏 转发到动态 举报
写回复
用AI写文章
48 条回复
切换为时间正序
请发表友善的回复…
发表回复
yyfhz 2006-09-05
  • 打赏
  • 举报
回复
啊,这个应该是我记错了,谢谢mathe()指出,很久没有碰数学书了,嘿嘿
mathe 2006-09-04
  • 打赏
  • 举报
回复
yyfhz瞎扯了,在1~N之间的素数个数大约是N/log(N)个(其中log以e为底),这是素数定理
yyfhz 2006-09-04
  • 打赏
  • 举报
回复
假设说Y是一个幂数字,则有
Y=(a1*a2*...*an)^(b1*b2*...*bm),其中a1<=a2<=...<=an, b1<=b2<=...<=bm都是质数。
我们现在来找b1。
b1的最小值当然是2, b1的最大值是Log2(Y)。

记得有一位数学家曾经说过:对于足够大的N, 在数字1~N之间的质数的个数和Log2(N)成正比例。
换言之,当Y很大时,在1~Log2(Y)中,一共有 k*Log2( Log2(Y) )个质数,
这个数量可以说是相当的少了,例如当Y在 10^100 这个范围内,质数的个数大约是200个。
可以整理一个够大的质数表,作为开方的指数。
一般来说,这个质数表能有1000个质数就差不多拉。
gxqcn 2006-08-28
  • 打赏
  • 举报
回复
为叙述方便,定义“k|n”为“整数n可以被整数k整除”;“^”表示乘幂。


Input: u32Num ( 0 <= n < 2^32 )
Output: TRUE/FLASE - u32Num 是否可以表示成 k^r 形式( k、r∈N,r≥2 )

1、令 u32Odd、u32RShift,使得 u32Num = ( 2^u32RShift ) * u32Odd,其中 u32Odd 为 0 或 一奇数;

2、if u32Odd <= 1 then return TRUE;(要么为“0”,要么为 2^u32RShift)

3、判定是否可表示成 k^r 型,其中 r = 31, 29, 23, 19, 17, 13, 11, 7 之一,显然此时的前提条件是 u32Odd 是否为 k^r 型;
由于 r 较大,所以仅用比较,遍历一个size=18的数组即可完成:
3.1 { 3^19、3^17、5^13、3^13、7^11、5^11、23^7、21^7、19^7、17^7、15^7、13^7 }
3.2 { 3^11 }
3.3 { 11^7、9^7、7^7 }
3.4 { 5^7 、3^7 }
如果 u32Odd 在 3.1 中,if u32RShift==0 then return TRUE; else return FLASE;
如果 u32Odd 在 3.2 中,if "11|u32RShift" then return TRUE; else return FLASE;
如果 u32Odd 在 3.3 或 3.4 中,if "7|u32RShift" then return TRUE; else return FLASE;
(程序中,并没有直接进行“k|n”的判定,而是通过简单的“==”进行优化)

4、判定是否可表示成 k^r 型,其中 r = 5, 3, 2 之一;
4.1 判定是否可表示成 k^2 型:前提条件:2|u32RShift 且 “8|(u32Odd-1)”(奇数完全平方数被8除必余1),如果可以表示成 k^2 型,return TRUE;
4.2 判定是否可表示成 k^3 型:前提条件:3|u32RShift,如果可以表示成 k^3 型,return TRUE;
4.3 判定是否可表示成 k^5 型:前提条件:5|u32RShift,如果可以表示成 k^3 型,return TRUE;

5、return FLASE;



优化说明:
一、仅通过是否为素数次幂进行判定,极大地减少了不必要的测试工作;
二、将对整数判定转化为对奇数判定,从而缩小了范围,并为后期预判定提供了快速依据;
三、对大指数型的测试,仅通过比较运算即可排除或肯定,算法非常高效;
四、对完全平方数,先通过是否满足“8|(u32Odd-1)”进行快速筛选;
五、不调用任何系统 math 算法库,不使用任何浮点指令。


最后,to bigc2000(公元2005年4月9日) :
我说的不是你所说的“我说得16ms问题是1000!”,而是10000!,请注意仔细阅读!
积木 2006-08-28
  • 打赏
  • 举报
回复
这样的问题,首先生成一个幂数字的文件。
然后每次运行程序的时候都用这个文件来初始化一次。
遇到一个数字就在里面搜索一次就可以了->用内存换时间,
2分搜索你总会吧。
gxqcn 2006-08-28
  • 打赏
  • 举报
回复
要不是为了证明人品问题(“做人要厚道!”),我不会花功夫去研究该算法;至于输赢本无所谓。

而“10000!”也确确实实就是“精确计算并输出10000的阶乘的具体数值”。

我本人对整数类的问题研究得比较多,“HugeCalc”即是多年开发的产物,阁下可从各大下载站点下载体验之。
bigc2000 2006-08-28
  • 打赏
  • 举报
回复
你赢了!

10000!我把它看成是阶乘,我不知道windows下calc.exe怎么算的,你说只要16ms,所以我表示怀疑,calc估计也达不到。它超过3万多位。末位联系0的个数都有2499。这个就是我怀疑的地方,所以上面有疑惑,请别见怪

我的算法,相比之下,非常笨重,唯一的好处是对于任意给定的整数具有同一的算法。
下面是运行结果:第一个是你的,第二个是我的,10w个最大数字2^31-1数字

从2147283647 到2147383647 用时15 ms,统计有1个幂数字
2147302921是幂数

从2147283647 到2147383647 用时1450 ms,统计有1个幂数字
2147302921是幂数

如果是debug的话,你的约为170ms,我的约为1800ms。

题外话:本人比较笨,实在看不出来你说的和超超的枚举指数有多大关系。
好了,这个贴我就到此为止了。
你赢了!恭喜
bigc2000 2006-08-27
  • 打赏
  • 举报
回复
做人要厚道!
bigc2000 2006-08-27
  • 打赏
  • 举报
回复
刚看了下,实在佩服!连汇编都用上了。把它copy下来来看看。不过,我好像没有搞清楚
我说得16ms问题是1000!(无关紧要了).只是你这个程序与我的思路没有多大的区别,我是把数字 N = N/质数i;不断迭代下去,每次都与前面出现的质素次数的最大公约数,如果为1就为false。
这个,我想你的速度应该要快些,但不是因为算法的缘故,而是你用的是静态算法,即你先目测了一些质数,和可能的最大指数,换句话说,如果我预先得到一些可能质素,那么我的算法次数更少(但可能有除法而慢一些)
最坏情况是做了log2(N)次除法和取余,或者{查找到可能为N的开方根的最大质素所需要的次数}的这么多次取余算法。当然找到质素的开销没有算在内。
最好情况是一次除法,二次取余。比如N被6整除,但不能被4整除的任何数。

对于任意给定的N,我计算的最大复杂性最坏情况主要集中在查找N的开方以内的质素。所以说我枚举底数,比如说是查找质素。希望你也分析下你得先。

还有你的算法与你所的枚举指数 好像并没有多大关系

谢谢你的回复,不用称我为阁下。我copy你写的代码先
gxqcn 2006-08-27
  • 打赏
  • 举报
回复
请将 IsPower() 函数中的第一个“u32Try = (( u32Max + u32Min ) >> 1 );”移到“do”前。
gxqcn 2006-08-27
  • 打赏
  • 举报
回复
to bigc2000(公元2005年4月9日):
看样子,我再不给出代码就被人称为“不厚道”了。
阁下既会java又会c,比我强多了(我就会c);希望能看到你公正的对比测试报告。


// 快速判定一个 UINT32 类型的整数是否为整数次幂
// gxqcn@163.com(2006-08-27)

#include <stdio.h>

typedef unsigned int UINT32;
typedef long BOOL;
#define TRUE 1;
#define FALSE 0;

const UINT32 Pow2( const UINT32 u32Base )
{
return u32Base * u32Base;
}

const UINT32 Pow3( const UINT32 u32Base )
{
return u32Base * u32Base * u32Base;
}

const UINT32 Pow5( const UINT32 u32Base )
{
const UINT32 u32Square = u32Base * u32Base;
return u32Square * u32Square * u32Base;
}

typedef const UINT32 (*fnPow)( const UINT32 );

const BOOL IsPower( const UINT32 u32Num, const UINT32 u32Exp/* 2, 3, or 5 */ )
{
UINT32 u32Pow;
UINT32 u32Root;
UINT32 u32Try;
UINT32 u32Max;
UINT32 u32Min;

fnPow pfnPow;

switch( u32Exp )
{
case 2:
u32Min = 1UL;
u32Max = 65536UL;
pfnPow = Pow2;
break;

case 3:
u32Min = 1UL;
u32Max = 1626UL;
pfnPow = Pow3;
break;

case 5:
u32Min = 1UL;
u32Max = 85UL;
pfnPow = Pow5;
break;

default:
return FALSE;
}

do
{
u32Try = (( u32Max + u32Min ) >> 1 );
u32Pow = ( *pfnPow )( u32Root = u32Try );

if ( u32Num > u32Pow )
{
u32Min = u32Try;
}
else if ( u32Num < u32Pow )
{
u32Max = u32Try;
}
else if ( u32Num == u32Pow )
{
return TRUE;
}

u32Try = (( u32Max + u32Min ) >> 1 );

} while( u32Root != u32Try );

return FALSE;
}

int main(int argc, char* argv[])
{
UINT32 u32Num;
UINT32 u32RShift;
UINT32 u32Odd;
BOOL bIsPower;

do
{
printf( "\nn = " );
scanf( "%u", &u32Num );

_asm
{
mov eax, u32Num
bsf eax, eax
mov [u32RShift], eax
}

u32Odd = u32Num >> u32RShift;

bIsPower = ( 1 >= u32Odd );
while ( !bIsPower )
{
if ( 1162261467UL /* 3^19 */ == u32Odd \
|| 129140163UL /* 3^17 */ == u32Odd \
|| 1220703125UL /* 5^13 */ == u32Odd \
|| 1594323UL /* 3^13 */ == u32Odd \
|| 1977326743UL /* 7^11 */ == u32Odd \
|| 48828125UL /* 5^11 */ == u32Odd \
|| 3404825447UL /* 23^7 */ == u32Odd \
|| 1801088541UL /* 21^7 */ == u32Odd \
|| 893871739UL /* 19^7 */ == u32Odd \
|| 410338673UL /* 17^7 */ == u32Odd \
|| 170859375UL /* 15^7 */ == u32Odd \
|| 62748517UL /* 13^7 */ == u32Odd )
{
bIsPower = ( 0 == u32RShift );
break;
}

if ( 177147UL /* 3^11 */ == u32Odd )
{
bIsPower = ( 0 == u32RShift || 11 == u32RShift );
break;
}

if ( 19487171UL /* 11^7 */ == u32Odd \
|| 4782969UL /* 9^7 */ == u32Odd \
|| 823543UL /* 7^7 */ == u32Odd )
{
bIsPower = ( 0 == u32RShift || 7 == u32RShift );
break;
}

if ( 78125UL /* 5^7 */ == u32Odd \
|| 2187UL /* 3^7 */ == u32Odd )
{
bIsPower = ( 0 == u32RShift || 7 == u32RShift || 14 == u32RShift );
break;
}

if ( 0 == ( u32RShift % 2 ) && 1 == ( 7 & u32Odd ) && IsPower( u32Odd, 2 ))
{
bIsPower = TRUE;
break;
}

if ( 0 == ( u32RShift % 3 ) && IsPower( u32Odd, 3 ))
{
bIsPower = TRUE;
break;
}

if ( 0 == ( u32RShift % 5 ) && IsPower( u32Odd, 5 ))
{
bIsPower = TRUE;
break;
}

break;
}

if ( bIsPower )
{
printf( "%u is a perfect power!\n", u32Num );
}
else
{
printf( "%u is not a perfect power!\n", u32Num );
}

} while( 0 != u32Num );

return 0;
}

/* 附记:
1、本算法是根据特定的判定对象定制的,不具备普适性;
2、本算法全部为整型运算,未调用任何浮点指令;
3、本算法中少量取余还可以进一步优化;IsPower() 函数还可进一步优化,或可采用 Newton's iteration
*/

另外,如果仅是统计某范围内“perfect power”的数目,可先计算各素数指数时可取的最大底数,而后通过容斥原理可快速算得。
yuxh 2006-08-26
  • 打赏
  • 举报
回复
我写的一个十进制数运算类,ispow()判断是否是整数次幂
http://www.cublog.cn/u/2882/
bigc2000 2006-08-26
  • 打赏
  • 举报
回复
知道16ms的概念吗?
我可以用它完成10000!的精确计算;
或者搜索并输出 12000000 以内的所有素数(788060个素数)!

------------------------
谢谢你的回复,请将你的代码写出来,我来测试下先,第一我是用java程序写的,如果你用c
我会改成相应c,第二,我是在debug条件下的,而不是release。
bacmoz 2006-08-26
  • 打赏
  • 举报
回复
感谢大家讨论,我这个算法本来是作为预判断使用的,如果不是幂的形式就进行下一步的计算
如果复杂度稍高一点就没有意义了(因为本来幂形式的数就不多,因此减少的运算量可能就不太大)
复杂度高一点点,对整个过程都很有影响,因为几乎对每个数字(范围之内的)都要判断

后来我不采用这种方法了
gxqcn 2006-08-26
  • 打赏
  • 举报
回复
对于任意个int大数时间不超过16ms。
================================

知道16ms的概念吗?
我可以用它完成10000!的精确计算;
或者搜索并输出 12000000 以内的所有素数(788060个素数)!

n^x 远比 x^n 增长得快,所以 mmmcd(超超)所言“枚举指数效率要远高于枚举底数”是合理的。
bigc2000 2006-08-26
  • 打赏
  • 举报
回复
Tp mmmcd(超超) ( ) 信誉:124 Blog 2006-08-25 18:54:00 得分: 0
不知道您那个时间是多少!
int java中 最大只能是2^31-1,对于任意个int大数时间不超过16ms。

做枚举的话,枚举指数效率要远高于枚举底数。

可是,这个不正确的,比如幂数N = M^2,N是个很大的数,M也不小,这必然要枚举底数
而且如果从小数枚举的时间不少,
按照你的说法,这样的程序
采用 从大到小来枚举,比如你的程序(不知道我理解的对不对)
int i = 0;
e =2;
do{
i = (int)extract(N,e);//开方运算将N开e次方,转化为int型
if(i <= 1) return false;
if(pow(i,e) == N)
{
return true;

}
else
e++;
}

上面是指数从小到大枚举(从大到小也可以,结果也差不多),可是有个问题,如果是2^N(最坏情况),收敛速度是比较快的
但有个问题,浮点数开方运算远比 加法运算和乘法,除法整数运算,慢很多。所以我得出,效率比你提供算法高的结论。像65536以内的所有数字,所用时间均显示为0ms

当然的确像你说的如果是1w位,或许你的算法更快。我没法去测试(我喜欢用java)。
mmmcd 2006-08-25
  • 打赏
  • 举报
回复
呵呵
虽然楼主说“不需要这个算法了”,讨论一下确实很有意思。

如果输入长达1万位的整数,判断一下是否幂的形式?
mmmcd 2006-08-25
  • 打赏
  • 举报
回复
做枚举的话,枚举指数效率要远高于枚举底数。
如果数太小就没可比性,
就像快速排序在元素少时效率可能比冒泡排序低。
mmmcd 2006-08-25
  • 打赏
  • 举报
回复
topcoder的SRM305这道题:

Problem Statement for PowerCollector


Problem Statement
Your friends collect butterflies and stamps, but you collect numbers. Your collection of prime numbers is the finest in three counties, and your collection of transcendental numbers has been featured on national talkshows. Lately you've decided to start collecting powers, that is, numbers that can be written in the form MK, where M and K are positive integers with K > 1. However, you're wondering how big a box you'll need to hold them all. Given a number N, represented as a String, determine how many powers lie between 1 and N, inclusive. Return the number of powers as a String with no leading zeros.


Definition
Class: PowerCollector
Method: countPowers
Parameters: String
Returns: String
Method signature: String countPowers(String N)
(be sure your method is public)




Constraints
- N will contain between 1 and 19 characters, inclusive.
- Each character in N will be a digit ('0'-'9').
- The first character in N will not be zero ('0').
- N will represent a number between 1 and 1000000000000000000 (10^18), inclusive.

Examples
0)
"10"


Returns: "4"

The powers between 1 and 10 are 1, 4, 8, and 9.


1)
"36"


Returns: "9"

The powers between 1 and 36 are 1, 4, 8, 9, 16, 25, 27, 32, and 36.


2)
"1000000000000000000"


Returns: "1001003332"



输入N,统计1~N之间多少个整数是幂的形式
N最大10的18次方
bigc2000 2006-08-25
  • 打赏
  • 举报
回复
mmmcd(超超) ( ) 信誉:124 Blog 2006-08-25 11:28:00 得分: 0
呵呵
虽然楼主说“不需要这个算法了”,讨论一下确实很有意思。

如果输入长达1万位的整数,判断一下是否幂的形式?
-------------------
这就不好说了,不知道那位数学家能够写出幂数的通式!恐怕这个是最重要的,也就是方法是关键,至于效率可以改进

还有我发现我的方法(今天我有改进了些)比你说的要快好多倍

gxqcn(★) HugeCalc ← http://maths.diy.myrice.com/ (☆) ( ) 信誉:100 Blog

你的算法我不太明白(我很愚钝),写出算法来,我来看看速度就可以了

讨论这个确实有点意思
我又选了最小的数字 1--64000 总共花时间63ms(我机器512M,赛扬D2.4G)




加载更多回复(28)

33,006

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧