Skip to content

Instantly share code, notes, and snippets.

@ben196888
Last active October 2, 2021 06:01
Show Gist options
  • Save ben196888/bd736a480f10c2db0ecaca94a4919647 to your computer and use it in GitHub Desktop.
Save ben196888/bd736a480f10c2db0ecaca94a4919647 to your computer and use it in GitHub Desktop.
const SCALE = 1000;
const rounds = 10;
// Standard Normal variate using Box-Muller transform.
function randn_bm() {
let u = 0, v = 0;
while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
while(v === 0) v = Math.random();
let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
num = num / 10.0 + 0.5; // Translate to 0 -> 1
if (num > 1 || num < 0) return randn_bm() // resample between 0 and 1
return num
}
// ability: 0-1, kpi: 0-6, years: number
const Hire = (join) => ({ ability: randn_bm(), kpi: 0, years: 0, join, pr: -1 });
// init
let engineers = [];
let promoted = [];
for (let i = 0; i < rounds; i++) {
// new hire
const n = SCALE - engineers.length;
const newHire = [...new Array(n)].map(() => Hire(i));
engineers = [...engineers, ...newHire];
engineers.sort((a, b) => b.ability - a.ability);
// average ability
// const avgAbly = engineers.map(eng => eng.ability).reduce((a, b) => a + b, 0) / engineers.length;
// console.log(`In the ${i + 1} year, ${engineers.length} ppl average ability is ${avgAbly.toFixed(5)}`);
// score
let idx = 0;
for (let cnt = 0; cnt < 10/100 * SCALE; cnt++, idx++) {
engineers[idx].kpi += 4;
}
for (let cnt = 0; cnt < 25/100 * SCALE; cnt++, idx++) {
engineers[idx].kpi += 2;
}
for (let cnt = 0; cnt < 50/100 * SCALE; cnt++, idx++) {
engineers[idx].kpi += 1;
}
const window = SCALE/100;
engineers.forEach((eng, id) => {
eng.pr = 99 - Math.floor(id/window);
});
engineers.forEach(eng => {
eng.years++;
});
// promote
const pplShouldPromote = engineers.filter(eng => eng.kpi >= 6);
engineers = engineers.filter(eng => eng.kpi < 6);
// list promoted
promoted = [...promoted, ...pplShouldPromote];
// average promotion years
// const avgYrs = promoted.map(eng => eng.years).reduce((a, b) => a + b, 0) / promoted.length;
// console.log(`After ${i + 1} years, ${promoted.length} ppl get promoted, average promotion year: ${avgYrs}`);
// max engineer year
// const maxYrs = Math.max(...engineers.map(eng => eng.years));
// console.log(`After ${i + 1} years, max ${maxYrs} years engineer is not promoted yet`);
}
function getPRSummary(promotedList) {
promotedList.sort((a, b) => b.pr - a.pr);
for (let i = 99; i >= 0; i--) {
const prPromoted = promotedList.filter(eng => eng.pr === i);
if (prPromoted.length === 0) {
console.log(`PR ${i}: no one is promoted.`);
continue;
}
prPromoted.sort((a, b) => a.years === b.years ? a.ability - b.ability : a.years - b.years);
const avgYrs = prPromoted.map(eng => eng.years).reduce((a, b) => a + b, 0) / prPromoted.length;
const avgAbly = prPromoted.map(eng => eng.ability).reduce((a, b) => a + b, 0) / prPromoted.length;
const minYrs = Math.min(...prPromoted.map(eng => eng.years));
const minAbly = prPromoted.find(eng => eng.years === minYrs).ability;
const maxYrs = Math.max(...prPromoted.map(eng => eng.years));
const maxAbly = prPromoted.find(eng => eng.years === maxYrs).ability;
const mediumYrs = prPromoted.length % 2 === 0 ?
(prPromoted[prPromoted.length/2].years + prPromoted[prPromoted.length/2 - 1].years) / 2 :
prPromoted[(prPromoted.length-1)/2].years;
const mediumAbly = prPromoted.length % 2 === 0 ?
(prPromoted[prPromoted.length/2].ability + prPromoted[prPromoted.length/2 - 1].ability) / 2 :
prPromoted[(prPromoted.length-1)/2].ability;
console.log(`PR ${i}: total ${prPromoted.length} ppl are promoted. Avg ${avgYrs.toFixed(2)}(${avgAbly.toFixed(5)}), min ${minYrs}(${minAbly.toFixed(5)}), max ${maxYrs}(${maxAbly.toFixed(5)}), medium ${mediumYrs}(${mediumAbly.toFixed(5)}) years`);
}
}
console.log(`Total ${promoted.length} ppl are promoted in ${rounds} years`);
getPRSummary(promoted);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment