[英]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);
}
為了跟蹤到目前為止您所遇到的偶數,只需傳遞一個額外的參數即可。
此外,您還可以為偶數之和傳遞一個額外的參數,當您遇到基本情況時,您可以返回平均值,即偶數之和除以它們的計數。
還有一件事,您的代碼對於第一個和最后一個元素都有兩個基本用例,它們是不需要的。
n
(從數組的大小開始,直到第一個元素),或者 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);
好了,我們也不得不加入到sum
和count
,對不對?
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);
}
同時,使用當前值更新容器 。
向后收集時,您需要將所有信息存儲在返回值中 。
由於要記住許多事情,因此應使用容器作為返回類型( Results
或2
-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.