算法思路改进

这段时间学习效率很低,论文进展的很缓慢,思考了这些“不务正业”的事情,就当边玩边学吧。前不久在网上看到一个问题,

0出现的总次数__,1出现的总次数__

2出现的总次数__,3出现的总次数__

4出现的总次数__,5出现的总次数__

6出现的总次数__,7出现的总次数__

8出现的总次数__,9出现的总次数__
最初的分析,就是要得到一个长度为10的数组int[],统计10个元素出现的次数后,数字i出现的次数+1正好等于int[i]的数值。比如数组10个元素中数字1出现了5次,那么int[1]的值为6(因为题目中红色数字0~9各出现了一次)。所以,要解决的问题就是,要怎么去寻找符合要求的长度10数组。
       1、最简单粗暴的方法就是穷举法。我刚开始也是用的穷举,看看有没有解再说,顺便看看到底要花多少时间。创建两个长度为10的数组num[]和com[],不难分析出,数组的元素至少是1,所以数组中元素的初值从1而不是0开始。num数组存放穷举的元素,从[1]...[1]到[9]...[9](现在还只讨论不超过9的数)。com[]赋初值也是1,统计num中数字出现的次数,然后对比num和com,如果相同,num就满足要求,不同就继续穷举。

用穷举法结果是得到了,num[]元素分别为1,7,3,2,1,1,1,2,1,1,但是时间花费非常久,处理器2.70Ghz的环境下,统计了10次平均时间179s。

2、逐次修改的方法。试想num[]赋了初值的数组,与统计数组com[]比较,比较到不同的元素就把num[]中不匹配的元素修改成正确的,比如统计num[]中数字1出现了5次,而num[1]=6,这时就修改num[1]=5,反复改过程,直到所有元素都正确。

 下面贴出第一版的代码(会内存溢出):
 

static int num[];//存储数字的数组
	static int wrongIndex;//检测到错误的index
	static int rightNum;//错误index对应的正确数字

	public int[] searchArray(){
		if(rightNum!=0){//只有第一次运行时,rNum==0,故第一次不改变num的值
		  num[wrongIndex] = rightNum;
		}
		boolean result = countArrayAndCom(num);
		if(!result){
			searchArray();		
		}
		return num;
	}
	
	 //统计int数组中数字的个数,并判断num数组是否匹配,
	 //匹配则返回true,否则返回false
		public boolean countArrayAndCom(int num[]){
			//创建长度为10的数组,用于存放统计结果
			int[] com = new int[10];
			//赋给数组初值1
			for(int i=0;i<com.length;i++){
				com[i]=1;
			}
			for(int i=0;i<num.length;i++){
			 //num[i]分成百位,十位,个位再统计个数,如101→1,0,1
			 int[] digit = getSingleDigit(num[i]);
			 if(null!=digit){
			  for(int j=0;j<digit.length;j++){
			   switch(digit[j]){
				case 0:com[0]++;break;
				case 1:com[1]++;break;
				case 2:com[2]++;break;
				case 3:com[3]++;break;
				case 4:com[4]++;break;
				case 5:com[5]++;break;
				case 6:com[6]++;break;
				case 7:com[7]++;break;
				case 8:com[8]++;break;
				case 9:com[9]++;break;		
				default:System.out.println("Error");break;
				}//switch
			  }//for digit
			 }//if digit
			}
			//比较num和com数组						
			return compareArray(num,com);
		}
	
		//比较两个数组是否相同
		public boolean compareArray(int[] num,int[] com){
		     if(num.length!=com.length)
		    	 return false;	   
			 for(int i=0;i<num.length;i++){
				    if(num[i]!=com[i]){
			    	 wrongIndex=i;
			    	 rightNum=com[i];
			    	 return false; 
			        }//if num[i]!=com[i]
			        if(num[num.length-i-1]!=com[num.length-i-1]){	 
			    	  wrongIndex = num.length-i-1;
			    	  rightNum = com[num.length-i-1];
			    	  return false; 
			        }//if num.length-1
			 }//for num.length			 
              return true;
		}
		
		//123→int[1][2][3]
		public int[] getSingleDigit(int num){
			if(num>=0&&num<=9){
				int[] result = new int[1];
				result[0]=num;
				return result;
			}else if(num>9){
				String strDigit = num+"";
				int digit = strDigit.length();
				int[] result = new int[digit];
				for(int i=0;i<result.length;i++){
					result[i]= Integer.parseInt(strDigit.substring(i, i+1));
				}	
				return result;
			}else{
				System.out.println("Error!");
			}		
			return null;
		}
 需要说明的几个问题,countArrayAndCom(int[] num)方法调用compareArray(int[] num,int[] com)和getSingleDigit(int num)方法,检测num数组是否满足要求,getSingleDigit(int num)解决多位数问题,比如num[1]=11,那么就返回长度为2的int数组,两个元素都是1,然后再统计,就能统计2个1。

关键的方法是searchArray(),修改num数组中错误的元素,然后检测修改后的num数组是否满足要求,再递归调用,直到出现满足要求的num[]数组。

然后用main函数调用

	public static void main(String[] args){	
		num = new int[10];
		//赋给初值
		for(int i=0;i<num.length;i++)
			num[i]=1;		
 
		long start = System.currentTimeMillis();
		CountNumII cn = new CountNumII();
		num = cn.searchArray();
		long end = System.currentTimeMillis();
		 System.out.println("用时:"+(end-start)+"毫秒");

		if(null!=num){
			 for(int i=0;i<num.length;i++)
				System.out.println("  num[i]是:"+num[i]);		   
		}else{System.out.println("没有结果");	}
		
	}

这样的方法看似还不错,但是运行之后报错,得不到结果,内存溢出,抛出异常如下:

Exception in thread "main" java.lang.StackOverflowError

打印num数组的变化过程,发现searchArray()方法陷入了死循环,递归怎么都跳不出来。num数组一直循环{1,12,1,1,1,1,1,1,1,1}→{1,11,1,1,1,1,1,1,1,1}→{1,12,1,1,1,1,1,1,1,1}。

看着上述死循环,突然发现如果{1,11,1,1,1,1,1,1,1}从num[9]→num[0]的顺序寻找不匹配,就会把num[2]的1换成2,而不是把num[1]的11换成12,即出现{1,11,2,1,1,1,1,1,1,1}。这不正是满足要求的一组数吗?所以就考虑比对num和com数组时,不要每次都从num[0]→num[9]的顺序。

首先想的是num[0]→num[9]和num[9]→num[0]依次比较,但仍然是死循环,只是循环的组合成员变多了(具体情况和不详细讲了,与上述情况类似)。

进一步就想到比较顺序随机选定,代码只修改了compareArray(int[] num,int[] com)方法,

//比较两个数组是否相同
		public boolean compareArray(int[] num,int[] com){
		     if(num.length!=com.length)
		    	 return false;	   
			 int randomflag = (new Random()).nextInt(2);
			 for(int i=0;i<num.length;i++){
			    if(randomflag%2==0){
				    if(num[i]!=com[i]){
			    	 wrongIndex=i;
			    	 rightNum=com[i];
			    	 return false; 
			        }//if num[i]!=com[i]
			    }else{
			        if(num[num.length-i-1]!=com[num.length-i-1]){	 
			    	  wrongIndex = num.length-i-1;
			    	  rightNum = com[num.length-i-1];
			    	  return false; 
			        }//if num.length-1
			    }//if else random%2
			 }//for num.length			 
              return true;
		}

 虽然if,for语句有点多,影响阅读,仔细看还是挺清晰的。经此修改后,再运行主函数,就能得到结果,num数组是{1,7,3,2,1,1,1,2,1,1},满足要求。但用时经常是0毫秒,偶尔1毫秒,是毫秒不是秒。

 速度如此之快还有一种可能就是,赋给num初值全部是1,离结果比较近。那么给num赋初值全部是9,计算时间就稳定到了1毫秒(初值为1时,运行时间经常为0毫秒,偶尔是1毫秒,上图是截取1毫秒的情况)。就效率来说,相比穷举法,有了极大幅度的提高。

但是还有一个没解决的问题,前文提到组合{1,11,2,1,1,1,1,1,1}也是一组解,就说明存在多解。用上述方法总是收敛到{1,7,3,2,1,1,1,2,1,1}。mai函数中给num随机赋初值也是同样的情况。所以怎样得到更完整的解,还需要继续思考。

思考这个问题只是兴趣使然,觉得有点意思再加上坐车比较无聊就拿来想了一番,没有探究算法是否有意义,我想并不是所有的事情都该去深究其意义何在,感兴趣就想一想,仅此而已。




 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值