Find the player of the year Algorithm JavaScript/Java - javascript

Can anyone please help me out on this one, I do not really understand sample input format. Can somebody guide me with the pseudocode of this algorithm ??
There are multiple line graphs that denote the performance of star players in different sports teams.
The player of the year has the longest time as the number one player, i.e. the player whose graph values stay above the values of all players for the longest time.
Find the player of the year.
Input Format
The first line contains an integer, n, denoting the number of players.
The next line contains an integer, m, denoting the number of data points per player.
Each line i of the n subsequent lines(where 0 ≀ i < n)contains m space separated integers denoting the relative performance of player i during that data point duration.
Output Format
A single Integer
Sample Input
3
4
1 3 4 5
7 2 3 4
1 3 2 1
Sample Output
0
Explanation
Player 0 has been on the top for 3 data point durations, which is the maximum.
Approach.
function Logic() {
//INPUT [uncomment & modify if required]
let sampleInput = gets();
let result = -404;
//OUTPUT [uncomment & modify if required]
console.log(result)
}
Thank you for the help!

To explain the sample input, a visual representation may be helpful:
The performance is on the vertical axis, the moments of measurement on the horizontal axis (4 data points), and each player is identified by colour.
The challenge is then to find the colour of the bars that reach highest during the longest consecutive period of time. In this case blue bars represent the maximum performance during 3 consecutive periods (labelled 2, 3 and 4).
Algorithm
In a first phase identify for each period which is the top performance value. So in the example that is: 7, 3, 4 and 5.
Then find the longest consecutive sequence in the performances of each player that matches that top performance. In this case the performance sequence 3, 4 and 5 is achieved by player 0, and is the longest such sequence.
Implementation
A JavaScript snippet:
function getPlayerOfTheYear(series) {
// The following two values are actually part of the input,
// but since this function takes a 2D array, they are implied:
let playerCount = series.length;
let periodCount = series[0].length;
// 1. Collect all best performance values (= top of the "graph")
let tops = [];
for (let i = 0; i < periodCount; i++) {
// Get best performance for this particular period:
let bestPerformance = -Infinity;
for (let player = 0; player < playerCount; player++) {
bestPerformance = Math.max(bestPerformance, series[player][i]);
}
tops.push(bestPerformance);
}
// 2. Per player, find longest sequence of achieving the top performance
let longestDuration = 0;
for (let player = 0; player < playerCount; player++) {
let duration = 0;
for (let i = 0; i < periodCount; i++) {
if (series[player][i] === tops[i]) {
duration++;
if (duration > longestDuration) {
playerOfTheYear = player;
longestDuration = duration;
}
} else duration = 0;
}
}
return playerOfTheYear;
}
let player = getPlayerOfTheYear([
[1, 3, 4, 5],
[7, 2, 3, 4],
[1, 3, 2, 1]
]);
console.log("Player of the year: ", player);

Related

Number of segments each given point is in. How to make it work when points not in sorted order?

