簡體   English   中英

遞歸查找數組中偶數的平均值

[英]Recursively finding the average of even numbers in an array

我試圖使用遞歸找到數組中所有偶數平均值,但我陷入了困境。

我意識到對於每個奇數 n都必須遞減 n因此我用正確的值除以,但是我無法確定如何使用遞歸進行計算。

考慮到當我return時它將恢復原狀,因此我不了解如何跟蹤n

有什么方法可以讓我追蹤n ,或者我完全以錯誤的方式看待這個問題嗎?

編輯:我應該指定,我需要專門使用遞歸。 這是一項任務。

public static int getEvenAverage(int[] A, int i, int n)
{
    // first element
    if (i == 0)
        if (A[i] % 2 == 0)
            return A[0];
        else
            return 0;

    // last element
    if (i == n - 1)
    {
        if (A[i] % 2 == 0)
            return (A[i] + getEvenAverage(A, i - 1, n)) / n;
        else
            return (0 + getEvenAverage(A, i - 1, n)) / n;
    }

    if (A[i] % 2 == 0)
        return A[i] + getEvenAverage(A, i - 1, n);
    else
        return 0 + getEvenAverage(A, i - 1, n);
}

為了跟蹤到目前為止您所遇到的偶數,只需傳遞一個額外的參數即可。

此外,您還可以為偶數之和傳遞一個額外的參數,當您遇到基本情況時,您可以返回平均值,即偶數之和除以它們的計數。

還有一件事,您的代碼對於第一個和最后一個元素都有兩個基本用例,它們是不需要的。

  1. 您可以遞減n (從數組的大小開始,直到第一個元素),或者
  2. 您可以從0開始遞增i ,直到達到array的大小,即n

在這里,我嘗試過。

public static int getEvenAvg(int[] a, int n, int ct, int sum) {
    if (n == -1) {
        //make sure you handle the case
        //when count of even numbers is zero
        //otherwise you'll get Runtime Error.
        return sum/ct;
    }
    if (a[n]%2 == 0) {
        ct++;
        sum+=a[n];
    }
    return getEvenAvg(a, n - 1, ct, sum); 
}

您可以像這樣調用函數getEvenAvg(a, size_of_array - 1, 0, 0);

在處理遞歸操作時,從終止條件開始通常很有用。 那么我們這里的終止條件是什么?

沒有更多要處理的元素:

if (index >= a.length) {
    // To avoid divide-by-zero
    return count == 0 ? 0 : sum / count;
}

...好吧,現在我們如何減少要處理的元素數量? 我們應該增加索引嗎?

index++;

...哦,但是只有在進入下一個級別時:

getEvenAverage(elements, index++, sum, count);

好了,我們也不得不加入到sumcount ,對不對?

sum += a[index];
count++;

....除非僅當元素為偶數時:

if (a[index] % 2 == 0) {
    sum += a[index];
    count++;    
}    

...就是這樣:

static int getEvenAverage(int[] elements, int index, int sum, int count) {
    if (index >= a.length) {
        // To avoid divide-by-zero
        return count == 0 ? 0 : sum / count;
    }

    if (a[index] % 2 == 0) {
        sum += a[index];
        count++;    
    } 

    return getEvenAverage(elements, index + 1, sum, count);
}

...盡管您可能希望包裝器函數使其更漂亮:

static int getEvenAverage(int[] elements) {
    return getEvenAverage(elements, 0, 0, 0);
}

說明

這里的困難是您需要記住兩個值

  • 偶數的數量和
  • 偶數累加的總值。

並且您需要返回一個平均值的最終值。

這意味着您需要一次記住三個值,而只能返回一個元素。


大綱

對於干凈的設計,您需要某種容器來保存這些中間結果,例如,像這樣的類:

public class Results {
    public int totalValueOfEvens;
    public int amountOfEvens;

    public double getAverage() {
        return totalValueOfEvens + 0.0 / amountOfEvens;
    }
}

當然,您也可以使用帶有兩個條目的int[]類的東西。

之后,遞歸非常簡單。 您只需要遞歸遍歷數組,例如:

public void method(int[] values, int index) {
    // Abort if last element
    if (index == values.length - 1) {
        return;
    }

    method(array, index + 1);
}

同時,使用當前值更新容器


向后收集

向后收集時,您需要將所有信息存儲在返回值中

由於要記住許多事情,因此應使用容器作為返回類型( Results2 -entry int[] )。 然后簡單地走到最后,收集並返回。

它看起來像這樣:

public static Results getEvenAverage(int[] values, int curIndex) {
    // Traverse to the end
    if (curIndex != values.length - 1) {
        results = getEvenAverage(values, curIndex + 1);
    }

    // Update container
    int myValue = values[curIndex];

    // Whether this element contributes
    if (myValue % 2 == 0) {
        // Update the result container
        results.totalValueOfEvens += myValue;
        results.amountOfEvens++;
    }

    // Return accumulated results
    return results;
}

收集轉發

此方法的優點在於,調用者無需自己調用results.getAverage() 您將信息存儲在參數中,因此可以自由選擇返回類型。

我們獲取當前值並更新容器。 然后,我們調用下一個元素,並將當前容器傳遞給他。

調用最后一個元素后,保存在容器中的信息將是最終信息。 現在,我們只需要結束遞歸並返回第一個元素。 再次訪問第一個元素時,它將根據容器中的信息計算最終輸出並返回。

