簡體   English   中英

艱難的遞歸任務

[英]Tough recursive task

作為測試准備的一部分,我一直在努力解決問題,我想我可以使用你的幫助。 我需要編寫一個布爾方法,它接受帶有整數的數組(正數和負數),如果數組可以拆分為兩個等於組,則返回true,即每個組的數量等於另一個數組。 例如,對於這個數組:

int[]arr = {-3, 5, 12, 14, -9, 13};

該方法將返回true,因為-3 + 5 + 14 = 12 + -9 + 13。

對於這個數組:

int[]arr = {-3, 5, -12, 14, -9, 13};

該方法將返回false,因為即使-3 + 5 + 14 + -12 = -9 + 13,等式的每一側的數字量也不等於。

對於數組:

int[]arr = {-3, 5, -12, 14, -9};

該方法將返回false,因為數組長度不均勻。

該方法必須是遞歸的,允許重載,每個輔助方法也必須遞歸,我不需要擔心復雜性。

我一直試圖解決這個問題三個小時,我甚至都沒有顯示代碼,因為我所做的所有事情都遠非解決方案。

如果有人至少可以給我一些偽代碼,那就太棒了。

非常感謝你!

描述的問題是分區問題的一個版本。 首先請注意,您的公式相當於決定是否存在輸入的子集,其總和高達所有元素總和的一半(需要為整數,否則實例無法求解,但這很容易校驗)。 基本上,在每個遞歸步驟中,要確定是否要將第一個數字選擇到子集中,從而導致不同的遞歸調用。 如果n表示元素的數量,則必須有n/2 (需要再次積分)項目。

Sum表示輸入的總和,並且令Target := Sum / 2 ,其在后續中被假定為積分。 如果我們讓

f(arr,a,count) := true
                  if there is a subset of arr summing up to a with
                  exactly count elements
                  false
                  otherwise

我們獲得以下遞歸

f(arr,a,count) = (arr[0] == a && count == 1)
                 ||
                 (a == 0 && count == 0)
                 if arr contains only one element
                 f(arr\arr[0], a, count)
                 ||
                 f(arr\arr[0], a - arr[0], count -1)
                 if arr contains more than one element

在哪里|| 表示邏輯消除, &&表示邏輯連接, \\表示元素的刪除。 非單例數組的兩種情況對應於將arr的第一個元素選擇為所需子集或其相對補碼。 請注意,在實際實施中, a不會真正從數組中刪除; 一個起始索引(用作附加參數)將初始化為0並在每次遞歸調用中增加,最終到達數組的末尾。

最后, f(arr,Target,n/2)產生所需的值。

您要求使用偽代碼,但有時將其編寫為Java也同樣簡單明了。

該解決方案的一般思想是嘗試將每個數字添加到等式的左側或右側。 它跟蹤遞歸中每一步的每一側的計數和總和。 評論中有更多解釋:

class Balance {
  public static void main(String[] args) {
    System.out.println(balanced(-3, 5, 12, 14, -9, 13));   // true
    System.out.println(balanced(-3, 5, -12, 14, -9, 13));  // false
  }

  private static boolean balanced(int... nums) {
    // First check if there are an even number of nums.
    return nums.length % 2 == 0
        // Now start the recursion:
        && balanced(
            0, 0,  // Zero numbers on the left, summing to zero.
            0, 0,  // Zero numbers on the right, summing to zero.
            nums);
  }

  private static boolean balanced(
      int leftCount, int leftSum,
      int rightCount, int rightSum,
      int[] nums) {
    int idx = leftCount + rightCount;
    if (idx == nums.length) {
      // We have attributed all numbers to either side of the equation.
      // Now check if there are an equal number and equal sum on the two sides.
      return leftCount == rightCount && leftSum == rightSum;
    } else {
      // We still have numbers to allocate to one side or the other.
      return
          // What if I were to place nums[idx] on the left of the equation?
          balanced(
              leftCount + 1, leftSum + nums[idx],
              rightCount, rightSum,
              nums)
          // What if I were to place nums[idx] on the right of the equation?
          || balanced(
              leftCount, leftSum,
              rightCount + 1, rightSum + nums[idx],
              nums);
    }
  }
}

這只是第一個想法的解決方案。 它是O(2 ^ n),對於大n來說顯然相當慢,但是對於你給出的問題的大小來說這很好。

您的策略應該是嘗試所有可能的組合。 我將嘗試記錄如何實現這一目標。

注意我認為要求:讓每個函數都使用遞歸有點困難,因為我會通過省略一些幫助函數來解決這個問題,這些函數使得代碼更具可讀性,所以在這種情況下我不會這樣做。

通過遞歸,您總是希望向最終解決方案發展,並檢測您何時完成。 所以我們在函數中需要兩個部分:

  1. 遞歸步驟:為此我們將獲取輸入集的第一個元素,並嘗試如果我們將它添加到第一個集合會發生什么,如果找不到解決方案,我們將嘗試將它添加到第二集。
  2. 檢測我們何時完成,即輸入集為空時,在這種情況下,我們要么找到了解決方案,要么我們沒有找到解決方案。

我們的第一步中的一個技巧是,在獲取集合的第一個元素之后,如果我們嘗試對余數進行分區,我們不希望這兩個集合相等,因為我們已經將第一個元素分配給其中一個集合。

這導致了遵循此策略的解決方案:

public boolean isValidSet(MySet<int> inputSet, int sizeDifferenceSet1minus2)
{
    if (inputSet.isEmpty())
    {
         return sizeDifferenceSet1minus2== 0;
    }

    int first = inptuSet.takeFirst();
    return isValidSet(inputSet.copyMinusFirst(), sizeDifferenceSet1minus2+ first)
              || isValidSet(inputSet.copyMinusFirst(), sizeDifferenceSet1minus2+ -1 * first);
}

此代碼需要一些您仍需要實現的幫助功能。

它的作用是首先測試我們是否已達到結束條件,如果是,則返回此分區是否成功。 如果我們仍然在集合中留下了元素,我們會嘗試如果將它添加到第一個集合中會發生什么,然后將其添加到第二個集合時會發生什么。 請注意,我們實際上並沒有跟蹤集合,我們只是跟蹤集合1減去2之間的大小差異,減少(但您可以傳遞兩個集合)。

另請注意,要使此實現起作用,您需要復制輸入集而不是修改它!

對於一些背景信息:這個問題被稱為分區問題 ,它以NP完全着稱(這意味着它可能無法有效地解決大量輸入數據,但很容易驗證分區確實是一個解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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