如果你仔细阅读这个问题,这是一个关于“两个 32 位数字有多少相同的位”的问题。您必须检查二进制形式的数字(您可以查看每个位)并比较它们之间的位代表每个人特征的数字。
所以,一个人的数字可以用巴里表示为
01001101110011010011010100101111
另一个是
01111100010011010011010100010111
要查看它们的匹配程度,您必须查看有多少位具有相同的值,其中相同对应位置的位既是0,又是1。对具有相同值的每个位的匹配分数加一。
由于这是某种学习练习,我不会只是将代码交给您,而是会指向您reference on manipulating bits in Javascript。
事实证明,这个问题有多个阶段:
- 比较两个人的口罩编号并生成匹配分数。
- 对于所有可能的用户组合,计算所有匹配分数。
- 查找最高匹配分数,将它们添加到最终结果中,将它们从您目前拥有的所有匹配分数中删除,然后找到下一个最高匹配。重复,直到剩下 0 或 1 个人。
我的解决方案的输出(尚未链接,因此 OP 可以自己解决问题):
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
以下是比较两个数字中给定位是否相同的提示:
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
}
下一个提示:如果位掩码是2,那么您正在比较第二位。如果位掩码为4,则您将比较第三位8,然后是第四位16,第五位,以此类推直到第32位。添加一个计数器,您可以计算匹配的位。
这是一个可行的解决方案:
// 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>
比较位说明
关键部分是计算两个数字中相同的位:
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;
}
这是我认为最容易理解的实现(不是最快的)。基本上它所做的是定义一个初始值为1 的掩码号。当我们将掩码号与每个值进行逻辑与运算时,我们会在每个数字中隔离一个位。然后我们可以比较剩下的单个位,看看它们是否相等。如果是这样,增加我们的分数。然后,将掩码向左移动一个位置,以便我们查看下一位。重复 32 次,我们比较了 32 位中的每一个,计算有多少具有相同的值。
如果您想了解如何实现钝位操作,这里有一个非常快的算法,称为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;
}
这是一个更慢的实现,但位计数部分是一个简单的字符串计数:
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;
}
步骤:
- num1 XOR num2 - 如果 num1 和 num2 中的对应位具有相同的值,则生成一个位设置为
0 的结果,或者如果它们是不同的值,则设置为 1。
- XOR 值与我们想要的相反,所以我们用
~ 运算符取反。现在我们有了1,这两个数字的位数相等。
- 因为取反的值超过 32 位,我们用最大 32 位的值将高位清零。
- 然后因为 Javascript 只处理有符号值,但我们想要无符号值,我们使用了一个零填充右移的技巧,但使用
0 移位值>>> 0,这将清除符号位。
- 现在有一个匹配位列表。
- 然后,计算它们的一个非常简单的方法是转换为二进制的字符串表示并删除零。剩下的就是字符串中的
1s,所以只要看看字符串有多长,这就是字符串中有多少1s。