Greedy Algorithm with Maximum Salary Problem? - javascript

I am trying to solve the below problem. I think my solution is working well. But, the system in which I am trying to upload the solutions, is not accepting my solution. Probably some tests are failing. May I know what am I missing?
The problem is:
As the last question of a successful interview, your boss gives you a few pieces of paper with numbers on it and asks you to compose a largest number from these numbers. The resulting number is going to be your salary, so you are very much interested in maximizing this number. How can you do this?
Sample 1
Input:
2
21 2
Output: 221
Sample 2
Input:
3
23 39 92
Output: 923923
My solution is:
function MaxSallary(nums) {
let maxSize = Math.max(...nums).toString().length;
let newArr = [];
nums.map((num) => {
while (num.toString().length < maxSize) {
num = num.toString().concat(num.toString().split("").slice(-1)[0]);
}
newArr.push(Number(num));
});
finalArr = [];
while (newArr.length > 0) {
let minIndex = newArr.indexOf(Math.max(...newArr));
newArr.splice(minIndex, 1);
finalArr.push(...nums.splice(minIndex, 1));
}
return finalArr.join("");
}
console.log(MaxSallary([2, 12, 34, 11, 43, 21, 5]));

You want to know in which order the numbers should be concatenated, so that, once parsed back to a number, the result is the highest possible. Reworded this way, it looks like we should sort the array first.
When comparing two numbers a and b, to know which one should come first, we need to know which one is higher between ${a}${b} and ${b}${a}:
.sort((a, b) => parseInt(`${b}${a}`, 10) - parseInt(`${a}${b}`, 10)))
.sort mutates the array (and returns it), so I'm cloning it first.
function MaxSallary(nums) {
const salary = [...nums]
.sort((a, b) => parseInt(`${b}${a}`, 10) - parseInt(`${a}${b}`, 10))
.join("");
return salary;
}
console.log(MaxSallary([21, 2]));
console.log(MaxSallary([23, 39, 92]));
console.log(MaxSallary([2, 12, 34, 11, 43, 21, 5]));

Related

How to use JavaScript reduce() method to add ranges between an interval

The program below is used to add the four ranges with the ranges array and display the result 139.
(20 - 1) + (11930 - 11904) + (12020 - 11931) + (6 - 1) = 139
However, I cannot quite understand the use of the start parameter and the zero (0) in the code.
Help me understand how they are processed in the program.
Thanks
let ranges = [[1,20],[11904, 11930],[11931, 12020],[1,6]];
console.log(ranges.reduce((start, [from, to]) =>{
return start + (to - from);
}, 0));
Basics of reduce
Let's start simpler. Array.prototype.reduce is a way of folding multiple values into a single one. It accepts two arguments:
a function of two values
a starting point
This function should accept a value of your target type and the next element of your array, and it should return an element of your target type.1
So, what if we wanted to calculate the following?
[19, 26, 89, 5] .reduce (
(a, b) => a + b,
0
)
Our function is simply addition, adding its two parameters together. And our initial value is 0.
Here's how it is processed:
[19, 26, 89, 5] .reduce ((a, b) => a + b, 0)
`-------+-----' |
+-----------|---------+
| |
| +--+
| |
V ,-------,
[19, 26, 89, 5] | 0 | (0 + 19) ==> 19
== ==
[19, 26, 89, 5] | 19 | (19 + 26) ==> 45
== ==
[19, 26, 89, 5] | 45 | (45 + 89) ==> 134
== ==
[19, 26, 89, 5] | 134 | (134 + 5) ==> 139
= = ^
`----- final result
reduce is extremely powerful. Functions such as map, filter, find and others can easily be built atop reduce, if we so chose.
The callback function
Note that the target type and the array element type do not have to be the same. Your function just has to accept an argument of the target type and one of the element type and combine them into a new target type.
So perhaps we would like to fold an array of Strings into a number representing the total number of characters in all of them. We could write this as
['Four', 'score', 'and', 'seven', 'years', 'ago']
.reduce ((total, word) => total + word.length, 0)
//=> 25
Our callback accepts a number and a word and returns a new number by adding the length of the word to the input number.
Or you could fold these into an object:
['Four', 'score', 'and', 'seven', 'years', 'ago']
.reduce ((all, word) => ({...all, [word]: word.length}), {})
//=> {Four: 4, score: 5, and: 3, seven: 5, years: 5, ago:3}
Handling your ranges
For your sample case, the array elements are ranges represented as two element arrays, [from, to].
We could write it like this:
ranges.reduce((start, range) =>{
return start + (range.to - range.from);
}, 0)
but JS's parameter destructuring gives us a slightly nicer syntax
ranges.reduce((start, [from, to]) =>{
return start + (to - from);
}, 0)
1 This is an oversimplification. The callback function can actually take two additional parameters, but they aren't relevant here, and I would suggest avoiding them as much as you can.
Let f be the function defined for the first argument of the reduce.
f gets called for each element of ranges, where each element is of the form [from, to], and returns start + (to - from), where start is the value returned from the previous call to f.
For the first call to f, there is no previous value, so it has to be supplied in the call to reduce: hence, the second argument of reduce being 0.

