多位水仙花数算法

多位水仙花数算法

1.递归(用时16-20s;写起来非常方便,非常爽):

import java.math.BigInteger;
import java.util.ArrayList;

/**
 * 三位的水仙花数共有4个:153,370,371,407;  
	四位的水仙花数共有3个:1634,8208,9474;   
	五位的水仙花数共有3个:54748,92727,93084;   
	六位的水仙花数只有1个:548834;   
	七位的水仙花数共有4个:1741725,4210818,9800817,9926315;   
	八位的水仙花数共有3个:24678050,24678051,88593477 
 * @author Administrator
 *ps : 把new BigInteger(),改为BigInteger.valueof(); 把运行时间从35秒减低到20秒
 */
public class FlowerNumber_simple {
	private int size;
	private ArrayList<BigInteger> resultLists;
	private int[] base_num_count;
	private BigInteger[] subResult;
	
	public FlowerNumber_simple(int size){
		this.size = size;
		resultLists = new ArrayList<BigInteger>();
		base_num_count = new int[10];
		subResult = new BigInteger[10];
		
		/*for(int i=0;i<10;i++){
			subResult[i] = new BigInteger(i+"").pow(size);
		}*/
		for(int i = 0; i < 10; ++i){
			//subResult[i] = BigInteger.valueOf(i).pow(this.level);
			subResult[i] = BigInteger.ONE;
			for(int j = 0; j < size; ++j){
				subResult[i] = subResult[i].multiply(BigInteger.valueOf(i));
			}
		}
		
		
	}
	
	
	public static void main(String[] args) {
		FlowerNumber_simple fn = new FlowerNumber_simple(21);
		long start = System.currentTimeMillis();
		fn.backTracking(0, 0);
		for(BigInteger b : fn.resultLists){
			System.out.println(b);
		}
		long times = System.currentTimeMillis()-start;
		System.out.println(times/1000+"秒");
	}
	
	private void backTracking(int level,int current_index){
		if(level>=size){
			getresult();
			return ;
		}
		for(int i=current_index;i < 10;i++){
			base_num_count[i] ++;
			backTracking(level+1, i);
			base_num_count[i] --;
		}
		
	}

	private void getresult() {
		
		BigInteger result = BigInteger.ZERO;
		for(int i=0;i<10;i++){
			if(base_num_count[i]!=0){
				//result = result.add(subResult[i].multiply(new BigInteger(base_num_count[i]+""))  );
				result = result.add(subResult[i].multiply(BigInteger.valueOf(base_num_count[i])));
			}
		}
		String result_str = result.toString();
		if(result_str.length()== size){
			int[] same_num_count = new int[10];
			for(int i =0 ;i<size;i++){
				same_num_count[result_str.charAt(i)-'0'] ++;
			}
			for(int i=0;i<10;i++){
				if(same_num_count[i] != base_num_count[i])
					return ;
			}
			resultLists.add(result);
		}
	}
	
}



2.栈回溯(用时1s-3s;极速):

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Set;


/**
 * 寻找水仙花数问题
 * @author lixinji
 * 从三方面优化循环
*0.把常用的大数都缓存在数组。(21位水仙花数,缓存和不缓存时间相差10s到30s之间)
 * 1.循环对象优化:利用组合来穷举,例如0个9,1个9....9个9循环所有的可能性。只要这个组合不符合,那么可以排除很多数
 * 2.上下限优化:在特定的组合下,计算最小值,最大值,然后和最小值常量,最大值常量比较,如果超出那么立马排除
 * 3.组合违背优化: 在特定的组合下,比较最小值和最大值高位相同的部分,如果出现与特定组合的情况,排除。(例如组合假定只有2个9,而最小大值高位相同部分计算结果有3个,很明显不符合)
 */
public class FlowerNumber {
	private int size; 			//位数
	private  BigInteger MIN_CONSTANT; 	//最小数常量
	private  BigInteger MAX_CONSTANT;	//最大数常量
	private Hashtable<String, BigInteger> ht;  //存放常用的大数,用hashtable是未了提高效率
	private Set<BigInteger> resultsets = new HashSet<BigInteger>(); //存放结果集合
	private int index;//索引,其中9-index代表基数
	private int[] base_num_count;		// base_num_count[index] 表示当前基数的组合个数
	private int[] sum_num_count;		//sum_num_count[index] 表示当前循环总基数的组合个数
	private BigInteger[] totalResult;	//sum_num_count[index]  代表当前循环基数的总结果
	
	


	public FlowerNumber(int size) {
		this.size = size;
		int s = size<10?10:size;
		base_num_count = new int[s];
		sum_num_count = new int[s];
		totalResult = new BigInteger[s];
		ht = new Hashtable<String, BigInteger>();
		
		for(int i=0;i<=s;i++){ //存放基数,位数的大数字
			ht.put("n_"+i, new BigInteger(i+""));
		}
		for(int i=0;i<=10;i++){ //存放基数的size次方结果
			ht.put("p_"+i, new BigInteger(i+"").pow(size));
		}
		MIN_CONSTANT=N(10).pow(size-1);
		MAX_CONSTANT=P(10).subtract(N(1));
	}
	private BigInteger N(int i){
		return ht.get("n_"+i);
	}
	private BigInteger P(int i){
		return ht.get("p_"+i);
	}
	
	public static void main(String[] args) {
		
		FlowerNumber fn =new FlowerNumber(21); //这里更改位数
		int s = fn.MAX_CONSTANT.divide(fn.P(9)).intValue();//求出P(9)的除数
		for(int i=0;i<=s;i++){
			fn.find(i);
		}
		BigInteger[] results =new BigInteger[fn.resultsets.size()];
		fn.resultsets.toArray(results);
		Arrays.sort(results);
		for(BigInteger bi : results){
			System.out.println(bi);
		}
		
	}
	// 检查组合是否合格,如果组合位数未满,或者组合匹配正确返回真;不合格,返回假。
	private boolean checkCombination(){
		BigInteger minVal = totalResult[index];
		BigInteger maxVal = totalResult[index].add(P(9-index).multiply(N(size-sum_num_count[index])));
		
		//检查是否违反上下限
		if(minVal.compareTo(MAX_CONSTANT)>0) return false;
		if(maxVal.compareTo(MIN_CONSTANT)<0) return false;
		String minStr = minVal.toString();
		String maxStr = maxVal.toString();
		
		char c;
		int sameCount[] = new int[10];
		for(int i=0;i<size;i++){
			if((c = minStr.charAt(i)) == maxStr.charAt(i)){
				sameCount[c-'0']=sameCount[c-'0']+1;
			}else{ break;}
		}
		
		//检查是否违反组合设定
		for(int i =0;i<index;i++){
			if(base_num_count[i]<sameCount[9-i]){
				return false;
			}
		}
		
		if(sum_num_count[index] == size){
			
			
			BigInteger result = BigInteger.ZERO;
			String totalStr = totalResult[index].toString(); 
			for(int i=0;i<size;i++){
				int j=totalStr.charAt(i)-'0';
				result = result.add(P(j));
			}
			
			return totalResult[index].compareTo(result) == 0;
		}
		
		return true;
	}
	
	private void setValue(int num){  //num 代表当前index(基数为9-index) 的组合个数;totalResult[index-1]永远是指前一个基数的总和
		base_num_count[index] = num;
		if(index == 0){
			sum_num_count[index]=num;
			totalResult[index] = P(9-index).multiply(N(num));
		}else{
			sum_num_count[index] = sum_num_count[index-1]+num;
			totalResult[index] = totalResult[index-1].add(P(9-index).multiply(N(num)));
		}
	}
	private void find(int i) {
		index =0;
		int startValue=i;
		setValue(startValue);
		while(true){
			if(checkCombination()){
				if(sum_num_count[index]==size){
					resultsets.add(totalResult[index]);
					//System.out.println(totalResult[index]);
					if(back()) break;
				}
				//组合基数递减并全部填充;当基数为0时,证明尾数已经循环完了,不进入改分支
				if(index !=9 ){
					index ++;
					setValue(size-sum_num_count[index-1]);
					continue;
				}
			}
			//基数数目减少一个
			if(back()){
				break;
			}
		}
		
	}
	private boolean back() {
		if(checkEnd()) return true;
		if(base_num_count[index] ==0)
		{
			while(base_num_count[index] == 0){
				if(index > 0)
					index --;
				else 
					return true;
			}
		}
		//如果当前基数不是9,那么当前基数数目减1,设回原来的值
		if(index >0){
			setValue(base_num_count[index]-1);
			return false;
		}else{
			return true;
		}
		
	}
	
	private boolean checkEnd(){
		for(int i=0;i<=9;i++){
			if(base_num_count[i] !=0)
				return false;
		}
		return true;
	}
	
	
}



你可能感兴趣的:(多位水仙花数算法)