0

Consider the following problem http://potw.quinnftw.com/problem/2015/1/

Will has been feeling pretty lonely lately. In order to help himself he creates a dating website. Being a computer scientist, he comes up with an interesting way of computing how well one person would match with another.

Based on the information entered, Will creates a 32 bit unsigned integer which acts as a mask for the person's data. In order to compute compatibility between a couple Will checks how many similar bits they have. The more similarity the better the match. One also has to keep in mind though, opposites attract.

Your goal is to design an algorithm that displays the couple with the highest compatibility level, and the lowest compatibility level.

The problem in summary is to type in some integer N, and then after type in N amount of names with corresponding 32 bit unsigned integers.

The goal is to design an algorithm that displays the couple with the highest compatibility level, and the lowest compatibility level.

You can view the details in the link. It isn't too much of a read.

Consider the sample input

5 
Danniel 116077289 
Bowman 2316887705 
Cleveland 2186347654 
Marinda 2662238983 
Viviana 3393681530

And the output

Bowman Cleveland 
Marinda Viviana

I'm having a bit of a hard time understanding the problem. Is it telling me to match based on occurrences of numbers? Basically am I tracking how many integers in one int is the same in another names int? Is the output simply achieved by taking the pair of names with the most common numbers? and then the second pair with the least common numbers?

Any insight how I can understand this problem would be nice. My impression of it so far is to simply match two names with the most occurrences of a number. However the 32 bit part is confusing me, I'm not sure what this part means.

I would like to do this with javascript or java, but I don't think coding it will be too much of an issue once I understand exactly what the question is asking.

Thank you!

Consider one of the solutions here http://potw.quinnftw.com/solution/17/

The part I am having trouble understanding is this for loop

public static int getScore(long x, long y) {
        int score = 0;
        for (long i = x ^ y; i > 0; i = i >> 1)
            if (i-1 == (i & (i-1)))
                score++;
        return 32 - score;
    }