public static double getEvenAverage(int[] values, int curIndex, Results results) {
    // First element in recursion
    if (curIndex == 0) {
        // Setup the result container
        results = new Results();
    }

    int myValue = values[curIndex];

    // Whether this element contributes
    if (myValue % 2 == 0) {
        // Update the result container
        results.totalValueOfEvens += myValue;
        results.amountOfEvens++;
    }

    int returnValue = 0;
    // Not the last element in recursion
    if (curIndex != values.length - 1) {
        getEvenAverage(values, curIndex + 1, results);
    }

    // Return current intermediate average,
    // which is the correct result if current element
    // is the first of the recursion
    return results.getAverage();
}

最終用戶的使用

向后方法的用法如下:

Results results = getEvenAverage(values, 0);
double average results.getAverage();

而正向方法的用法如下:

double average = getEvenAverage(values, 0, null);

當然,您可以使用輔助方法將其隱藏給用戶:

public double computeEvenAverageBackward(int[] values) {
    return getEvenAverage(values, 0).getAverage();
}

public double computeEvenAverageForward(int[] values) {
    return getEvenAverage(values, 0, null);
}

然后,對於最終用戶,這只是此調用:

double average = computeEvenAverageBackward(values);

Java不是用於這種事情的好語言,但是我們開始:

public class EvenAverageCalculation {

    public static void main(String[] args) {
        int[] array = {1,2,3,4,5,6,7,8,9,10};
        System.out.println(getEvenAverage(array));
    }

    public static double getEvenAverage(int[] values) {
        return getEvenAverage(values, 0, 0);
    }

    private static double getEvenAverage(int[] values, double currentAverage, int nrEvenValues) {
        if (values.length == 0) {
            return currentAverage;
        }
        int head = values[0];
        int[] tail = new int[values.length - 1];
        System.arraycopy(values, 1, tail, 0, tail.length);
        if (head % 2 != 0) {
            return getEvenAverage(tail, currentAverage, nrEvenValues);
        }
        double newAverage = currentAverage * nrEvenValues + head;
        nrEvenValues++;
        newAverage = newAverage / nrEvenValues;
        return getEvenAverage(tail, newAverage, nrEvenValues);
    }
}

您將當前平均值和到目前為止的偶數元素傳遞給每個遞歸調用。 通過將平均值再次乘以到目前為止的元素數,再加上新的單個值,然后將其除以新的元素數,即可計算出新的平均值,然后再將其傳遞給下一個遞歸調用。

為每個遞歸調用重新創建新數組的方式對Java來說並不是很好。 還有其他語言具有用於拆分數組的頭部和尾部的語法,該語法還具有較小的內存占用空間(每個遞歸調用都會導致創建一個具有n-1個元素的新int數組)。 但是我實現的方式是函數式編程的經典方式(至少在1994年,我與編程語言Gofer進行了類似的分配時才學會它;-)

這是另一個變體,對平均值使用(中等)眾所周知的遞歸關系:

平均0 = 0

平均n =平均n-1 +(x n-平均n-1 )/ n

其中avg n是n 觀測值的平均值,x n是第n 觀測值。 這導致:

/*
 * a is the array of values to process
 * i is the current index under consideration
 * n is a counter which is incremented only if the current value gets used
 * avg is the running average
 */
private static double getEvenAverage(int[] a, int i, int n, double avg) {
   if (i >= a.length) {
      return avg;
   }
   if (a[i] % 2 == 0) {         // only do updates for even values
      avg += (a[i] - avg) / n;  // calculate delta and update the average
      n += 1;
   }
   return getEvenAverage(a, i + 1, n, avg);
 }

可以使用以下前端方法來調用該方法,以保護用戶無需了解參數初始化:

public static double getEvenAverage(int[] a) {
   return getEvenAverage(a, 0, 1, 0.0);
}

現在換一種完全不同的方法。

這其中借鑒了事實,如果你有兩個平均值,平均1基於N + 1組的意見和平均2基於N 2的觀察,你可以將它們結合起來,以產生一個匯集平均:

平均 =(n 1 * avg 1 + n 2 * avg 2 )/(n 1 + n 2 )。

這里唯一的問題是遞歸函數應該返回兩個值,即平均值和該平均值所基於的觀測值的數量。 在許多其他語言中,這不是問題。 在Java中,它需要一些瑣碎的助手類(盡管有些煩人)形式的黑客:

   // private helper class because Java doesn't allow multiple returns
   private static class Pair {
      public double avg;
      public int n;

      public Pair(double avg, int n) {
         super();
         this.avg = avg;
         this.n = n;
      }      
   }

應用分而治之策略產生以下遞歸:

   private static Pair getEvenAverage(int[] a, int first, int last) {
      if (first == last) {
         if (a[first] % 2 == 0) {
            return new Pair(a[first], 1);
         }
      } else {
         int mid = (first + last) / 2;
         Pair p1 = getEvenAverage(a, first, mid);
         Pair p2 = getEvenAverage(a, mid + 1, last);
         int total = p1.n + p2.n;
         if (total > 0) {
            return new Pair((p1.n * p1.avg + p2.n * p2.avg) / total, total);
         }
      }
      return new Pair(0.0, 0);
    }

我們可以處理空數組,保護最終用戶不必了解簿記參數,並通過使用以下公共前端僅返回平均值:

   public static double getEvenAverage(int[] a) {
      return a.length > 0 ? getEvenAverage(a, 0, a.length - 1).avg : 0.0;
   }

該解決方案的好處是,對於n個項目的陣列,O(log n)堆棧的增長較之於已提出的各種其他解決方案的O(n)有所提高。 結果,它可以處理更大的數組,而不必擔心堆棧溢出。

暫無
暫無

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

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