今日の面接でこんな質問がありました。
正の整数 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