Improve performance of this combination algorithm?

I'm working on this kata from Codewars. The task is:
Given a certain number, how many multiples of three could you obtain with its digits?
Supose that you have the number 362. The numbers that can be generated from it are:
362 ----> 3, 6, 2, 36, 63, 62, 26, 32, 23, 236, 263, 326, 362, 623, 632
I've written the following recursive function to calculate all possiblities:
const findMult_3 = (num) => {
const powerset = (set) => {
const combinations = []
const combine = (prefix, chars) => {
for (let i = 0; i < chars.length; i++) {
const newPrefix = parseInt(prefix + chars[i])
if (!combinations.includes(newPrefix)) {
combinations.push(newPrefix)
} else {
console.log('encountered duplicate')
}
combine(newPrefix, chars.filter((x, ind) => ind !== i))
}
}
combine('', set)
return combinations.sort((a, b) => a - b)
}
const allCombinations = powerset(num.toString().split(''))
const factorsOfThree = allCombinations.filter(x => x % 3 === 0).filter(x => x !== 0)
return [factorsOfThree.length, factorsOfThree.pop()]
}
findMult_3(43522283000229)
I noticed early on that I was encountered a lot of duplicate cases, hence the console.log('encountered duplicate') flag.
Execution of this algorithm is taking an extremely long time for large numbers, eg 43522283000229.
How can I improve the performance of this code, or should it be scrapped entirely?
With most coding katas, the choice of algorithm is far more important that implementation details, but before we get to that, let me point out the most glaring flaw of your implementation:
if (!combinations.includes(newPrefix)) {
combinations.push(newPrefix)
} else {
console.log('encountered duplicate')
}
combine(newPrefix, chars.filter((x, ind) => ind !== i))
combinations is an array, and includes works by iterating over the array and checking every element. That is, to check whether an element is a duplicate, you are comparing it with every previously encountered combination. Since there are exponentially many of those, this is going to be very slow. If you used a dictionary object or Map instead, your code would be far faster.
Also, did you notice you are proceeding with generating combination even if the combination is a duplicate? That's redundant.
So the cheap improvement would be:
const combinations = {};
if (combinations[prefix]) {
// duplicate, do nothing
} else {
combinations[prefix] = true;
combine(...);
}
The real improvement however is choosing a better algorithm. If you make use of the mathematical structure of the problem, you may be able to find the number of solutions without iterating over them all.
The following insights might help:
a number is divisible by three if and only if the sum of its digits is.
a sum of digits is divisible by 3 if and only if the sum of their remainders when divided by 3 is 0.
the order of digits in the input does not matter
One (first) optimization would be to only check or generate numbers where the sum of the digits is divisible by 3, since only those numbers are divisible by 3.
So in your example (362) you could skip all combinations with 3 and 2, 6 and 2 and all possible combinations with the 3 digits (because the sum of the 3 digits is not divisible by 3).
For the larger number (43522283000229) you can skip a lot more, for example all combinations with digits:
43, 35, 52, ...
435, 352, .., 283, ...
4352 (thus, including all possible combinations of those 4 digits), ...
43522, ...
43528, 43529, ...
43528309, ...
and so on
Possible algorithm:
Original number: 43522283000229
First, sort the digits: 00022222334589
Then find all distinct combinations of digits where
their sum is a multiple of 3:
left to right:
1 digit : 3, 9
2 digits: 03, 09, 24, 33, 39, 45, 48, ...
3 digits: 003, 009, 024, 033, 039, 222, 234, ...
n digits ...
Now, for all above numbers create every possible combination with their
digits, skip those with leading zeros.:
3, 9, 30, 90, 24, 42, 33, 39, 93, 45, 54, 48, 84, 300, 900,
204, 240, 402, 420, 309, 390, 903, 930, 222, 234, 243, ...
We don't have to check for division by 3, they all match.
We don't have to check for duplicates.
You could then sort the resulting list if needed.