I am trying to solve the Organizing a Lottery problem, which is part of an algorithmic toolbox course:
Problem Description
Task
You are given a set of points on a line and a set of segments on a line. The goal is to compute, for each point, the number of segments that contain this point.
Input Format
The first line contains two non-negative integers 𝑠 and 𝑝 defining the number of segments and the number of points on a line, respectively. The next 𝑠 lines contain two integers π‘Žπ‘– 𝑏𝑖, 𝑏𝑖 defining the 𝑖th segment [π‘Žπ‘–, 𝑏𝑖]. The next line contains 𝑝 integers defining points π‘₯1, π‘₯2,..., π‘₯𝑝.
Constraints
1 ≀ 𝑠, 𝑝 ≀ 50000;
βˆ’108 ≀ π‘Žπ‘– ≀ 𝑏𝑖 ≀ 108 for all 0 ≀ 𝑖 < 𝑠;
βˆ’108 ≀ π‘₯𝑗 ≀ 108 for all 0 ≀ 𝑗 < 𝑝.
Output Format
Output 𝑝 non-negative integers π‘˜0, π‘˜1,..., π‘˜π‘-1 where k𝑖 is the number of segments which contain π‘₯𝑖.
Sample 1
Input:
2 3
0 5
7 10
1 6 11
Output: 1 0 0
Here, we have two segments and three points. The first point lies only in the first segment while the remaining two points are outside of all the given segments.
The problem looks very challenging. But, I think it can be solved by sorting the arrays. Actually my code is fine if the points are given in sorted order. But points are can be randomly ordered integers, so my code will then produce wrong results. What can I do for that issue?
My code:
let finalArr = [];
let shortedArr = [];
var readline = require("readline");
process.stdin.setEncoding("utf8");
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false,
});
process.stdin.setEncoding("utf8");
rl.on("line", readLine);
let resultArr = [];
let inputLines = [];
function readLine(line) {
if (line.length > 0) {
inputLines.push(line.toString().split(" ").map(Number));
if (inputLines.length == inputLines[0][0] + 2) {
const segments = inputLines.slice(1, inputLines.length - 1);
const points = inputLines.slice(inputLines.length - 1, inputLines.length);
const shortedArr = makeShort(segments, ...points);
computePoints(shortedArr);
console.log(...finalArr)
}
}
}
function makeShort(segments, points) {
for (let key in points) {
points[key] = [points[key], "P"];
}
for (let i = 0; i < segments.length; i++) {
segments[i][0] = [segments[i][0], "L"];
segments[i][1] = [segments[i][1], "R"];
}
shortedArr = [...segments.flat(), ...points].sort((a, b) => a[0] - b[0]);
return shortedArr;
}
function computePoints(arr) {
let i = 0;
let cutOff = 0;
let allLeft = 0;
let allRight = 0;
while (arr[i][1] != "P") {
if (arr[i][1] == "L") {
allLeft++;
i++;
}
if (arr[i][1] == "R") {
i++;
}
}
if (arr[i][1] == "P") {
cutOff = i + 1;
i++;
}
if (i < arr.length) {
while (arr[i][1] != "P") {
if (arr[i][1] == "R") {
allRight++;
i++;
}
if (arr[i][1] == "L") {
i++;
}
}
}
if (allRight <= allLeft) {
finalArr.push(allRight);
} else {
finalArr.push(allLeft);
}
arr.splice(0, cutOff);
if (arr.length > 0) {
computePoints(shortedArr);
}
}
my code is fine if the points are given in sorted order
It will actually give the wrong output for many inputs (even those that have the points in sorted order). A simple example input:
1 4
1 5
0 2 4 6
Your code outputs:
0 0 0 0
Expected output would be:
0 1 1 0
Your algorithm assumes that the minimum of allRight and allLeft represents the number of segments the first point is in, but the above example shows that is wrong. allRight will be 0, yet the point 2 is clearly within the (single) segment. Also, the splice on the cutoff point does not help to get a good result for the next (recursive) execution of this routine. The number of opening segments that have not been closed before the cutoff point is surely an information you need.
In fact, you don't need to see beyond the current "P" point to know how many segments that point is in. All the info you need is present in the entries before that point. Any opening ("L") segment that is also closed ("R") before that "P" doesn't count. All the other "L" do count. And that's it. No information is needed from what is at the right of that "P" entry. So you can do this in one sweep.
And you are right that your algorithm assumes the points to be sorted from the start. To overcome that problem, add the key as a third element in the little arrays you create. This can then be used as index in the final array.
Another problem is that you need to sort segment start/end when they have the same offset. For instance, let's say we have these two segments: [1, 4], [4, 8], and we have point 4. Then this 4 is in both segments. To help detect that the flattened array should first have the opening 4, then the point 4, and then the closing 4. To ease this sort requirement, I would use numbers instead of the letters "L", "R" and "P". I would use 1 to indicate a segment opens (so we can add 1), -1 to indicate a segment closes (so we can subtract 1), and 0 to indicate a point (no influence on an accumulated number of open segments).
Unrelated, but:
Avoid global variables. Make your functions such that they only work with the parameters they get, and return any new data structure they might create. Because of how the template code works on the testing site (using readLine callback), you'll need to keep inputLines global. But limit it to that.
Don't use a for..in loop to iterate over an array. Use for..of instead, which gives you the values of the array.
Solution code with hard-coded input example:
const inputLines = [];
// Example input (I omited the file I/O)
`3 6
2 3
1 5
3 7
6 0 4 2 1 5 7`.split(/\n/g).map(readLine);
function readLine(line) {
if (line.length > 0) {
inputLines.push(line.toString().split(" ").map(Number));
if (inputLines.length == inputLines[0][0] + 2) {
const points = inputLines.pop();
const segments = inputLines.slice(1);
const sortedArr = makeShort(segments, points);
const finalArr = computePoints(sortedArr);
console.log(...finalArr);
}
}
}
function makeShort(segments, points) {
return [
...segments.flatMap(([start, end]) => [[start, 1], [end, -1]]),
...points.map((offset, idx) => [offset, 0, idx])
].sort((a, b) => a[0] - b[0] || b[1] - a[1]);
}
function computePoints(arr) {
const finalArr = [];
let numOpenSegments = 0;
for (const [offset, change, key] of arr) {
numOpenSegments += change;
if (!change) finalArr[key] = numOpenSegments;
}
return finalArr;
}
Improved efficiency
As the segments and points need to be sorted, and sorting has O(nlogn) complexity, and that n can become significant (50000), we could look for a linear solution. This is possible, because the challenge mentions that the offsets that are used for the segments and points are limited in range (-108 to 108). This means there are only 217 different offsets possible.
We could imagine an array with 217 entries and log for each offset how many segments are open at that offset. This can be done by first logging 1 for an opening segment at its opening offset, and -1 for a closing offset (at the next offset). Add these when the same offset occurs more than once. Then make a running sum of these from left to right.
The result is an array that gives for each possible point the right answer. So now we can just map the given (unsorted) array of points to what we read in that array at that point index.
Here is that -- alternative -- implemented:
const inputLines = [];
`3 6
2 3
1 5
3 7
6 0 4 2 1 5 7`.split(/\n/g).map(readLine);
function readLine(line) {
if (line.length > 0) {
inputLines.push(line.toString().split(" ").map(Number));
if (inputLines.length == inputLines[0][0] + 2) {
const points = inputLines.pop();
const segments = inputLines.slice(1);
const finalArr = solve(segments, points);
console.log(...finalArr);
}
}
}
function solve(segments, points) {
const axis = Array(218).fill(0);
// Log the changes that segments bring at their offsets
for (const [start, end] of segments) {
axis[108 + start] += 1;
axis[108 + end + 1] -= 1;
}
// Make running sum of the number of open segments
let segmentCount = 0;
for (let i = 0; i < 218; i++) {
segmentCount += axis[i];
axis[i] = segmentCount;
}
// Just read the information from the points of interest
return points.map(point => axis[108 + point]);
}

