簡體   English   中英

如何改進此代碼?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM