Is there a Javascript function to calculate the median of a Set [duplicate] - javascript

I've been trying to calculate median but still I've got some mathematical issues I guess as I couldn't get the correct median value and couldn't figure out why. Here's the code;
class StatsCollector {
constructor() {
this.inputNumber = 0;
this.average = 0;
this.timeout = 19000;
this.frequencies = new Map();
for (let i of Array(this.timeout).keys()) {
this.frequencies.set(i, 0);
}
}
pushValue(responseTimeMs) {
let req = responseTimeMs;
if (req > this.timeout) {
req = this.timeout;
}
this.average = (this.average * this.inputNumber + req) / (this.inputNumber + 1);
console.log(responseTimeMs / 1000)
let groupIndex = Math.floor(responseTimeMs / 1000);
this.frequencies.set(groupIndex, this.frequencies.get(groupIndex) + 1);
this.inputNumber += 1;
}
getMedian() {
let medianElement = 0;
if (this.inputNumber <= 0) {
return 0;
}
if (this.inputNumber == 1) {
return this.average
}
if (this.inputNumber == 2) {
return this.average
}
if (this.inputNumber > 2) {
medianElement = this.inputNumber / 2;
}
let minCumulativeFreq = 0;
let maxCumulativeFreq = 0;
let cumulativeFreq = 0;
let freqGroup = 0;
for (let i of Array(20).keys()) {
if (medianElement <= cumulativeFreq + this.frequencies.get(i)) {
minCumulativeFreq = cumulativeFreq;
maxCumulativeFreq = cumulativeFreq + this.frequencies.get(i);
freqGroup = i;
break;
}
cumulativeFreq += this.frequencies.get(i);
}
return (((medianElement - minCumulativeFreq) / (maxCumulativeFreq - minCumulativeFreq)) + (freqGroup)) * 1000;
}
getAverage() {
return this.average;
}
}
Here's the snapshot of the results when I enter the values of
342,654,987,1093,2234,6243,7087,20123
The correct result should be;
Median: 1663.5

Change your median method to this:
function median(values){
if(values.length ===0) throw new Error("No inputs");
values.sort(function(a,b){
return a-b;
});
var half = Math.floor(values.length / 2);
if (values.length % 2)
return values[half];
return (values[half - 1] + values[half]) / 2.0;
}
fiddle

