JavaSE 面接の質問 1 つ - 逆数の合計は 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 の制約を考慮すると、 には明らかに 1 つしかありませんケース 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;

                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 は true ではありません。

                        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/span>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 ループ (上限を指定して 1 つずつループ) を使用して速度を比較する実践をオンラインで投稿します。この上限は実際には指定できないため、計算する必要があります。

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

4 層ループは少し面倒ですが、効率はかなり良いです。もちろん最も速いのは再帰です。最も遅いループは、一見単純なシーケンシャル while ループですが、最初の 2 つと同じレベルではなく、この方法で上限を指定しても、完全な答えが得られるとは限りません。

次に、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