JavaSE面试题一则-倒数之和为1

今天面试了一道题:

若有正整数a、b、c、d, 使 1/a + 1/b + 1/c + 1/d = 1, 且a <= b <= c <= d, 求a, b, c, d的可能组合.

分析:

1.     ∵ a <= b <= c <= d

        ∴ 1/d<=1/c<=1/b<=1/a

        ∵ 1/a + 1/b + 1/c + 1/d = 1

        ∴  1=1/a + 1/b + 1/c + 1/d<=4*1/a

        ∴  a<=4,且显然有a>=2,即2<=a<=4.

2.    分情况讨论:

    若a=4,由a <= b <= c <= d的约束条件,显然只有一种情况 a=b=c=d=4;

    若a=3,剩余可分配1-1/a=2/3,此时1/b>=(1-1/a)/3,即3=a<=b<=9/2,即b=3或4;

扫描二维码关注公众号,回复: 17187041 查看本文章

                若b=3,剩余可分配1-1/a-1/b=1/3,此时1/c>=(1-1/a-1/b)/2,即 b<=c<=6

                        若c=3,1/a + 1/b + 1/c=1,不成立;

                        若c=4, 有成立序列[3, 3, 4, 12];

                        若c=5, d=2/15,不成立;

                         若c=6, 有成立序列[3, 3, 6, 6];

    其余情况的分析同上。

3.问题:java浮点数运算存在精度问题。转为int型的判断条件。(笔试的时候没考虑。。)

    i.最后的判断条件:1/a + 1/b + 1/c + 1/d=1   <==> a*b*c*d=b*c*d+a*c*d+a*b*d+a*b*c

    ii.最小值:a<=b<=c<=d

    iii.最大值:约束条件 (1).1/b>=(1-1/a)/3     <==>     b(a-1)<=3a

                                    (2).1/c>=(1-1/a-1/b)/2     <==>     c(ab-a-b)<=2ab

                                    (3).1/d>=(1-1/a-1/b-1/c)/1     <==>     d(abc-ab-ac-bc)<=abc

    iv.剩余未分配量的约束条件(>=0):  (1).1-1/a>0     <==>      a-1>0

                                                         (2).1-1/a-1/b>0     <==>     ab-a-b>0

                                                         (3).1-1/a-1/b-1/c>0     <==>     abc-ab-ac-bc>0

4.笔试时写法的正确版:4层for循环(笔试时未考虑浮点数运算的问题,凉凉。。)

	/**
	 * 若有 1/a+1/b+1/c+1/d=1.且a<=b<=c<=d,求a/b/c/d的可能组合
	 * @return
	 */
	public List<int[]> getPlusOne(){
		List<int[]> list=new ArrayList<>();
		for(int a=2;a<=3;a++) {                    //因为已经知道a=4时,就一种情况,单独拎出来处理就行了。
			for(int b=a;b*(a-1)<=3*a;b++) {
				int btemp=a*b-a-b;
				if(btemp<=0) continue;
				for(int c=b;c*btemp<=2*a*b;c++) {
					int ctemp=a*b*c-a*b-b*c-a*c;
					if(ctemp<=0) continue;
					for(int d=c;d*ctemp<=a*b*c;d++) {
						if(a*b*c*d==b*c*d+a*c*d+a*b*d+a*b*c) {							
							int[] num=new int[4];
							num[0]=a;
							num[1]=b;
							num[2]=c;
							num[3]=d;
							list.add(num);
						}
					}
				}
			}
		}
		list.add(new int[] {4,4,4,4});
		return list;
	}

这样写有点太扭曲了,效率也不高,而且限定了4个参数,不能改变。