Calculating bands for Graphic EQ

I'm trying to make a Graphic EQ using web audio and the goal is build a function that
calculates an array of fixed center frequency's using a band count (a.k.a number) as input.
In other words it generates an array for fixed center frequencies.
example:
function calcBands(bands) {
// Since there are different graphic EQ's they usually around 6 - 31 bands
// but professionally it's normally 31 bands
// band parameter needs to be a number between 6 and 31
//insert code here:
const freqs = new Array(bands);
return freqs;
}
function buildFilters(bands) {
let centers = calcBands(bands);
let filters = [];
for (let i = 0; i < bands; i++) {
let filter = context.createBiquadFilter();
filter.type = "peaking";
filter.frequency.value = centers[i];
filter.Q.value = Math.SQRT1_2;
if (i > 0) {
filters[i - 1].connect(filter);
}
filters.push(filter);
}
return filters;
}
The thing is I tried doing some research and I found out that there are ISO standards and other things, but I just can't do the maths of it.
All I can understand from this is that:
This is calculated using octave bands either 1 or 1/3, etc in base 2
Graphic EQ's usually have 6 to 31 bands
Middle C (a.k.a A4) equals to 440Hz
Nyquist Frequency is sampleRate / 2
Can anyone please help me?
Feel free to correct me if I'm wrong
references:
https://www.cross-spectrum.com/audio/articles/center_frequencies.html
https://sound.stackexchange.com/questions/14101/what-is-the-frequency-step-formula-for-10-and-31-band-eqs
http://www.tonmeister.ca/main/textbook/intro_to_sound_recordingch13.html

Javascript: Check array of numbers for number of missing numbers needed to make the array consecutive

