跳至主要内容

[演算法] Mean Median Mode 計算

keywords: 四捨五入, round

此系列筆記主要依照 [Udemy] Learning Algorithms in JavaScript from Scratch by Eric Traub 的課程脈絡加以整理,但部分程式碼是消化後以自己較易理解的方式重新撰寫,因此和原課程內容有些出入。

問題描述

當我們傳入一個全都是數值的陣列時,計算這些數值的 加總/總合(sum)、平均數(mean)、中位數(Median)和眾數(Mode),並以物件的方式回傳:

function meanMedianMode (arr) {
// call other three function
// return obj which has mean, median, mode in it
}

function getMean (arr) {...}
function getMedian (arr) {...}
function getMode (arr) {...}

演算法實做

平均數(Mean)

計算平均數之前,我們要先把所有數字加總,加總的方式我們可以使用 Array.prototype.reduce 這個方法:

let sum = arr.reduce((acc, cur) => acc + cur);

接著在除以所有的陣列數目 sum / arr.length ,最後我們可以透過 Math.round() 將輸出的結果進行四捨五入:

function getMean(arr) {
let sum = arr.reduce((acc, cur) => acc + cur);
let mean = sum / arr.length;
return Math.round(mean * 100) / 100; // 取到小數第二位
}

中位數(Median)

中位數會有兩種情況,當輸入陣列的個數是奇數時,就取最中間的那個;當輸入的數目是偶數時,則要取最中間的那兩個加總除以二。

當數目是偶數時中數會是:

median = (arr[arr.length / 2] + arr[arr.length / 2 - 1]) / 2;

這裡要注意的是,因為陣列裡面是使用 index,所以是 arr.length / 2 - 1 而不是 +1

當數目是奇數時,中數會是:

median = arr[(arr.length - 1) / 2];

綜合起來:

function getMedian(arr) {
arr = arr.sort((a, b) => a - b);
let median;
if (arr.length % 2 === 0) {
// 數目為偶數
median = (arr[arr.length / 2] + arr[arr.length / 2 - 1]) / 2;
} else {
// 數目為奇數
median = arr[(arr.length - 1) / 2];
}
return median;
}

眾數(mode)

最後一個稍稍複雜的是計算眾數,這裡我們會用到在先前 Harmless Ransom Note 計算陣列中每個元素出現幾次的方法。

我們先計算在陣列中每一個元素出現幾次:

function getMode(arr) {
let countList = {};
for (let value of arr) {
// 將陣列中的 Number 當作 String 來處理,以計算出現次數
value = value.toString();
if (!countList[value]) countList[value] = 0;
countList[value]++;
}
/* ... */
}

接著我們要根據這個 countList 找出被計數最多的元素:

function getMode(arr) {
/* ... */
let maxCount = 0;
let mode = []; // 眾數
for (let prop in countList) {
if (maxCount < countList[prop]) {
maxCount = countList[prop];
mode = [prop];
} else if (maxCount === countList[prop]) {
// 如果有同樣數目的眾數
mode.push(prop);
}
}

// 如果每個元素的計數一樣,則沒有眾數
if (mode.length === Object.keys(countList).length) {
mode = [];
}

return mode;
}

meanMedianMode

最後我們多一個小小的判斷,如果輸入的數值包含不是數值的話,則給予警告:

function meanMedianMode(arr) {
arr.forEach((item) => {
if (!/^[0-9]+$/.test(item)) {
console.warn(item + ' is not a number.');
}
});

return {
mean: getMean(arr),
median: getMedian(arr),
mode: getMode(arr),
};
}

完整程式碼

function meanMedianMode (arr) {
arr.forEach(item => {
if (!(/^[0-9]+$/.test(item))) {
console.warn(item + ' is not a number.')
}
})

return {
mean: getMean(arr),
median: getMedian(arr),
mode: getMode(arr)
}
}


/**
* 計算加總與平均數
**/
function getMean (arr) {
let sum = arr.reduce((acc, cur) => acc + cur)
let mean = sum / arr.length
return Math.round(mean * 100) / 100 // 取到小數第二位
}

/**
* 計算中位數
**/
function getMedian (arr) {
arr = arr.sort((a, b) => a - b)
let median
if (arr.length % 2 === 0) {
// 數目為偶數
median = (arr[arr.length / 2] + arr[arr.length / 2 - 1]) / 2
} else {
// 數目為奇數
median = arr[(arr.length - 1) / 2 ]
}
return median
}

/**
* 計算眾數
**/
function getMode (arr) {
let countList = {}
for (let value of arr) {
value = value.toString()
if (!countList[value]) countList[value] = 0
countList[value]++
}
let maxCount = 0
let mode = []
for (let prop in countList) {
if (maxCount < countList[prop]) {
maxCount = countList[prop]
mode = [prop]
} else if (maxCount === countList[prop]) {
mode.push(prop)
}
}

if (mode.length === Object.keys(countList).length) {
mode = []
}

return mode
}

console.log(meanMedianMode([1, 2, 3, 4, 5, 4, 6, 1])) // { mean: 3.25, median: 3.5, mode: [ '1', '4' ] }
console.log(meanMedianMode([9, 10, 23, 10 ,23, 9])) // { mean: 14, median: 10, mode: [] }

資料來源