编程之美-找符合条件的整数 用字符串来表示大整数避免溢出



import java.util.LinkedList;

public class FindInteger {

/**
* 编程之美 找符合条件的整数 用字符串来表示大整数避免溢出
* 题目:任意给定一个正整数N,求一个最小的正整数M(M>1),使得N*M的十进制表示形式里只含有1和0
*
* 假设当前正在搜索由0,1组成的K位十进制数,这样的K位十进制数共有2^k个。
* 假设其中有两个数X、Y,它们模N同余,那么在搜索由0、1组成的K+1位十进制数时,
* X和Y会被扩展出四个数:10X, 10X+1, 10Y, 10Y+1。
* 因为X和Y同余(同余完全可以看作相等),所以10X与10Y同余,10X+1与10Y+1同余。
* 也就是说由Y扩展出来的子树和由X扩展产生出来的子树产生完全相同的余数,
* 如果X比Y小,那么Y肯定不是满足要求的最小的数,所以Y这棵子树可以被剪掉
*/
public static void main(String[] args) {
/*测试发现,在i=36时,方法一和方法二已经出错了
for(int i=1;i<=36;i++){
System.out.println(i+"-------------------");
System.out.println(find3(i));
System.out.println(find2(i));
System.out.println(find(i));
}
*/
for(int i=1;i<Integer.MAX_VALUE;i++){
System.out.println("("+find3(i)+" mod "+i+")=0");
}
}

/*
* 方法三:对方法二的改进
* 用字符串来表示大整数,避免溢出
* 难点在于,如何由X求得(10*X)以及(10*X+1)对n的余数:
* if X%n=q, X=n*K+q
* then (10*X)%n=(10*n*K+10*q)%n=(10*q)%n
*/
public static String find3(int n){
if(n<=0){
return null;
}
if(n==1){
return "1";
}
String[] data=new String[n]; //data[i]代表(x%n=i)的x,x用字符串表示:"1101" --> int x=1101
data[1]="1";
int k=2;
while(true){ //不必担心这是个死循环,可以证明,M是一定存在的
for(int i=0;i<n;i++){
String di=data[i];
if(di==null){
continue;
}
int len=di.length();
if((len+1)==k){ //K-->K+1
String s=di+"0"; //di*10
String t=di+"1"; //di*10+1
int rs=(i*10)%n; //(di*10)%n=(i*10)%n
int rt=(i*10+1)%n; //(di*10+1)%n=(i*10+1)%n
if(rs==0){
return s;
}else if(data[rs]==null || greaterThan(data[rs],s)){ //只保留最小的data[i]
data[rs]=s;
}
if(rt==0){
return t;
}else if(data[rt]==null || greaterThan(data[rt],t)){
data[rt]=t;
}
}
}
k++;
}

}

/*
* 比较由s和t代表的数字的大小。按位比较,从高位到低位。
* 如果s比t大,返回true,否则返回false
*/
public static boolean greaterThan(String s,String t){
if(!s.matches("[0-9]+") || !t.matches("[0-9]+")){
return false;
}
if(s.length()!=t.length()){
return s.length()>t.length();
}
int len=s.length();
char[] ss=s.toCharArray();
char[] tt=t.toCharArray();
for(int i=0;i<len;i++){
if(ss[i]!=tt[i]){
return ss[i]>tt[i];
}
}
return false;
}

//方法一:因为N*M的取值就是1,10,11,100,101,110,111,......所以直接在这个空间搜索
public static int find(int n){
if(n<=0){
return -1;
}
if(n==1){
return 1;
}
LinkedList<Integer> queue=new LinkedList<Integer>();
queue.add(1);
while(!queue.isEmpty()){
int t=queue.pollFirst(); //Retrieves and removes
if(t%n==0){
return t;
}
queue.addLast(t*10);
queue.addLast(t*10+1);
}
return -1;
}

//方法二:将方法一的搜索空间按模N余数分类,但没有解决N*M超出Integer.MAX_VALUE的溢出问题
public static int find2(int n){
if(n<=0){
return -1;
}
if(n==1){
return 1;
}
int[] data=new int[n];
data[1]=1;
int k=2;
while(true){
for(int i=0;i<n;i++){
int di=data[i];
if(di==0){
continue;
}
int len=0;
int dii=di;
while(dii!=0){ //计算是几位整数。计算K+1位时只需考虑K位
dii /=10;
len++;
}
if((len+1)==k){
int s=di*10;
int t=di*10+1;
if(s%n==0){
return s;
}else if(data[s%n]==0 || data[s%n]>s){
data[s%n]=s;
}
if(t%n==0){
return t;
}else if(data[t%n]==0 || data[t%n]>t){
data[t%n]=t;
}
}
}
k++;
}

}


}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值