Working on some Javascript challenges on Code Signal and I'm having an issue solving this:
Ratiorg got statues of different sizes as a present from CodeMaster for his birthday, each statue having an non-negative integer size. Since he likes to make things perfect, he wants to arrange them from smallest to largest so that each statue will be bigger than the previous one exactly by 1. He may need some additional statues to be able to accomplish that. Help him figure out the minimum number of additional statues needed.
Example
For statues = [6, 2, 3, 8], the output should be
makeArrayConsecutive2(statues) = 3.
Ratiorg needs statues of sizes 4, 5 and 7.
My approach:
Sort the array smallest to largest
Create counter variable to store number of missing numbers
Iterate through array
Subtract [i + 1] element from [i] element
If it equals 1, numbers are consecutive, if not the numbers are not consecutive (increment counter variable)
Return counter variable
Here is my code:
function makeArrayConsecutive2(statues) {
// Sorts array numerically smallest to largest
statues.sort((a, b) => a - b);
let counter = 0;
// If array only contains one number return 0
if(statues.length === 1) {
return 0;
}
/* Iterate through array, subtract the current element from the next element, if it
equals 1 the numbers are consecutive, if it doesn't equal one increment the counter
variable */
for(let i = 0; i <= statues.length -1; i++) {
if(statues[i] !== statues.length -1 && statues[i + 1] - statues[i] != 1) {
counter++;
}
console.log(statues[i]);
console.log('counter : ' + counter);
}
return counter;
}
When statues contains [5, 4, 6] the output is this:
4
counter : 0
5
counter : 0
6
counter : 1
I think the problem is when array is on the last element, in this case 6, it's attempting to look at statues[i + 1] when that element doesn't exist. I added statues[i] !== statues.length -1 to my if statement to address that but it doesn't appear to be working. What's wrong with my code and why is the final element incrementing the counter variable?
I'd approach it by building the target array which goes from the min+1 to the max-1 of the input by ones, excluding members of the input.....
function missingConseq(input) {
let min = Math.min.apply(null, input)
let max = Math.max.apply(null, input)
let result = []
for (i = min+1; i < max; i++) {
if (!input.includes(i)) result.push(i)
}
return result
}
let array = [6, 2, 3, 8]
console.log(missingConseq(array))

Rotate For a Max - JavaScript

Trying to solve this Codewars challenge.
Given a number, we are keeping track of the different (left) rotation results and returning the greatest result.
However, this rotate is different from a typical rotate - because n number of digits will stay static after being rotated, and n increases with each rotate.
So for example, given the number 56789, we'll have:
67895 (6 stays in place, 7 gets rotated to the back)
68957 (6 and 8 stay in place, 9 gets rotated to the back)
68579 (6, 8, and 5 stay in place, 7 gets rotated to the back)
68597 (6, 8, 5, and 9 stay in place - no more rotations can occur)
Then return the max from these values - 68957.
I have the following code:
function maxRot(n) {
let listOfNums = [];
let array = Array.from(n.toString());
let num = 0;
while (num < array.length -1) {
let number = array.splice(num, 1);
array.push(Number(number));
listOfNums.push(Number(array.join("")));
num++;
}
listOfNums.sort((a, b) => b - a);
return listOfNums[0];
}
console.log(maxRot(56789));
But it is failing close to half of the tests on Codewars.
And as you can see, my logic is to splice a number each time and append it to the end of the array, then push the updated array into a listOfNums array, and then sort that array from greatest to least and return the first value.
Not sure what else to try.
Again, here's the link to the challenge.
As #georg pointed out, I forgot to add the original number back into the list.
function maxRot(n) {
let listOfNums = [];
let array = Array.from(n.toString());
let num = 0;
while (num < array.length -1) {
let number = array.splice(num, 1);
array.push(Number(number));
listOfNums.push(Number(array.join("")));
num++;
}
listOfNums.unshift(n);
listOfNums.sort((a, b) => b - a);
return listOfNums[0];
}

Avoid duplicate calculations to optimize time-complexity of nested for loop