Here's another solution:
function median(numbers) {
const sorted = Array.from(numbers).sort((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return (sorted[middle - 1] + sorted[middle]) / 2;
}
return sorted[middle];
}
console.log(median([4, 5, 7, 1, 33]));

The solutions above - sort then find middle - are fine, but slow on large data sets. Sorting the data first has a complexity of n x log(n).
There is a faster median algorithm, which consists in segregating the array in two according to a pivot, then looking for the median in the larger set. Here is some javascript code, but here is a more detailed explanation
// Trying some array
alert(quickselect_median([7,3,5])); // 2300,5,4,0,123,2,76,768,28]));
function quickselect_median(arr) {
const L = arr.length, halfL = L/2;
if (L % 2 == 1)
return quickselect(arr, halfL);
else
return 0.5 * (quickselect(arr, halfL - 1) + quickselect(arr, halfL));
}
function quickselect(arr, k) {
// Select the kth element in arr
// arr: List of numerics
// k: Index
// return: The kth element (in numerical order) of arr
if (arr.length == 1)
return arr[0];
else {
const pivot = arr[0];
const lows = arr.filter((e)=>(e<pivot));
const highs = arr.filter((e)=>(e>pivot));
const pivots = arr.filter((e)=>(e==pivot));
if (k < lows.length) // the pivot is too high
return quickselect(lows, k);
else if (k < lows.length + pivots.length)// We got lucky and guessed the median
return pivot;
else // the pivot is too low
return quickselect(highs, k - lows.length - pivots.length);
}
}
Astute readers will notice a few things:
I simply transliterated Russel Cohen's Python solution into JS,
so all kudos to him.
There are several small optimisations worth
doing, but there's parallelisation worth doing, and the code as is
is easier to change in either a quicker single-threaded, or quicker
multi-threaded, version.
This is the average linear time
algorithm, there is more efficient a deterministic linear time version, see Russel's
post for details, including performance data.
ADDITION 19 Sept. 2019:
One comment asks whether this is worth doing in javascript. I ran the code in JSPerf and it gives interesting results.
if the array has an odd number of elements (one figure to find), sorting is 20% slower that this "fast median" proposition.
if there is an even number of elements, the "fast" algorithm is 40% slower, because it filters through the data twice, to find elements number k and k+1 to average. It is possible to write a version of fast median that doesn't do this.
The test used rather small arrays (29 elements in the jsperf test). The effect appears to be more pronounced as arrays get larger. A more general point to make is: it shows these kinds of optimisations are worth doing in Javascript. An awful lot of computation is done in JS, including with large amounts of data (think of dashboards, spreadsheets, data visualisations), and in systems with limited resources (think of mobile and embedded computing).

var arr = {
max: function(array) {
return Math.max.apply(null, array);
},
min: function(array) {
return Math.min.apply(null, array);
},
range: function(array) {
return arr.max(array) - arr.min(array);
},
midrange: function(array) {
return arr.range(array) / 2;
},
sum: function(array) {
var num = 0;
for (var i = 0, l = array.length; i < l; i++) num += array[i];
return num;
},
mean: function(array) {
return arr.sum(array) / array.length;
},
median: function(array) {
array.sort(function(a, b) {
return a - b;
});
var mid = array.length / 2;
return mid % 1 ? array[mid - 0.5] : (array[mid - 1] + array[mid]) / 2;
},
modes: function(array) {
if (!array.length) return [];
var modeMap = {},
maxCount = 1,
modes = [array[0]];
array.forEach(function(val) {
if (!modeMap[val]) modeMap[val] = 1;
else modeMap[val]++;
if (modeMap[val] > maxCount) {
modes = [val];
maxCount = modeMap[val];
}
else if (modeMap[val] === maxCount) {
modes.push(val);
maxCount = modeMap[val];
}
});
return modes;
},
variance: function(array) {
var mean = arr.mean(array);
return arr.mean(array.map(function(num) {
return Math.pow(num - mean, 2);
}));
},
standardDeviation: function(array) {
return Math.sqrt(arr.variance(array));
},
meanAbsoluteDeviation: function(array) {
var mean = arr.mean(array);
return arr.mean(array.map(function(num) {
return Math.abs(num - mean);
}));
},
zScores: function(array) {
var mean = arr.mean(array);
var standardDeviation = arr.standardDeviation(array);
return array.map(function(num) {
return (num - mean) / standardDeviation;
});
}
};

2022 TypeScript Approach
const median = (arr: number[]): number | undefined => {
if (!arr.length) return undefined;
const s = [...arr].sort((a, b) => a - b);
const mid = Math.floor(s.length / 2);
return s.length % 2 === 0 ? ((s[mid - 1] + s[mid]) / 2) : s[mid];
};
Notes:
The type in the function signature (number[]) ensures only an array of numbers can be passed to the function. It could possibly be empty though.
if (!arr.length) return undefined; checks for the possible empty array, which would not have a median.
[...arr] creates a copy of the passed-in array to ensure we don't overwrite the original.
.sort((a, b) => a - b) sorts the array of numbers in ascending order.
Math.floor(s.length / 2) finds the index of the middle element if the array has odd length, or the element just to the right of the middle if the array has even length.
s.length % 2 === 0 determines whether the array has an even length.
(s[mid - 1] + s[mid]) / 2 averages the two middle items of the array if the array's length is even.
s[mid] is the middle item of an odd-length array.

TypeScript Answer 2020:
// Calculate Median
const calculateMedian = (array: Array<number>) => {
// Check If Data Exists
if (array.length >= 1) {
// Sort Array
array = array.sort((a: number, b: number) => {
return a - b;
});
// Array Length: Even
if (array.length % 2 === 0) {
// Average Of Two Middle Numbers
return (array[(array.length / 2) - 1] + array[array.length / 2]) / 2;
}
// Array Length: Odd
else {
// Middle Number
return array[(array.length - 1) / 2];
}
}
else {
// Error
console.error('Error: Empty Array (calculateMedian)');
}
};

const median = (arr) => {
return arr.slice().sort((a, b) => a - b)[Math.floor(arr.length / 2)];
};

Short and sweet.
Array.prototype.median = function () {
return this.slice().sort((a, b) => a - b)[Math.floor(this.length / 2)];
};
Usage
[4, 5, 7, 1, 33].median()
Works with strings as well
["a","a","b","b","c","d","e"].median()

For better performance in terms of time complexity, use MaxHeap - MinHeap to find the median of stream of array.

Simpler & more efficient
const median = dataSet => {
if (dataSet.length === 1) return dataSet[0]
const sorted = ([ ...dataSet ]).sort()
const ceil = Math.ceil(sorted.length / 2)
const floor = Math.floor(sorted.length / 2)
if (ceil === floor) return sorted[floor]
return ((sorted[ceil] + sorted[floor]) / 2)
}

Simple solution:
function calcMedian(array) {
const {
length
} = array;
if (length < 1)
return 0;
//sort array asc
array.sort((a, b) => a - b);
if (length % 2) {
//length of array is odd
return array[(length + 1) / 2 - 1];
} else {
//length of array is even
return 0.5 * [(array[length / 2 - 1] + array[length / 2])];
}
}
console.log(2, calcMedian([1, 2, 2, 5, 6]));
console.log(3.5, calcMedian([1, 2, 2, 5, 6, 7]));
console.log(9, calcMedian([13, 9, 8, 15, 7]));
console.log(3.5, calcMedian([1, 4, 6, 3]));
console.log(5, calcMedian([5, 1, 11, 2, 8]));

Simpler, more efficient, and easy to read
cloned the data to avoid alterations to the original data.
sort the list of values.
get the middle point.
get the median from the list.
return the median.
function getMedian(data) {
const values = [...data];
const v = values.sort( (a, b) => a - b);
const mid = Math.floor( v.length / 2);
const median = (v.length % 2 !== 0) ? v[mid] : (v[mid - 1] + v[mid]) / 2;
return median;
}

const medianArr = (x) => {
let sortedx = x.sort((a,b)=> a-b);
let halfIndex = Math.floor(sortedx.length/2);
return (sortedx.length%2) ? (sortedx[Math.floor(sortedx.length/2)]) : ((sortedx[halfIndex-1]+sortedx[halfIndex])/2)
}
console.log(medianArr([1,2,3,4,5]));
console.log(medianArr([1,2,3,4,5,6]));

function Median(arr){
let len = arr.length;
arr = arr.sort();
let result = 0;
let mid = Math.floor(len/2);
if(len % 2 !== 0){
result += arr[mid];
}
if(len % 2 === 0){
result += (arr[mid] + arr[mid+1])/2
}
return result;
}
console.log(`The median is ${Median([0,1,2,3,4,5,6])}`)

function median(arr) {
let n = arr.length;
let med = Math.floor(n/2);
if(n % 2 != 0){
return arr[med];
} else{
return (arr[med -1] + arr[med])/ 2.0
}
}
console.log(median[1,2,3,4,5,6]);

The arr.sort() method sorts the elements of an array in place and returns the array. By default, it sorts the elements alphabetically, so if the array contains numbers, they will not be sorted in numerical order.
On the other hand, the arr.sort((a, b) => a - b) method uses a callback function to specify how the array should be sorted. The callback function compares the two elements a and b and returns a negative number if a should be sorted before b, a positive number if b should be sorted before a, and zero if the elements are equal. In this case, the callback function subtracts b from a, which results in a sorting order that is numerical in ascending order.
So, if you want to sort an array of numbers in ascending order, you should use arr.sort((a, b) => a - b), whereas if you want to sort an array of strings alphabetically, you can use arr.sort():
function median(numbers) {
const sorted = Array.from(numbers).sort((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return (sorted[middle - 1] + sorted[middle]) / 2;
}
return sorted[middle];
}

function findMedian(arr) {
arr.sort((a, b) => a - b)
let i = Math.floor(arr.length / 2)
return arr[i]
}
let result = findMedian([0, 1, 2, 4, 6, 5, 3])
console.log(result)

Related

Calculating second largest number in array (Javascript) : Seems to work in Sandbox but fails Hackerrank testing

Tried to determine the second largest number in an array (Javascript) on CodeSandbox. It seems to work fine, but it fails the CodeWars testing. I have added a dummy array just to run my own tests in Sandbox.(Have mercy, I'm a beginner and this is my first StackOverFlow question)
const nums = [3, 100.3, 88, 1, -2.4, 9, 18];
const getSecondLargest = (nums) => {
const descending = nums.sort((a, b) => b - a);
return descending[1];
};
console.log(getSecondLargest(nums)); // console returns 88
EDIT: Okay so I with my super-tired brain I said CodeWars, when I actually meant Hackerrank (so sorry!). I realized they didn't necessarily test with NaNs, but they did have repeating numbers, so using the index of [1] isn't ideal. The exercise is from the 10 Days of Javascript - Day 3: Arrays https://hackerrank.com/domains/tutorials/10-days-of-javascript
So I now tried this code below, and it passes...but my code seems a bit janky, is there a cleaner way to write this, and can I combine it with the isNan logic then?
const nums = [3, 100, 88, 100, -2.4, 9, 18];
const getSecondLargest = (nums) => {
const ascending = nums.sort((a, b) => a - b);
if (ascending[ascending.length - 2] === ascending[ascending.length - 1]) {
return ascending[ascending.length - 3];
} else {
return ascending[ascending.length - 2];
}
};
console.log(getSecondLargest(nums)); // console returns 88
It looks like there maybe strings in the array and you need to handle that. Here are a few ways:
One is to filter the non-numerical stuff out before sorting. You can use isNaN() to test if an object "is not a number".
const getSecondLargest = (nums) => {
const descending = nums
.filter(n => !isNaN(n))
.sort((a, b) => b - a);
return descending.length < 2 ? undefined : descending[1];
};
Another option is to handle the strings in sorting. Push them to the end of the array:
const getSecondLargest = (nums) => {
const descending = nums.sort((a, b) => {
if (isNaN(a) && isNaN(b)) return 0;
if (isNaN(a)) return 1;
if (isNaN(b)) return -1;
return b - a;
});
return descending.length < 2 || isNaN(descending[1]) ? undefined : descending[1];
};
A third way is a simple for loop that keeps track of the 2 highest values:
const getSecondLargest = (nums) => {
let max1 = undefined;
let max2 = undefined;
for (let n of nums) {
if (isNaN(n)) continue;
if (max2 === undefined || n > max2) {
if (max1 === undefined || n > max1 ) {
max2 = max1;
max1 = n;
}
else {
max2 = n;
}
}
}
return max2;
}

How do .push() and .reduce() work together here? (fibonacci)

I've done an assignment, where a function's output is the last number of a fibonacci-array. Truth it, I got stuck hard on this one and I found the code in the second else if statement on stackoverflow. But I can't wrap my head around it, how this is working exactly.
Here is the code:
const fibonacci = function(input) {
let n = Number(input);
if (n === 1) {
return 1;
} else if (n < 1) {
return "OOPS";
} else if (n > 1) {
let array = new Array(n); // <---- Starting here
let filled = array.fill(1);
let reduced = filled.reduce((acc, _, i) => {
acc.push((i <=1) ? i : acc[i-2] + acc[i-1])
return acc;
},[]);
return reduced[n - 1] + reduced[n - 2];
}
}
My question: Why does reduced returns an Array instead of a single value? And since it returns an array - why won't the push'ed numbers get added to the initial array, which already has values in it? -> let's say input = 4 then filled = [1, 1, 1, 1].
const fibonacci = function(input) {
let n = Number(input);
if (n === 1) {
return 1;
} else if (n < 1) {
return "OOPS";
} else if (n > 1) {
let array = new Array(n); // <---- Starting here
let filled = array.fill(1);
let reduced = filled.reduce((acc, _, i) => {
acc.push((i <=1) ? i : acc[i-2] + acc[i-1])
return acc;
},[]); // <- reduce is initialized with an array (new array),
return reduced[n - 1] + reduced[n - 2];
}
}
as reduce is initialized with a new array, the function is reducing (adding new values to the new initialized array) and returning the same.
here how the reducers work
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce

What's wrong in my code? It fails just 2 of the test cases without showing any error

I have started learning programming on my own just a few months back. So pardon me if my question sounds a bit silly.
One of the challenges on freeCodeCamp needs to define a function that takes an array with 2 values as an input and the function should return the LCM of all the numbers within that range inclusive of those 2 numbers.
My code below passes the tests number 1,2,3,6 given in the exercise. but somehow fails for the tests 4 & 5. Also freeCodeCamp is not showing any error! So I am unable to figure out what am I doing wrong in the below code.
function smallestCommons(arr) {
let allNum = [];
for (let i = Math.min(...arr); i <= Math.max(...arr); i++) {
allNum.push(i);
}
function findFactors(x) {
let allFactors = [];
for (let i = 1; i <= x; i++) {
if (x % i == 0) {
allFactors.push(i);
}
}
return allFactors;
}
function findGCF(a,b) {
return findFactors(a).filter(item => findFactors(b).includes(item)).reduce((p,q) => p*q);
}
return allNum.reduce((a,b) => ((a*b)/findGCF(a,b)));
}
The tests given in the exercise are as follows. My code passes 1,2,3 & 6 but fails 4 & 5.
smallestCommons([1, 5]) should return a number.
smallestCommons([1, 5]) should return 60.
smallestCommons([5, 1]) should return 60.
smallestCommons([2, 10]) should return 2520.
smallestCommons([1, 13]) should return 360360.
smallestCommons([23, 18]) should return 6056820.
function smallestCommons(arr) {
let allNum = [];
for (let i = Math.min(...arr); i <= Math.max(...arr); i++) {
allNum.push(i);
}
function findFactors(x) {
let allFactors = [];
for (let i = 1; i <= x; i++) {
if (x % i == 0) {
allFactors.push(i);
}
}
return allFactors;
}
function findGCF(a,b) {
return findFactors(a).filter(item => findFactors(b).includes(item)).reduce((p,q) => p*q);
}
return allNum.reduce((a,b) => ((a*b)/findGCF(a,b)));
}
console.log(smallestCommons([1, 5])); // should return a number.
console.log(smallestCommons([1, 5])); // should return 60.
console.log(smallestCommons([5, 1])); // should return 60.
console.log(smallestCommons([2, 10])); // should return 2520.
console.log(smallestCommons([1, 13])); // should return 360360.
console.log(smallestCommons([23, 18])); // should return 6056820.
Your findGCF function is off. To find the GCF of two numbers, you should find the largest factor which evenly divides both. Eg
findGCF(60, 6)
should be 6. (But yours returns 36)
function findFactors(x) {
// console.log(x);
let allFactors = [];
for (let i = 1; i <= x; i++) {
if (x % i === 0) {
allFactors.push(i);
}
}
return allFactors;
}
function findGCF(a, b) {
const bFac = findFactors(b);
return findFactors(a)
.filter(item => bFac.includes(item))
.reduce((p, q) => p * q);
}
console.log(findGCF(60, 6)); // should be 6
To reduce the computational complexity and fix it at the same time, make a Set of one of the factor collections, then iterate over an array of the other factor collections, starting from the largest factor and working your way downwards, and .find the first factor which is contained in the Set (it may end up being 1):
function findGCF(a, b) {
const bFacSet = new Set(findFactors(b));
return findFactors(a)
.reverse()
.find(item => bFacSet.has(item));
}
Fix that, and your smallestCommons function works as desired:
function smallestCommons(arr) {
const allNum = [];
for (let i = Math.min(...arr); i <= Math.max(...arr); i++) {
allNum.push(i);
}
function findFactors(x) {
const allFactors = [];
for (let i = x; i >= 1; i--) {
if (x % i === 0) {
allFactors.push(i);
}
}
return allFactors;
}
function findGCF(a, b) {
const bFacSet = new Set(findFactors(b));
return findFactors(a)
.find(item => bFacSet.has(item));
}
return allNum.reduce((a,b) => ((a*b)/findGCF(a,b)));
}
console.log(smallestCommons([2, 10])) // should return 2520.
console.log(smallestCommons([1, 13])) // should return 360360.

refactorized solve for an algorithm in javascript

I have attended a technical interview for a development company. They asked me the following:
Giving an array of numbers (n) find 2 numbers that sum gives (k) and print them.
e.g
Input: n = [2,6,4,5,7,1], k = 8
Output: result=(2,6),(7,1)
My solution:
function findSum(n,k){
let aux = []
for (let i = 0; i < n.length; i++) {
for (let j = i+1; j < n.length; j++) {
if (n[i] + n[j] == k) {
aux.push({ first: n[i], second: n[j] })
}
}
}
return aux;
}
They told me that, it is possible to make the exercise with some kind of key or mapping.
Does some one know how to do it with only one loop?
The key to solving a question like this with low time complexity is the ability to efficiently search the data structure. A lot of answers rearrange the array in a way where searching an array is optimized. Another approach is with a data structure that inherently has fast search.
Set and Map data structures have O(1) time complexity for searches, which make them good data structures where searching can be leveraged to increase performance.
I use a new Map and traverse the array while adding it as a key. I set the key to the number and the value to the number of times I see it. I use a map over a new Set because I can also keep track of the number of instances of that particular number.
I search for the number that would sum up to k, which is: (k - num). If I find that number, I add both numbers to my results data structure and decrement the value by 1, to show that it's been used.
Time complexity: O(n), memory complexity: O(2n). Twice the amount of space compared to the original array because I have a key and a value to store in my Map
function pairSums(arr, k){
const map = new Map
const matches = []
for (let num of arr) {
const search = k - num
if (map.get(search) > 0) {
matches.push([num, k - num])
map.set(search, map.get(search) - 1)
} else if (!map.has(num)){
map.set(num, 1)
} else {
map.set(num, map.get(num) + 1)
}
}
return matches
}
console.log(pairSums([2, 6, 6, 6, 2, 4, 4, 4, 5, 7, 1, 4, 2], 8))
Match a number x from array with a key Math.min(x, k - x). Then run through your array and store every number in a hash using mentioned key. When the key you are going to add already is in the hash - check if stored value and current number gives required sum.
function findSum(n, k){
let hash = {};
for(let i = 0; i < n.length; ++i){
let x = n[i], key = Math.min(x, k - x);
if((key in hash) && hash[key] + x == k)
return [hash[key], x];
else hash[key] = x;
}
}
A task like this can be as simple or as complicated as you want to make it. Here's one solution, for example:
function findPairs(n, k) {
return n.reduce((pairs, next, i) => pairs.concat(
n.slice(i + 1)
.filter(num => next + num === k)
.map(num => [ next, num ])
),
[]
);
}
For the inputs [2, 6, 4, 5, 7, 1] and 8 will output [ [2, 6], [7, 1] ].
From https://www.geeksforgeeks.org/write-a-c-program-that-given-a-set-a-of-n-numbers-and-another-number-x-determines-whether-or-not-there-exist-two-elements-in-s-whose-sum-is-exactly-x/:
Sort the array in non-decreasing order.
Initialize two index variables to find the candidate elements in the sorted array. Initialize l to the leftmost index: l = 0, Initialize r to the rightmost index: r = n.length - 1
Loop while l < r.
if (n[l] + n[r] == k) then return 1
else if( n[l] + n[r] < k ) then l++
else r--
No candidates in whole array - return 0
I think by sorting you can do that
var n = [2,6,4,5,7,1];
var k = 8 ;
n.sort();
let start = 0, end = n.length-1;
while(start < n.length && end >= 0) {
let current_sum = (n[start] + n[end]);
if(current_sum == k) {
console.log('Found sum with '+ n[start] +' and '+ n[end]);
break;
}
else if(current_sum > k) {
end--;
} else {
start++;
}
}
if(start == n.length || end < 0) {
console.log('Not Found');
}
but while writing this code I got one another approach also
const set = new Set([2,6,4,5,7,1]);
var k = 8;
let found = false;
for (let item of set) {
let another = k - item;
if(set.has(another)){
console.log('found with '+item +' and ' +another);
found = true;
break;
}
}
if(!found) {
console.log('Not found');
}
If numbers are non-negative and the target value is within JavaScript array limit:
function findsums(arr,k){
var ret=[];
var aux=[];
arr.forEach(function(i){
if(i<=k){
if(aux[k-i])
ret.push([k-i,i]);
aux[i]=true;
}
});
return ret;
}
console.log(findsums([2,6,4,5,7,1],8));
Similar approach could work with a bitfield (or even with a sparse array of bitfields) too.
Minified alternative similar to #Andrew's great answer, but assumes that all numbers are above 0 :
var pairs = (arr, k) => arr.reduce((a, n) =>
(a[n - k]-- ? a.push([n, k - n]) : a[-n] = a[-n] | 0 + 1, a), []);
console.log(JSON.stringify( pairs([2,6,4,5,7,1], 8) ));

javascript fibonacci memoization

To calculate the nth term of the fibonacci sequence, I have the familiar recursive function:
var fibonacci = function(index){
if(index<=0){ return 0; }
if(index===1){ return 1; }
if(index===2){ return 2; }
return fibonacci(index-2) + fibonacci(index-1);
}
This works as expected. Now, I am trying to store calculated indices in an object:
var results = {
0: 0,
1: 1,
2: 2
};
var fibonacci = function(index){
if(index<=0){ return 0; }
if(index===1){ return 1; }
if(index===2){ return 2; }
if(!results[index]){
results[index] = fibonacci(index-2) + fibonacci(index-1);
}
}
I know this isn't actually increasing performance since I'm not accessing the results object, but I wanted to check first if my results object was being populated correctly before memoizing. Unfortunately, it isn't. For fibonacci(9), I get:
Object {0: 0, 1: 1, 2: 2, 3: 3, 4: NaN, 5: NaN, 6: NaN, 7: NaN, 8: NaN, 9: NaN}
Why am I getting NaN for indices past 3?
Here's a solution using "Helper Method Recursion":
function fib(n) {
const memorize = {};
function helper(n) {
if (n in memorize) return memorize[n];
if (n < 3) return 1;
return memorize[n] = helper(n - 1) + helper(n - 2);
}
return helper(n);
}
Here is my solution:
function fib(n, res = [0, 1, 1]) {
if (res[n]) {
return res[n];
}
res[n] = fib(n - 1, res) + fib(n - 2, res);
return res[n];
}
console.log(fib(155));
The recursive Fibonacci consume too much processing power which is not good for application. to improve this we use Memoization. which keeps the computed result store in Array. so next when the same value comes it will simply return the Stored value from the computed Array.
function memoizeFabonaci(index, cache = []) {
// console.log('index :', index, ' cache:', cache)
if (cache[index]) {
return cache[index]
}
else {
if (index < 3) return 1
else {
cache[index] = memoizeFabonaci(index - 1, cache) + memoizeFabonaci(index - 2, cache)
}
}
return cache[index];
}
let a = 15
console.log('Memoize febonaci', memoizeFabonaci(a))
const f = (n, memo = [0, 1, 1]) => memo[n] ? memo[n] : (memo[n] = f(n - 1, memo) + f(n - 2, memo)) && memo[n]
console.log(f(70))
Going to close the loop on this answer by posting #Juhana's comment:
"Because your function doesn't return anything when index > 2"
Here're my solutions
With Memoization (Dynamic Programming) (Time complexity approximately O(n))
const results = {}
function fib(n) {
if (n <= 1) return n
if (n in results) {
return results[n]
}
else {
results[n] = fib(n - 2) + fib(n - 1)
}
return results[n]
}
console.log(fib(100))
Without Memoization (Time complexity approximately O(2^n))
function fib(n) {
if (n <= 1) return n
return fib(n - 1) + fib(n - 2)
}
console.log(fib(10))
Here is my object orientated attempt.
var memofib = {
memo : {},
fib : function(n) {
if (n === 0) {
return 0;
} else if (n === 1) {
return 1;
} else {
if(this.memo[n]) return this.memo[n];
return this.memo[n] = this.fib(n - 1) + this.fib(n - 2);
}
}
};
console.log(memofib.fib(10));
Here's my solution achieving memoization using a non-recursive approach.
// The Fibonacci numbers.
// 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ……..
function fibonacci(n) {
const map = new Map(); // Objects can also be used
map.set(0,1); // seed value for f(0)
map.set(1,1); // seed value for f(1)
for(let i=2; i < n - 1; i++) {
const result = map.get(i - 1) + map.get(i - 2);
map.set(i,result);
}
return map.get(n - 2);
}
console.log(fibonacci(20)); // 4181
I have added some additions.
var results = {};
var fibonacci = function (index) {
if (index <= 0) return 0;
if (index == 1 || index == 2) return 1;
return fibonacci(index - 2) + fibonacci(index - 1);
};
for (var i = 1; i <= 10; i++) {
results[i] = fibonacci(i);
}
console.log(results);

Categories