Round number down to nearest power of ten

I have a number and I need to round it down to the nearest power of ten. It seems like this should be possible without a whole bunch of if statements or using recursion or looping, but I don't know the most elegant way to do it. In case it's unclear what I mean, here are some examples:
f(1) === 1
f(5) === 1
f(15) === 10
f(43) === 10
f(456) === 100
f(999) === 100
To clarify: I do not need nearest multiple of 10 (not: 10, 20, 30...), but nearest power of ten (10, 100, 1000...).
Edit: To my knowledge this is not a duplicate question. Please stop closing as a duplicate of a question asking about rounding to the nearest multiple of ten. This question is unrelated to the linked question, because it asks about rounding to the nearest power of ten. If you would like to close this question as a duplicate, please find and link a duplicate question. The question which is currently linked was commented about 30 seconds after I posted the question, by someone who did not read the entire question and merely commented complaining about it being a duplicate. That person has since deleted his comments (after realizing he was wrong), although you can see comments by myself and someone else both pointing out that this is not a duplicate.
You could take the logarithm of 10 and take the integer value for the power of 10.
function f(v) {
return Math.pow(10, Math.floor(Math.log10(v)));
}
console.log(f(1)); // 1
console.log(f(5)); // 1
console.log(f(15)); // 10
console.log(f(43)); // 10
console.log(f(456)); // 100
console.log(f(999)); // 100
Simply get the length of the number(by converting Number into a string) and then generate the result by taking the power of 10(where the exponent is length - 1).
function generateNum(v) {
return Math.pow(10, v.toString().length - 1);
}
var data = [1, 5, 15, 43, 456, 456, 999];
data.forEach(function(v) {
console.log(generateNum(v));
})
function generateNum(v) {
return Math.pow(10, v.toString().length - 1);
}
FYI : In case number includes decimal part then you need to avoid decimal part by taking the floor value.
function generateNum(v) {
return Math.pow(10, Math.floor(v).toString().length - 1);
}
Here's a variant that works for negative numbers:
let round10 = v => Math.pow(10, Math.floor(Math.log10(Math.abs(v)))) * Math.pow(-1, v < 0);
You can do it in the following way
function f(num){
let count = 0;
while(num > 1){
count ++;
num/= 10;
}
return Math.pow(10, count-1) * (Math.round(num) ? 10: 1);
}
console.log(f(453));
f = n => +("1"+"0".repeat((""+n).length-1));
or:
f = n => +(""+n).split("").map((_,i)=>i?"0": "1").join("");

Decomposing a value into results of powers of two

