[英]how to improve this code?
我已經開發了一個代碼來表示2的冪,並且在下面附加了相同的代碼。
但是問題在於表達的輸出應該具有最小長度。
我得到的輸出為3^2+1^2+1^2+1^2
,這不是最小長度。 我需要以這種格式輸出:
package com.algo;
import java.util.Scanner;
public class GetInputFromUser {
public static void main(String[] args) {
// TODO Auto-generated method stub
int n;
Scanner in = new Scanner(System.in);
System.out.println("Enter an integer");
n = in.nextInt();
System.out.println("The result is:");
algofunction(n);
}
public static int algofunction(int n1)
{
int r1 = 0;
int r2 = 0;
int r3 = 0;
//System.out.println("n1: "+n1);
r1 = (int) Math.sqrt(n1);
r2 = (int) Math.pow(r1, 2);
// System.out.println("r1: "+r1);
//System.out.println("r2: "+r2);
System.out.print(r1+"^2");
r3 = n1-r2;
//System.out.println("r3: "+r3);
if (r3 == 0)
return 1;
if(r3 == 1)
{
System.out.print("+1^2");
return 1;
}
else {
System.out.print("+");
algofunction(r3);
return 1;
}
}
}
動態編程就是以這樣一種方式來定義問題:如果您知道答案較小的原始版本的答案,則可以使用它來更快/更直接地回答主要問題。 就像應用數學歸納法一樣。
在您的特定問題中,我們可以將MinLen(n)定義為n的最小長度表示。 接下來,假設我們要求解MinLen(12),假設我們已經知道MinLen(1),MinLen(2),MinLen(3),...,MinLen(11)的答案。 我們如何使用這些較小問題的答案來找出MinLen(12)? 這是動態編程的另一半-弄清楚如何使用較小的問題來解決較大的問題。 如果您遇到一些較小的問題,這無濟於事,但無法將它們重新組合在一起。
對於這個問題,我們可以簡單地聲明:“對於12,它的最小長度表示形式肯定有1 ^ 2、2 ^ 2或3 ^ 2。” 通常,n的最小長度表示形式將包含小於或等於n的某個平方。 您可能會做出更好的聲明,這將改善運行時間,但我會說這已經足夠了。
該語句意味着MinLen(12)= 1 ^ 2 + MinLen(11),或2 ^ 2 + MinLen(8),OR 3 ^ 2 + MinLen(3)。 您選中所有這些,然后選擇最佳的,現在將其另存為MinLen(12)。 現在,如果您想求解MinLen(13),也可以這樣做。
獨奏時的建議:我自己測試這種程序的方法是插入1,2,3,4,5等,並在第一次出現錯誤時查看。 另外,我碰巧想到的任何假設都是一個好主意,我問:“真的小於n的最大平方數是MinLen(n)的表示嗎?”
您的代碼:
r1 = (int) Math.sqrt(n1);
r2 = (int) Math.pow(r1, 2);
體現了這個假設(貪婪的假設),但這是錯誤的,因為您已經清楚地看到了MinLen(12)的答案。
相反,您想要更多類似這樣的東西:
public ArrayList<Integer> minLen(int n)
{
// base case of recursion
if (n == 0)
return new ArrayList<Integer>();
ArrayList<Integer> best = null;
int bestInt = -1;
for (int i = 1; i*i <= n; ++i)
{
// Check what happens if we use i^2 as part of our representation
ArrayList<Integer> guess = minLen(n - i*i);
// If we haven't selected a 'best' yet (best == null)
// or if our new guess is better than the current choice (guess.size() < best.size())
// update our choice of best
if (best == null || guess.size() < best.size())
{
best = guess;
bestInt = i;
}
}
best.add(bestInt);
return best;
}
然后,一旦有了列表,就可以對其進行排序(不保證它以排序順序出現),並以所需的方式打印出來。
最后,您可能會注意到,對於較大的n值(1000可能太大),如果您插入上述遞歸,它將開始變得非常慢。 這是因為我們一直在重新計算所有小的子問題-例如,當我們調用MinLen(4)時,我們會計算出MinLen(3),因為4-1 ^ 2 =3。但是對於MinLen(7),我們會計算出兩次。 -> 3 = 7-2 ^ 2,但3也是7-1 ^ 2-1-2 ^ -1 -2-1 ^ 2。 而且,越大越糟。
解決此問題的方法是使用一種稱為Memoization的技術,它可以非常快地解決n = 1,000,000或更多的問題 。 這意味着一旦弄清楚了MinLen(3),我們便將其保存在某個地方,比方說它是一個易於使用的全局位置。 然后,每當我們嘗試重新計算它時,我們都會首先檢查全局緩存以查看是否已經執行過。 如果是這樣,那么我們只是使用它,而不是重做所有工作。
import java.util.*;
class SquareRepresentation
{
private static HashMap<Integer, ArrayList<Integer>> cachedSolutions;
public static void main(String[] args)
{
cachedSolutions = new HashMap<Integer, ArrayList<Integer>>();
for (int j = 100000; j < 100001; ++j)
{
ArrayList<Integer> answer = minLen(j);
Collections.sort(answer);
Collections.reverse(answer);
for (int i = 0; i < answer.size(); ++i)
{
if (i != 0)
System.out.printf("+");
System.out.printf("%d^2", answer.get(i));
}
System.out.println();
}
}
public static ArrayList<Integer> minLen(int n)
{
// base case of recursion
if (n == 0)
return new ArrayList<Integer>();
// new base case: problem already solved once before
if (cachedSolutions.containsKey(n))
{
// It is a bit tricky though, because we need to be careful!
// See how below that we are modifying the 'guess' array we get in?
// That means we would modify our previous solutions! No good!
// So here we need to return a copy
ArrayList<Integer> ans = cachedSolutions.get(n);
ArrayList<Integer> copy = new ArrayList<Integer>();
for (int i: ans) copy.add(i);
return copy;
}
ArrayList<Integer> best = null;
int bestInt = -1;
// THIS IS WRONG, can you figure out why it doesn't work?:
// for (int i = 1; i*i <= n; ++i)
for (int i = (int)Math.sqrt(n); i >= 1; --i)
{
// Check what happens if we use i^2 as part of our representation
ArrayList<Integer> guess = minLen(n - i*i);
// If we haven't selected a 'best' yet (best == null)
// or if our new guess is better than the current choice (guess.size() < best.size())
// update our choice of best
if (best == null || guess.size() < best.size())
{
best = guess;
bestInt = i;
}
}
best.add(bestInt);
// check... not needed unless you coded wrong
int sum = 0;
for (int i = 0; i < best.size(); ++i)
{
sum += best.get(i) * best.get(i);
}
if (sum != n)
{
throw new RuntimeException(String.format("n = %d, sum=%d, arr=%s\n", n, sum, best));
}
// New step: Save the solution to the global cache
cachedSolutions.put(n, best);
// Same deal as before... if you don't return a copy, you end up modifying your previous solutions
//
ArrayList<Integer> copy = new ArrayList<Integer>();
for (int i: best) copy.add(i);
return copy;
}
}
我的程序花了大約5秒鍾才能運行n = 100,000。 顯然,如果我們希望它更快並且要解決更大的n,還有很多事情要做。 現在的主要問題是,在存儲先前答案的整個結果列表時,我們會占用大量內存。 以及所有復制! 您可以做更多的事情,例如只存儲一個整數和一個指向子問題的指針,但是我會讓您做到這一點。
然后乘以1000 = 30 ^ 2 + 10 ^ 2。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.