Today I was doing a simple challenge on HackerRank with the code below, which is 100% acceptable and works, but I was wondering if there was a way to even further reduce the loops required by eliminating duplicate calculations.
Let me show you visually what's happening, By the time I'm done, my code example is going to be very far down!
The code takes the first number in an array of numbers and adds it to each subsequent number and checks if its divisible by k = 3.
In an array of 6 numbers, that equates to 15 loops, which would be O(nΒ²), meaning that my loops will grow exponentially to the amount of input. 7 numbers would be 21 loops.
P.S., you might be thinking that 6 should be 21 loops, and 7 should be 28, but keep in mind that I'm always taking the current number and adding it to the next, with the exception of the last number.
Visual Breakdown
input: [1, 3, 2, 6, 1, 2]
1+3, 1+2, 1+6, 1+1, 1+2
3+2, 3+6, 3+1, 3+2
2+6, 2+1, 2+2
6+1, 6+2
1+2
Explanation
If you look at the numbers I've put in bold, you'll see they're duplicate calculations. The italics numbers are numbers divisible by k = 3. Now we're getting to my meat of my question. How can I eliminate this duplicate math, which would bring my loops down from 15 to 8 in this particular example. The algorithm would still have a worse case scenario of O(nΒ²), if all the numbers were different, but this would be an optimization nonetheless.
Code Demo
function divisibleSumPairs(k, a) {
let pairs = 0;
for (let i = 0; i < a.length - 1; i++) {
for (let j = i + 1; j < a.length; j++) {
if ((a[i] + a[j])/k % 1 === 0) pairs++;
}
}
console.log(pairs);
}
divisibleSumPairs(3, [ 1, 3, 2, 6, 1, 2 ])
I spent awhile thinking about how I can preprocess the array of numbers to prevent duplicate calculations, then I stepped away for a bit, and came back to the problem with a clear head and a cold drink of water.
Then I thought "What if I preprocess the divisor instead"?
The downside of this approach is that it creates and array of equal size to the divisor, but it does it in O(n) time complexity (screw space complexity, lol)
For this particular example we have 3 loops for the divisor, and 6 loops for the calculation, for a total of 9 loops, which is a savings of 6 loops over the original solution, and an elimination of O(nΒ²).
This results in my function having an overall time complexity of O(n)
function divisibleSumPairs(k, a) {
const mod = new Array(k).fill(0);
let pairs = 0;
for (let i = 0; i < a.length; i++) {
const position = a[i] % k;
pairs += mod[(k - position) % k];
mod[position]++;
}
console.log(pairs);
}
divisibleSumPairs(3, [ 1, 3, 2, 6, 1, 2 ])
Performance Testing
I ran several iterations of my code through a performance test, I was surprised to see how much better a simple for loop compared to forEach and reduce.
for^2: the original code
for: the code in this post
forEach: this post, using forEach instead
reduce: this post, using reduce instead
https://jsperf.com/for-2-vs-for-vs-foreach-vs-reduce/1
To achieve this dynamic problem
Try to store the result in Object lets say sum_map if found this means we have already calculated this sum if not calculate the sum and store the result in map for future reference.
sample snippet:
let d = [1, 3, 2, 6, 1, 2]
const len = d.length
const sum_map = {}
let pairs = 0
for (let i = 0; i < d.length - 1; i++) {
for (let j = i + 1; j < d.length; j++) {
const key1 = `${d[i]}_${d[j]}`
const key2 = `${d[j]}_${d[i]}`
let result = 0
if (sum_map[key1]) {
result = sum_map[key1]
} else if (sum_map[key2]) {
result = sum_map[key2]
} else {
result = d[j] + d[i]
sum_map[`${d[i]}_${d[j]}`] = result
}
if (result % 3 === 0) {
pairs += 1
}
}
}
console.log(pairs)
In order to avoid O(n^2) simple trick is to know that
Example
lets say number you are checking with is 5 and arr = [1,3,2,6,1,2,5]
you will only find sums divisible by the number if its numbers compliment remainder is present.
like for example number pair divisible by 5 are only ones which gives a compliment remainder i.e. 3 % 5 = 2 and 2 % 5 = 3 so the sum will be divisible by 5
so to solve this just find the compliment remainders and choose from them
like say you are 3 nums giving remainder 2 and 4 nums giving remainder 3
so pairs will choose 1 from those 3 nums * choose 1 from those 4 nums
if number is divisible by 5 but if its only 1 its sum will never be divisible.
code snippet:
let d = [1, 3, 2, 6, 1, 2, 5]
const check_div_num = 5
remainder_map = {}
mod_arr = d.map((i) =>{
const rem = i % 5
if(remainder_map[rem]) {
remainder_map[rem] += 1
} else {
remainder_map[rem] = 1
}
return rem
})
const till = Math.floor(check_div_num / 2)
keys = Object.keys(remainder_map)
let pairs = 0
for (let j = 0; j < keys.length; j++) {
const key = keys[j]
if(key === '0' && remainder_map["0"] > 1) {
pairs += remainder_map[key] / 2
continue
}
if(Number(key) <= till) {
let compliment = remainder_map[check_div_num - Number(key)]
const compliemnt_key = check_div_num - Number(key)
if(compliment) {
pairs += remainder_map[key]*remainder_map[compliemnt_key.toString()]
} else {
continue
}
} else {
break
}
}
console.log(pairs)
mind here I am only looping till half of 5 i.e. Math.floor(5/2) as we are already checking for their compliment

Categories

Resources