面试完后,回家重新改了下代码,改成递归形式。

	/**
	 * 递归-倒数和为1
	 * @param n	等式左侧有n项
	 * @return 存储符合等式条件的序列集合
	 */
	public List<int[]> getPlust(int n) {
		int[] m=new int[n];
		List<int[]> list=new ArrayList<>();
		getPlusOne2(m,0,2,n,1,list);
		for(int i=0;i<m.length;i++) {    //单独把各元素=n的情况拿出来,最后add,减少循环次数
			m[i]=n;
		}
		list.add(m);
		return list;
	}
	/**
	 * 递归-倒数和为1
	 * @param m 存放序列的数组
	 * @param n 初始为0,用于控制递归深度
	 * @param min 序列元素的下限,初始为2
	 * @param temp1	初始值为n,用于控制序列元素上限的条件
	 * @param temp2	用于存储临时变量-序列元素的连乘 如:a*b*c
	 * @param list 用于存储不同序列
	 */
	public void getPlusOne2(int[] m,int n,int min,int temp1,int temp2, List<int[]> list){
		if(n==m.length) {                    //递归出口:n初始为0,记录递归深度,当n==m.length表示序列元素已取完
			if(temp2==arrayPoly(m)) {    //判断式等价于 1/a+1/b+1/c+1/d=1 类似等式
				int[] k=m.clone();    //由于整个函数共用一个int[] m,需要以clone的形式复制后add;
				list.add(k);            //若直接add(m),list中所有元素都指向m,将导致list中所有元素变为同一值
			}
			return;
		}else if(n==0) {
			for(int i=min;i<=temp1-1;i++) {    //这里把n==0,递归入口的上下限控制与后面的分开处理
				m[n]=i;                    //这里不需要判断剩余值是否<=0,且temp1初始重赋为a-1
				getPlusOne2(m,n+1,i,i-1,i,list);
			}
		}else {
			min=min>n+1?min:n+1;  //这里有一个判断:第n个序列元素值必然>=n,如第3个元素c>=3,可以过滤如b=2,c=2的判断
			for(int i=min;i*temp1<=(m.length-n)*temp2;i++) { //这里和上面循环的方式一样
				m[n]=i;
				int t=i*temp1-temp2;
				if(n!=m.length-1) {    //当n=m.length-1,即n个元素取完时,需要放t==0的序列到上面递归出口判断
					if(t<=0) continue;
				}else {
					if(t<0) continue;
				}
				getPlusOne2(m,n+1,i,t,temp2*i,list);
			}
		}		
	}

辅助函数:

	/**
	 * 计算类似多项式
	 * m[0]*m[1]*m[2]+m[0]*m[2]*m[3]+m[1]*m[2]*m[3]+m[0]*m[1]*m[3]
	 * @param m
	 * @return
	 */
	public int arrayPoly(int[] m) {
		int sum=0;
		for(int i=0;i<m.length;i++) {
			int product=1;
			for(int j=0;j<m.length;j++) {
				if(i==j) continue;
				product*=m[j];
			}
			sum+=product;
		}
		return sum;
	}

测试一下运行速度:

	public static void main(String[] args) {
		PrimeNum primeNum = new PrimeNum();
		long t2=System.nanoTime();
		List<int[]> list=primeNum.getPlusOne();
		for(int[] n1:list) {
			System.out.println(Arrays.toString(n1));
		}
		System.out.println("总共:"+list.size()+"种");
		long t3=System.nanoTime();
		List<int[]> list2=primeNum.getPlust(4);
		for(int[] n1:list2) {
			System.out.println(Arrays.toString(n1));
		}
		System.out.println("总共:"+list2.size()+"种");
		long t4=System.nanoTime();
		System.out.println("while循环:");
		List<int[]> list3=primeNum.whateverTheNameIs(42);
		for(int[] n1:list3) {
			System.out.println(Arrays.toString(n1));
		}
		long t5=System.nanoTime();
		System.out.println("四层循环-倒数之和:"+(t3-t2)+"纳秒");
		System.out.println("递归-倒数之和:"+(t4-t3)+"纳秒");
		System.out.println("while循环-倒数之和:"+(t5-t4)+"纳秒");
	}

贴一下网上的做法while循环(指定上限,逐个循环)用于对比速度--这个上限事实上无法指定,需要计算出来

	public List<int[]> whateverTheNameIs(int max){
		// 视edge1为小值域 如果不符合则调换
		List<int[]> list = new ArrayList<>();
		int min =1;
		int a, b, c, d;
		a = b = c = d = min;
		while (a <= max) {
			if (a * b * c * d == a * b * c + a * b * d + a * c * d + b * c * d) {
				int[] num=new int[4];
				num[0]=a;
				num[1]=b;
				num[2]=c;
				num[3]=d;
				list.add(num);
			}
			d++;
			if (d > max) {
				c++;
				d = c;
			}
			if (c > max) {
				b++;
				d = c = b;
			}
			if (b > max) {
				a++;
				d = c = b = a;
			}
		}
		return list;
	}