Is it possible to get the integers that, being results of powers of two, forms a value?
Example:
129 resolves [1, 128]
77 resolves [1, 4, 8, 64]
I already thought about using Math.log and doing also a foreach with a bitwise comparator. Is any other more beautiful solution?
The easiest way is to use a single bit value, starting with 1 and shift that bit 'left' until its value is greater than the value to check, comparing each bit step bitwise with the value. The bits that are set can be stored in an array.
function GetBits(value) {
var b = 1;
var res = [];
while (b <= value) {
if (b & value) res.push(b);
b <<= 1;
}
return res;
}
console.log(GetBits(129));
console.log(GetBits(77));
console.log(GetBits(255));
Since shifting the bit can be seen as a power of 2, you can push the current bit value directly into the result array.
Example
You can adapt solutions from other languages to javascript. In this SO question you'll find some ways of solving the problem using Java (you can choose the one you find more elegant).
decomposing a value into powers of two
I adapted one of those answers to javascript and come up with this code:
var powers = [], power = 0, n = 129;// Gives [1,128] as output.
while (n != 0) {
if ((n & 1) != 0) {
powers.push(1 << power);
}
++power;
n >>>= 1;
}
console.log(powers);
Fiddle
Find the largest power of two contained in the number.
Subtract from the original number and Add it to list.
Decrement the exponent and check if new 2's power is less than the number.
If less then subtract it from the original number and add it to list.
Otherwise go to step 3.
Exit when your number comes to 0.
I am thinking of creating a list of all power of 2 numbers <= your number, then use an addition- subtraction algorithm to find out the group of correct numbers.
For example number 77:
the group of factors is { 1,2,4,8,16,32,64} [ 64 is the greatest power of 2 less than or equal 77]
An algorithm that continuously subtract the greatest number less than or equal to your number from the group you just created, until you get zero.
77-64 = 13 ==> [64]
13-8 = 7 ==> [8]
7-4 = 3 ==> [4]
3-2 = 1 ==> [2]
1-1 = 0 ==> [1]
Hope you understand my algorithm, pardo my bad english.
function getBits(val, factor) {
factor = factor || 1;
if(val) {
return (val % 2 ? [factor] : []).concat(getBits(val>>1, factor*2))
}
return [];
}
alert(getBits(77));

In JavaScript, get high numbers in a set of numbers, based on variance

I have JavaScript code that grabs the highest part of a series of numbers.
Let's say I have a series of 10 numbers, such as 1, 3, 4, 4, 7, 15, 16, 16, 30, 31. Let's say I want to grab the highest 25% (rounding up), then I end up with 16, 30, 31. But just by looking at the results, the 16 doesn't "belong". Ideally I'd like to get a result of 30, 31 instead. (Note that this is just an example; in reality, I have sets of hundreds of numbers, and their numbers are pretty random.)
Is there a better way to grab the highest portion of a series of numbers, based on variance? And then all I'd have to do is specify the value of the variance until I get the numbers I want from multiple sets of numbers.
var sequence = [14, 28, 20, 11, 7, 15, 18]; //all the numbers
var generalCap = Math.round(sequence.length / 4); //25%
var currenthighs = [];
function calculatehighs(strictness) {
for(var i=0;i<sequence.length;i++) {
var current = sequence[i];
if(currentHighs.length > generalCap - 1) {
var match = false;
for(var n=0;n<currenthighs.length && !match;n++) {
if(current > currenthighs[n]) {
currenthighs.splice(n); //delete one
currenthighs.push(current);
match = true;
}
}
} else {
currenthighs.push(current); //if under 25% simply put it in the array
}
}
for(i=0;i<currenthighs.length;i++) {
var difference = 0;
for(n=0;n<currenthighs.length;n++) {
difference += Math.abs(currenthighs[i] - currenthighs[n]);
}
if(difference > currenthighs[i] / strictness) {
currenthighs.splice(i);
}
}
}
Here this might work.
I guess you want around top 25% of the set, and also the variance should be low. Define a function of variance of the subset and its size. (eg. *variance+abs(sizeOfSubset-idealSize) ).
Now start from smallest acceptable subset and iterate upto largest acceptable subset. Then pick subset that minimizes your function. The key here is to pick the function correctly, it will have a significant effect on your results. Also, you might want to normalize the variance as variances of larger numbers will be larger than that of smaller numbers. So, a sample objective function (i.e. the function to be minimized) could be
variance/mean + abs(sizeOfSubset-idealSize)/totalSize.

Categories