One JavaSE interview question - the sum of reciprocals is 1

I had a question in the interview today:

If there are positive integers a, b, c, d, let 1/a + 1/b + 1/c + 1/d = 1, and a <= b <= c <= d, find a, b , possible combinations of c, d.

analyze:

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, and obviously a>=2, that is, 2<=a<=4.

2. Discuss by situation:

    If a=4, given the constraints of a <= b <= c <= d, obviously has only one Case a=b=c=d=4;

    If a=3, the remaining can be allocated 1-1/a=2/3, then 1/b>=(1-1/a)/3, that is, 3=a<=b<=9/2, that is, b =3 or 4;

                If b=3, the remainder can be distributed as 1-1/a-1/b=1/3. At this time, 1/c>=(1-1/a-1/b)/2, that is, b<=c<= 6

                        If c=3,1/a + 1/b + 1/c=1, it is not true;

                        If c=4, there is a valid sequence [3, 3, 4, 12];

                        If c=5, d=2/15, it is not true;

                         If c=6, there is a established sequence [3, 3, 6, 6];

    The analysis of other situations is the same as above.

3. Problem:There is a precision problem in Java floating point number operations. Convert to int type judgment condition. (I didn’t consider it during the written test.)

    i. The final judgment condition: 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. Minimum value: a<=b<=c<=d

    iii. Maximum value: Constraints (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. Constraints on the remaining unallocated amount (>=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. The correct version of the writing method in the written test: 4-layer for loop (floating point calculations were not considered in the written test, cool...)

	/**
	 * 若有 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;
	}

Writing this way is a bit too twisted, not very efficient, and it limits 4 parameters and cannot be changed.

After the interview, I went home and changed the code again, changing it to recursive form.

	/**
	 * 递归-倒数和为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);
			}
		}		
	}

Helper functions:

	/**
	 * 计算类似多项式
	 * 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;
	}

Test the running speed:

	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)+"纳秒");
	}

Post the online practice of using a while loop (specify the upper limit, loop one by one) to compare the speed - this upper limit cannot actually be specified and needs to be calculated.

	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;
	}

operation result:

[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纳秒

Although the four-layer loop is a bit cumbersome, the efficiency is actually pretty good. The fastest is of course recursion. The slowest one is the seemingly simple sequential while loop, which is not at the same level as the first two, and specifying the upper limit in this way cannot ensure that the answer obtained is complete.

Then post an enumeration that recursively calculates the sum of 5 reciprocals:

[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种

This cannot be done by specifying an upper limit.

递归-倒数之和:3089125纳秒


Reference: https://zhidao.baidu.com/question/243707012608764404.html


Guess you like

Origin blog.csdn.net/ever_who/article/details/80636074