运行结果:

[2, 3, 7, 42]
[2, 3, 8, 24]
[2, 3, 9, 18]
[2, 3, 10, 15]
[2, 3, 12, 12]
[2, 4, 5, 20]
[2, 4, 6, 12]
[2, 4, 8, 8]
[2, 5, 5, 10]
[2, 6, 6, 6]
[3, 3, 4, 12]
[3, 3, 6, 6]
[3, 4, 4, 6]
[4, 4, 4, 4]
总共:14种
[2, 3, 7, 42]
[2, 3, 8, 24]
[2, 3, 9, 18]
[2, 3, 10, 15]
[2, 3, 12, 12]
[2, 4, 5, 20]
[2, 4, 6, 12]
[2, 4, 8, 8]
[2, 5, 5, 10]
[2, 6, 6, 6]
[3, 3, 4, 12]
[3, 3, 6, 6]
[3, 4, 4, 6]
[4, 4, 4, 4]
总共:14种
while循环:
[2, 3, 7, 42]
[2, 3, 8, 24]
[2, 3, 9, 18]
[2, 3, 10, 15]
[2, 3, 12, 12]
[2, 4, 5, 20]
[2, 4, 6, 12]
[2, 4, 8, 8]
[2, 5, 5, 10]
[2, 6, 6, 6]
[3, 3, 4, 12]
[3, 3, 6, 6]
[3, 4, 4, 6]
[4, 4, 4, 4]
四层循环-倒数之和:560376纳秒
递归-倒数之和:294460纳秒
while循环-倒数之和:3330401纳秒

四层循环虽然有点繁琐,效率其实也还可以。最快的当然是递归。最慢的是形式看似简洁的逐次while循环,和前两个不是一个等级,而且这样指定上限不能确保获得的答案完整。

再贴个递归计算5项倒数之和的枚举:

