找回密码
 欢迎注册
楼主: gxqcn

[擂台] 寻找最小的幂,其首部由连续若干个相同数字构成

[复制链接]
 楼主| 发表于 2009-8-11 09:40:19 | 显示全部楼层
这个问题必须借助高精度大数算法库。
它看似乘幂,实则为高精度开方,如果需要追求高效搜索的话。

我刚才用 6# 的代码搜索 a(200),结果如下:90832044031629032072266808763200776597377591732476977169715120443019881412500755990159501693981554567930162406118174820455381598058901681451049443406788180959583740354008958099702541401274789601262
其 200 次方的前 200 个连续数字均为 4
搜索耗时用了不到半小时,期间进行大数开方 352724 次,最大被开方数有 39392 位。

该算法效率比我最初预估的高了不少。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-8-11 19:46:08 | 显示全部楼层
我觉得用浮点数计算似乎要快点
有点懒得算,呵呵
还要装mpf

另外,为什么不用个记号呢
$mIn(p)$表示$p$位数字,其中前$n$位是由$m$重复得到的
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-8-11 21:22:31 | 显示全部楼层
import  java.math.BigDecimal;
import  java.math.MathContext;
public class root
{
public  static  void  main(String[]  args)  {

/*
System.out.println(Math.pow(2,  1.0  /  5));

System.out.println(root(2,  5));

BigDecimal  b  =  root(new  BigDecimal(2),  5);
System.out.println(b.toString());   
//可以用下面两条Java句子解决:
System.out.println(Integer.MAX_VALUE);//打印最大整数:2147483647
System.out.println(Integer.MIN_VALUE);//打印最小整数:-2147483648
//相应的浮点数:
System.out.println(Float.MAX_VALUE);
System.out.println(Float.MIN_VALUE);
//双精度:
System.out.println(Double.MAX_VALUE);
System.out.println(Double.MIN_VALUE);

for(int n=1;n<=14;n++)
label1:for(int m=1;m<=200;m++)
for(int j=1;j<9;j++)
{
if(Math.floor(Math.pow(Math.pow(10,m)*(Math.pow(10,n)-1)/9*j,1.0/n))+1
==Math.floor(Math.pow(Math.pow(10,m)*((Math.pow(10,n)-1)/9*j+1),1.0/n)))
{
System.out.print(n+":\t");
System.out.println(Math.floor(Math.pow(Math.pow(10,m)*((Math.pow(10,n)-1)/9*j+1),1.0/n)));

break label1;
}
}
*/
BigDecimal ONE=BigDecimal.ONE;
BigDecimal TEN=BigDecimal.TEN;
BigDecimal N9=TEN.subtract(ONE);
for(int n=1;n<=14;n++)
label2:for(int m=1;m<=200;m++)
for(int j=1;j<9;j++)
{
BigDecimal J=new BigDecimal(j);

BigDecimal M=root(TEN.pow(m).multiply(TEN.pow(n).add(ONE.negate()).divide(N9).multiply(J)),n).add(ONE);
BigDecimal N=root(TEN.pow(m).multiply(TEN.pow(n).add(ONE.negate()).divide(N9).multiply(J).add(ONE)),n);
if(M.toBigInteger().compareTo(N.toBigInteger())==0)
{
System.out.print(n+":\t");
System.out.println(M.toBigInteger());

break label2;
}
}


}


//  算法来源:http://bbs.51js.com/thread-59522-1-197.html,9楼的回帖
//  原算法使用  JavaScript  写的,使用  Java  改进了一下
//  这个算法的收敛速度很快,可以堪称优秀了
public  static  double  root(double  num,  int  radix)  {
double  result  =  1.0;
double  pow  =  result;
double  r  =  pow  /  result;
double  error  =  pow  -  num;
double  p  =  r  *  radix;
double  epsilon  =  1.0e-10;
while  (Math.abs(error)  >  epsilon)  {
  result  =  result  -  error  /  p;   
  pow  =  result;
  int  k  =  1;
  while  (k  <  radix)  {
   pow  *=  result;
   k++;
  }
  r  =  pow  /  result;
  error  =  pow  -  num;
  p  =  r  *  radix;
}
return  result;
}

//  BigDecimal  的  pow()  方法只能是  int  类型的,这样就不能计算
//  方根,按上述算法新增一个
public  static  BigDecimal  root(BigDecimal  num,  int  radix)  {
int  precision  =  6;  //  增大可以提高运算精度
BigDecimal  result  =  new  BigDecimal(1);
BigDecimal  pow  =  result;
BigDecimal  r  =  pow.divide(result);
BigDecimal  error  =  pow.subtract(num);
BigDecimal  p  =  r.multiply(new  BigDecimal(radix));
BigDecimal  epsilon  =  new  BigDecimal(10).pow(-precision,  new  MathContext(precision));
while  (error.abs().compareTo(epsilon)  >  0)  {
  result  =  result.subtract(error.divide(p,  new  MathContext(precision)));   
  pow  =  result;
  int  k  =  1;
  while  (k  <  radix)  {
   pow  =  pow.multiply(result);
   k++;
  }
  r  =  pow.divide(result);
  error  =  pow.subtract(num);
  p  =  r.multiply(new  BigDecimal(radix));
}
return  result;
}
}
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2009-8-12 07:55:44 | 显示全部楼层
牛顿迭代法是高精度算法中经常用要用到的基本技巧之一,包括用在开方运算中。

本题若无高效的大数算法库支持,效率和精度将无法保证,很难进行下去。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-8-12 21:03:06 | 显示全部楼层
要提高效率,怎么修改算法呢
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-8-14 22:42:04 | 显示全部楼层
首先就不能选Java语言
呵呵
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-8-15 16:00:27 | 显示全部楼层
这个题目里面Java不见得会比C效率差.主要是大数运算库的效率问题
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-8-18 21:28:16 | 显示全部楼层


  1. //n 为首部由连续相同数字的长度
  2. // 令 11111......111   = m (n 位相同的数字1)
  3. //k 为相同的数字( 1--9)
  4. //  [X] 取整

  5. for(k = 1;k<= 9;++k){//可能的首位相同的数字
  6.      for(i = 0;i<n;i++){//后面追加的0的个数
  7.     x = (m × 10^i) ^(1/n) ;  // ^(1/n)  开n次方
  8.     x = [x];
  9.     y = ((m+1) × 10^i) ^(1/n) ;  
  10.     y = [y];
  11.     if(x != y)
  12.    M.push_back(y);
  13.     }
  14. }

  15. return  min(M);

  16. //开方次数为 2 × 9  × n
  17. //手上没有现成的高精度大数运算接口.没程序.
  18. //使用上面的一些结果.符合.
复制代码
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-8-18 21:42:04 | 显示全部楼层
剔除 999999 n个9的情况。
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2009-8-18 22:35:48 | 显示全部楼层
我不认为虚拟机语言的高精度计算能比C更好

除了几个底层就支持高精度计算的语言,比如Smalltalk外
Java等,必须用包装了整数运算的类再包装高精度运算
而且很多语言不能解决双精度运算问题,只能模拟双精度运算
不过java等是否语言层次支持双精度运算,俺不清楚

包括C都无法避免双精度计算问题,只能用内嵌汇编避开
虚拟机语言做双精度计算效率更低

研究过C#的il,很失望的发现,内核级的双精度计算指令没有
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-4-20 20:46 , Processed in 0.048871 second(s), 15 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表