I`m confused about why I am using xOR here and diving by 2 each time I iterate the loop.

Community
  • 1
  • 1
Muntasir Alam
  • 1,777
  • 2
  • 17
  • 26
  • 32 bit simply means a number in the range 0 to (2^32 - 1) although a signed integer uses negative numbers so the maximum positive number is about half of that. If you have to write this in java, use a long to guarantee enough "space" – Olipro Jul 22 '16 at 01:37
  • So how should I match people with names? Do I just look at which digits in each number and match them up with another name that has the most "similar digits? i.e 123456 would be matched up with 1234567 most likely? – Muntasir Alam Jul 22 '16 at 01:40
  • Your problem isn't clear whether it wants the most matching decimal digits or the physically closest number. (e.g. if I input 3999 is 4000 the closest or 3991)... but the website link is, hah, I didn't see it. You need to perform a bitwise & of the values and then take the result that has the largest number of bits that are set (note, NOT the highest number) – Olipro Jul 22 '16 at 01:42
  • @Olipro - The exercise says ***checks how many similar bits they have*** which is pretty clear that they want to compare binary bits. – jfriend00 Jul 22 '16 at 01:47
  • @jfriend00 try reading my entire comment. Granted I did edit it, but that was 5 minutes before you replied – Olipro Jul 22 '16 at 17:19
  • I got it!! Only took me 5 hours!! – Muntasir Alam Jul 22 '16 at 23:39

3 Answers3

3

If you read the problem carefully, this is a question about "how many identical bits do two 32 bit numbers have). You have to examine the number in binary form (where you can look at each bit) and compare bits between the numbers that represent each person's characteristics.

So, one person's number might be represented in bary as

01001101110011010011010100101111 

and another as

01111100010011010011010100010111 

To see how well they match, you have to see how many bits have the same value where the bit in the same corresponding position is both 0 or both 1. Add one to the matching score for each bit that has the same value.

Since this is some sort of learning exercise, I won't just hand you the code, but will point you to a reference on manipulating bits in Javascript.


As it turns out, there are multiple phases to this problem:

  1. Compare the mask number for two people and generate a match score.
  2. For all possible user combinations, compute all match scores.
  3. Find the highest match score, add them to the final results, remove them from all the match scores you have so far and find the next highest match. Repeat until there are 0 or 1 people left.

Output from my solution (which has not yet been linked so the OP can work the problem themselves):

Visually in Binary
00000110111010110011001011101001 Danniel
10001010000110001110011010011001 Bowman
10000010010100010000010010000110 Cleveland
10011110101011101000101100000111 Marinda
11001010010001110111100001111010 Viviana

All Comparisons (sorted)
Bowman,Cleveland:19
Danniel,Viviana:17
Danniel,Bowman:16
Cleveland,Viviana:16
Danniel,Cleveland:15
Danniel,Marinda:15
Bowman,Marinda:15
Bowman,Viviana:15
Cleveland,Marinda:14
Marinda,Viviana:12

Best Matches
Bowman,Cleveland: 19
Danniel,Viviana: 17

Here's a hint for comparing if a given bit is the same in two numbers:

var bitmask = 1;      // start with lowest bit
var num1 = 116077289;
var num2 = 2316887705;
if ((num1 & bitmask) === (num2 & bitmask)) {
    // the bit represented by bitmask is the same in both numbers
}

Next hint: If bitmask is 2, then you're comparing the second bit. If bitmask is 4, you're comparing the third bit, 8 then the fourth bit, 16, the fifth bit and so on up to the 32nd bit. Add a counter and you can count the bits that match.


Here's a working solution:

// function used to make display prettier
function zeroPadLeft(str, len) {
    while (str.length < len) {
        str = "0" + str;
    }
    return str;
}

function compareBits(num1, num2) {
    // score is the number of matching bits
    var score = 0;
    // start with first bit
    var mask = 1;
    // create rotating mask to individually compare each of the lowest 32 bits
    for (var i = 0; i < 32; i++) {
        // if this bit has the same value, increase the score
        if ((num1 & mask) === (num2 & mask)) {
            ++score;
        }
        // advance mask to next bit with shift left operator
        mask = mask << 1;
    }
    return score;
}

// input data
var data = [
    {name:"Danniel", value:116077289}, 
    {name:"Bowman", value:2316887705}, 
    {name:"Cleveland", value:2186347654}, 
    {name:"Marinda", value:2662238982}, 
    {name:"Viviana", value:3393681530}
];

// show the starting data in binary so we can see a visual representation of the actual bits
log("<b>Visually in Binary</b>");
data.forEach(function (item) {
    log(zeroPadLeft(item.value.toString(2), 32) + "  " + item.name);
});

// record the score of all possible combinations in the scores array of objects
log("<hr>");
log("<b>All Comparisons</b>");
var scores = [];
for (var j = 0; j < data.length; j++) {
    for (var k = j + 1; k < data.length; k++) {
        var score = compareBits(data[j].value, data[k].value);
        // record the score and two names as an object inserted into an array
        scores.push({
            name1: data[j].name,
            name2: data[k].name,
            score: score
        })
    }
}

// sort by best score to make it easier to find the highest score
scores.sort(function (a, b) {
    return b.score - a.score;
});
// output sorted scores so we can see them visually
scores.forEach(function (item) {
    log(item.name1 + "," + item.name2 + ":" + item.score);
});

// now find the top scores with no person repeated
log("<hr>");
log("<b>Best Matches</b>");
// namesUsed keeps track of which names have already found a high score so we don't use them again
var namesUsed = {};
while (scores.length > 0) {
    var bestItem = scores.shift();
    // if either of these names has already been used, then skip this score
    if (namesUsed[bestItem.name1] || namesUsed[bestItem.name2]) {
        continue;
    }
    log(bestItem.name1 + "," + bestItem.name2 + ": " + bestItem.score);
    namesUsed[bestItem.name1] = true;
    namesUsed[bestItem.name2] = true;
}
body {font-family: "Courier New";}
<script src="http://files.the-friend-family.com/log.js"></script>

Explanation of comparing bits

The key part is counting the bits that are the same in two numbers:

function compareBits(num1, num2) {
    // score is the number of matching bits
    var score = 0;
    // start with first bit
    var mask = 1;
    // create rotating mask to individually compare each of the lowest 32 bits
    for (var i = 0; i < 32; i++) {
        // if this bit has the same value, increase the score
        if ((num1 & mask) === (num2 & mask)) {
            ++score;
        }
        // advance mask to next bit with shift left operator
        mask = mask << 1;
    }
    return score;
}

This is what I think is the simplest to understand implementation (not the fastest). Basically what it does is define a mask number with an initial value of 1. When we logically AND the mask number with each of our values, we isolate a single bit in each number. We can then compare that remaining single bit to see if they are equal. If so, increase our score. Then, shift the mask left by one place so we can look at the next bit. Repeat that 32 times and we've compared each of the 32 bits, counting how many have the same value.


If you want to see how obtuse bit manipulation can get, here's a pretty fast algorithm called the Hamming weight implementation:

function countSimilarBitsHamming(num1, num2) {
    // xor sets a bit to 0 if both are the same and 1 if different
    // so if we xor and then negate, we get bits that are the same
    var sameBits = ((~(num1 ^ num2)) & 0xFFFFFFFF) >>> 0;
    sameBits = sameBits - ((sameBits >> 1) & 0x55555555);
    sameBits = (sameBits & 0x33333333) + ((sameBits >> 2) & 0x33333333);
    return (((sameBits + (sameBits >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}

Here's an even slower implementation, but the bit counting part is a simple string count:

function countBitsString(num1, num2) {
    // xor sets a bit to 0 if both are the same and 1 if different
    // so if we xor and then negate, we get bits that are the same
    var sameBits = ((~(num1 ^ num2)) & 0xFFFFFFFF) >>> 0;
    var str = sameBits.toString(2).replace(/0/g, "");
    return str.length;
}

Steps:

  1. num1 XOR num2 - This generates a result where a bit is set to 0 if both corresponding bits in num1 and num2 had the same value or 1 if they were different values.
  2. That XOR value is the inverse of what we want so we negate it with the ~ operator. Now we have a 1 wherever the two numbers had equal bits.
  3. Because there are more than 32 bits in the negated value, we and it with the max 32-bit value to zero out the higher bits.
  4. Then because Javascript only deals with signed values, but we want unsigned values, we use a trick of doing a zero fill right shift, but with a 0 shift value >>> 0 and this will clear the sign bit.
  5. Now there is a list of matching bits.
  6. Then, a really simple way to count them is to convert to string represeantation of binary and remove the zeroes. All that will be left is the 1s in the string so just see how long the string is and that's how many 1s where in the string.
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I see. So basically I should try to match one binaries numbers digits with another. Should I get the binary number then split each digit into an array or something? How else would I compare digit by digit of X amount of name values – Muntasir Alam Jul 22 '16 at 01:48
  • @cresjoy - You can use binary operators to directly evaluate/compare individual bits. No need to convert to binary string in an array (though you could do that too, but it is not necessary). – jfriend00 Jul 22 '16 at 01:52
  • Great, the other post mentioned the & operator, which I'll try and let you know by tommorow hopefully how it worked if you dont mind! – Muntasir Alam Jul 22 '16 at 01:53
  • How would this work in javascript? Its not like I can ask the user like I can in java by doing a scanner if they want to put in 5 names, I mean I can ask call a function which takes the number of names you want to put in I guess but then I would have to manually type in those names anyway.. – Muntasir Alam Jul 22 '16 at 02:21
  • @cresjoy - In a browser environment, you can create a web page that has the appropriate form elements for entering the data. Then you can have some Javascript that runs when the user clicks a button. That Javascript can grab all the data out of the form and run it's comparisons. – jfriend00 Jul 22 '16 at 02:28
  • @cresjoy - Starting with a data table, I have the algorithm all worked out. I can post it if you want, though it's probably better for you to try to work it out yourself. – jfriend00 Jul 22 '16 at 02:58
  • Heres what I'm doing, I already used some splitting and shifting to make a prompt n amount of times. First I store the name in an array and the id in an array. Cool. Now I will take each combo of everything in the array anded with each other and push all the combinations into a new array which has the anded values of all the combos. The only issue I can think of know is remembering which two names that actually corresponds too. – Muntasir Alam Jul 22 '16 at 03:01
  • @cresjoy - An item number anded with another item number will not give you the count of matching bits. There's a bit more to it than that. – jfriend00 Jul 22 '16 at 03:23
  • @cresjoy - I added the output from my algorithm to the end of my answer so you can see the steps. – jfriend00 Jul 22 '16 at 03:25
  • Can I have a hint? Right now I have all the names and Ids in two seperate arrays. Where should I go from here? I know I need to and each of the values and find which of the two values anded have the most and least ones. Then I will need the names of those. – Muntasir Alam Jul 22 '16 at 03:26
  • @cresjoy - I'd say your next step is to write a function for counting the number of identical bits in two numbers. That's how you get a score for a combination. – jfriend00 Jul 22 '16 at 03:39
  • Alright Ill try doing that tommorow, its over midnight here. I'll keep you updated. Goodnight – Muntasir Alam Jul 22 '16 at 03:58
  • @cresjoy - I added a hint for comparing the value of a specific bit in two numbers. – jfriend00 Jul 22 '16 at 04:05
  • Why am I anding the number with one don't I and the two values each other and check for common ones? As said in the answer below – Muntasir Alam Jul 22 '16 at 10:43
  • I posted one of the answers, mabye you could help me go through that for loop then I can translate it into javascript easily – Muntasir Alam Jul 22 '16 at 12:48
  • How does your algorithm work though? How does anding with one help? – Muntasir Alam Jul 22 '16 at 14:57
  • 1
    @cresjoy - Anding with 1 checks the first bit. Anding with 2 checks the second bit. Anding with 4 checks the 3rd bit, Anding with 8 checks the 4th bit and so on up to the 32nd bit. Keep a counter as you go and you've got the count of all the bits that match. – jfriend00 Jul 22 '16 at 15:34
  • oh.my.god your a genius!! – Muntasir Alam Jul 22 '16 at 15:37
  • @cresjoy - Do you want to figure it out yourself or do you just want one or more answers in Javascript that you can study? – jfriend00 Jul 22 '16 at 15:37
  • @cresjoy - There are tricks with xor that find all the matching bits in one step, but you still have to count the bits. No need to use that for your first and simplest implementation. – jfriend00 Jul 22 '16 at 15:38
  • I think I can do it! Could you expand on that java for loop though, I cant rack my brains around it – Muntasir Alam Jul 22 '16 at 15:39
  • @cresjoy - I've already written three different implementations (for my own entertainment). There are many ways to do it because of various bit manipulation tricks. You'd be very surprised how obtuse some of the faster implementations are. – jfriend00 Jul 22 '16 at 15:42
  • I'm confused on how to organize the data for names and values, perhaps I could just take a look at your solution and learn – Muntasir Alam Jul 22 '16 at 16:41
  • Its ok if you are busy, but could you take a look at my own solution that I tried without looking at yours, I'm almost there! I just don't know how to match the name. Thanks – Muntasir Alam Jul 22 '16 at 17:43
  • Ah, I think I got the jist of your solution. I skimmed over it and will try it once I get home. Appreciate it – Muntasir Alam Jul 22 '16 at 18:10
  • @cresjoy - Do not put your own attempt at a solution in your question. Questions should remain questions, answers go in answers. If you want, you can document your current attempt at a solution by filing your own answer. But, please remove it from your question - that is not how stack overflow should be used. – jfriend00 Jul 22 '16 at 18:15
  • @cresjoy - I thought you were going to put your solution into an answer. I don't see it there. – jfriend00 Jul 22 '16 at 18:27
  • added it, I'll try what you suggested – Muntasir Alam Jul 22 '16 at 18:31
  • Ok I did what you suggested and now I made an array scores like you did and pushed in each of the names and the score. How do I actually find which one has the highest score now within an object of arrays? I dont quite understand where you did that – Muntasir Alam Jul 22 '16 at 18:59
  • @cresjoy - In my solution, I sorted the array of objects with a custom sort callback. It's in the code in my answer. – jfriend00 Jul 22 '16 at 21:07
  • in your .sort method what does a and b represent? – Muntasir Alam Jul 22 '16 at 21:53
  • Please read the description of a custom compare function for `.sort()` here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort `a` and `b` are two items from your array being compared. – jfriend00 Jul 22 '16 at 22:31
  • I'm so close to the solution, for some reason my count is always reaching 32... I have the exact same compareBit function as you as well.. – Muntasir Alam Jul 22 '16 at 23:01
  • I DID IT YESSSSSSSSSSSSS – Muntasir Alam Jul 22 '16 at 23:34
  • AFTER 4 HOURS OF BRAIN RACKING – Muntasir Alam Jul 22 '16 at 23:34
  • Crazy to think how some people solved this in like 20 minutes, atleast thats what it says on that website – Muntasir Alam Jul 22 '16 at 23:41
  • I have a new coding puzzle posted, if you are interested mabye you could take a look at it too. http://stackoverflow.com/questions/38542069/find-separation-values-from-a-starting-node – Muntasir Alam Jul 23 '16 at 13:00
1

"Bit" is short for "binary digit". Therefore, a 32-bit integer is an integer that can be represented using 32 binary digits. An "unsigned" integer cannot be negative, and so it is stored using plain binary instead of using two's complement.

In binary, the input might make more sense.

Danniel   00000110111010110011001011101001
Bowman    10001010000110001110011010011001
Cleveland 10000010010100010000010010000110
Marinda   10011110101011101000101100000111
Viviana   11001010010001110111100001111010

According to the problem, compatibility is measured by the number of identical bits. Therefore, you can use the bitwise operator &. The bitwise AND combines two numbers by comparing bit by bit. If both bits are the same it will put a 1 in the result. If both bits are different, it will put a 0 in the result.

For example, here's how & would compare Danniel with Bowman.

Danniel   00000110111010110011001011101001
Bowman    10001010000110001110011010011001

Result:   01110011000011000010101110001111

To find the most compatible couple, find the couple whose & results in the most 1s.

To find the least compatible couple, find the couple whose & results in the fewest 1s.

Community
  • 1
  • 1
4castle
  • 32,613
  • 11
  • 69
  • 106
  • This post helped me a bunch. I totally remember taking a digital logic design course last semester and we did and gates and stuff, all the knowledge is flowing back to me now :O – Muntasir Alam Jul 22 '16 at 01:52
  • If you don't mind im going to try this soon, and post my code edited here, if I have troubles would you be as so kind as to help me again? – Muntasir Alam Jul 22 '16 at 01:55
  • Sure. Let me just give you a hint. To count the number of `1`s in the result, convert the result to a string. In JavaScript that would be with `num.toString(2)`, and in Java that would be with `Integer.toBinaryString(num)` – 4castle Jul 22 '16 at 02:01
  • If you know how bit-shifting works, you could also use that to count the number of `1`s using a loop. When the number is odd, it means the far-right bit is a `1`. – 4castle Jul 22 '16 at 02:09
  • I'm just trying to think first of all how this could be done in javascript, since I can't really ask for the user to type in like 5 names and values and make it dynamic. I would have to call a function and write the names and id's myself... hmm.. – Muntasir Alam Jul 22 '16 at 02:18
  • @cresjoy Use `prompt()`. So like `var name = prompt("Enter 1st name"); var id = prompt("Enter 1st id");` – 4castle Jul 22 '16 at 02:21
  • Wouldnt I want to input the name and id on the same line disaccount for the space in the middle? – Muntasir Alam Jul 22 '16 at 02:24
  • Sure, you could do that too. Use `.split(" ")` on the what get's entered from the prompt. It will produce an array where the first element would be the name, and the second element would be the id. – 4castle Jul 22 '16 at 02:25
  • Crazy to think after using javascript for three months making websites I've never used prompt. Ill give it a shot! – Muntasir Alam Jul 22 '16 at 02:31
  • Heres what I'm doing, I already used some splitting and shifting to make a prompt n amount of times. First I store the name in an array and the id in an array. Cool. Now I will take each combo of everything in the array anded with each other and push all the combinations into a new array which has the anded values of all the combos. The only issue I can think of know is remembering which two names that actually corresponds too – Muntasir Alam Jul 22 '16 at 03:07
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/117980/discussion-between-4castle-and-cresjoy). – 4castle Jul 22 '16 at 03:25
  • I posted one of the answers, mabye you could help me go through that for loop then I can translate it into javascript easily – Muntasir Alam Jul 22 '16 at 12:48
  • I got it!! I posted my solution below – Muntasir Alam Jul 22 '16 at 23:36
0

AFTER 5 HOURS OF HITTING MY HEAD

function compareBit(num1,num2)
{

    var mask = 1;
    var count = 0;
    for(var i = 0; i<32;i++)
        {
            if((num1&mask) === (num2&mask))
                {
                    ++count;            
                }

            mask = mask << 1;   
        }
    return count;
}
var obj = [];
var number = prompt("Enter how many names and id's you will put");
for(var i = 0 ; i<number ; i++)
    {
        query = prompt("Enter name then id, you must put one space in between");
        query_result = query.split(" ");
        obj.push({name:query_result[0],value:query_result[1]});      
    }



var scores = [];

for (var a = 0 ; a<obj.length ; a++){
    for(var b = a+1 ; b<obj.length ; b++){
        var score = compareBit(obj[a].value,obj[b].value);
        scores.push({name1:obj[a].name,name2:obj[b].name,score:score})
    }
}
var max = scores.reduce(function(prev,current){
   return (prev.score > current.score)  ? prev : current;
});
var min = scores.reduce(function(prev,current){
   return (prev.score < current.score)  ? prev : current;
});
console.log(max.name1+ " " + max.name2);
console.log(min.name1+ " " + min.name2);
Muntasir Alam
  • 1,777
  • 2
  • 17
  • 26
  • I'd suggest breaking the problem into smaller pieces which you can put into functions and you can test each function to see if it is doing its job. That is much easier to understand, see intermediate results and see where things go wrong. Also, this is a maximum problem which means you have to collect all the results and then find the unique best matches. You can't do both at the same time very easily. – jfriend00 Jul 22 '16 at 18:34