[2, 3, 7, 43, 1806]
[2, 3, 7, 44, 924]
[2, 3, 7, 45, 630]
[2, 3, 7, 46, 483]
[2, 3, 7, 48, 336]
[2, 3, 7, 49, 294]
[2, 3, 7, 51, 238]
[2, 3, 7, 54, 189]
[2, 3, 7, 56, 168]
[2, 3, 7, 60, 140]
[2, 3, 7, 63, 126]
[2, 3, 7, 70, 105]
[2, 3, 7, 78, 91]
[2, 3, 7, 84, 84]
[2, 3, 8, 25, 600]
[2, 3, 8, 26, 312]
[2, 3, 8, 27, 216]
[2, 3, 8, 28, 168]
[2, 3, 8, 30, 120]
[2, 3, 8, 32, 96]
[2, 3, 8, 33, 88]
[2, 3, 8, 36, 72]
[2, 3, 8, 40, 60]
[2, 3, 8, 42, 56]
[2, 3, 8, 48, 48]
[2, 3, 9, 19, 342]
[2, 3, 9, 20, 180]
[2, 3, 9, 21, 126]
[2, 3, 9, 22, 99]
[2, 3, 9, 24, 72]
[2, 3, 9, 27, 54]
[2, 3, 9, 30, 45]
[2, 3, 9, 36, 36]
[2, 3, 10, 16, 240]
[2, 3, 10, 18, 90]
[2, 3, 10, 20, 60]
[2, 3, 10, 24, 40]
[2, 3, 10, 30, 30]
[2, 3, 11, 14, 231]
[2, 3, 11, 15, 110]
[2, 3, 11, 22, 33]
[2, 3, 12, 13, 156]
[2, 3, 12, 14, 84]
[2, 3, 12, 15, 60]
[2, 3, 12, 16, 48]
[2, 3, 12, 18, 36]
[2, 3, 12, 20, 30]
[2, 3, 12, 21, 28]
[2, 3, 12, 24, 24]
[2, 3, 13, 13, 78]
[2, 3, 14, 14, 42]
[2, 3, 14, 15, 35]
[2, 3, 14, 21, 21]
[2, 3, 15, 15, 30]
[2, 3, 15, 20, 20]
[2, 3, 16, 16, 24]
[2, 3, 18, 18, 18]
[2, 4, 5, 21, 420]
[2, 4, 5, 22, 220]
[2, 4, 5, 24, 120]
[2, 4, 5, 25, 100]
[2, 4, 5, 28, 70]
[2, 4, 5, 30, 60]
[2, 4, 5, 36, 45]
[2, 4, 5, 40, 40]
[2, 4, 6, 13, 156]
[2, 4, 6, 14, 84]
[2, 4, 6, 15, 60]
[2, 4, 6, 16, 48]
[2, 4, 6, 18, 36]
[2, 4, 6, 20, 30]
[2, 4, 6, 21, 28]
[2, 4, 6, 24, 24]
[2, 4, 7, 10, 140]
[2, 4, 7, 12, 42]
[2, 4, 7, 14, 28]
[2, 4, 8, 9, 72]
[2, 4, 8, 10, 40]
[2, 4, 8, 12, 24]
[2, 4, 8, 16, 16]
[2, 4, 9, 9, 36]
[2, 4, 9, 12, 18]
[2, 4, 10, 10, 20]
[2, 4, 10, 12, 15]
[2, 4, 12, 12, 12]
[2, 5, 5, 11, 110]
[2, 5, 5, 12, 60]
[2, 5, 5, 14, 35]
[2, 5, 5, 15, 30]
[2, 5, 5, 20, 20]
[2, 5, 6, 8, 120]
[2, 5, 6, 9, 45]
[2, 5, 6, 10, 30]
[2, 5, 6, 12, 20]
[2, 5, 6, 15, 15]
[2, 5, 7, 7, 70]
[2, 5, 8, 8, 20]
[2, 5, 10, 10, 10]
[2, 6, 6, 7, 42]
[2, 6, 6, 8, 24]
[2, 6, 6, 9, 18]
[2, 6, 6, 10, 15]
[2, 6, 6, 12, 12]
[2, 6, 7, 7, 21]
[2, 6, 8, 8, 12]
[2, 6, 9, 9, 9]
[2, 7, 7, 7, 14]
[2, 8, 8, 8, 8]
[3, 3, 4, 13, 156]
[3, 3, 4, 14, 84]
[3, 3, 4, 15, 60]
[3, 3, 4, 16, 48]
[3, 3, 4, 18, 36]
[3, 3, 4, 20, 30]
[3, 3, 4, 21, 28]
[3, 3, 4, 24, 24]
[3, 3, 5, 8, 120]
[3, 3, 5, 9, 45]
[3, 3, 5, 10, 30]
[3, 3, 5, 12, 20]
[3, 3, 5, 15, 15]
[3, 3, 6, 7, 42]
[3, 3, 6, 8, 24]
[3, 3, 6, 9, 18]
[3, 3, 6, 10, 15]
[3, 3, 6, 12, 12]
[3, 3, 7, 7, 21]
[3, 3, 8, 8, 12]
[3, 3, 9, 9, 9]
[3, 4, 4, 7, 42]
[3, 4, 4, 8, 24]
[3, 4, 4, 9, 18]
[3, 4, 4, 10, 15]
[3, 4, 4, 12, 12]
[3, 4, 5, 5, 60]
[3, 4, 5, 6, 20]
[3, 4, 6, 6, 12]
[3, 4, 6, 8, 8]
[3, 5, 5, 5, 15]
[3, 5, 5, 6, 10]
[3, 6, 6, 6, 6]
[4, 4, 4, 5, 20]
[4, 4, 4, 6, 12]
[4, 4, 4, 8, 8]
[4, 4, 5, 5, 10]
[4, 4, 6, 6, 6]
[5, 5, 5, 5, 5]
总共:147种

这种完全不能靠指定上限。

递归-倒数之和:3089125纳秒


参考:https://zhidao.baidu.com/question/243707012608764404.html


猜你喜欢

转载自blog.csdn.net/ever_who/article/details/80636074