【问题标题】:Matching names with corresponding 32 bit unsigned integers将名称与相应的 32 位无符号整数匹配
【发布时间】:2016-07-22 01:25:35
【问题描述】:

考虑以下问题http://potw.quinnftw.com/problem/2015/1/

Will 最近感到很孤独。为了帮助自己,他创建了一个约会网站。作为一名计算机科学家,他想出了一种有趣的方法来计算一个人与另一个人的匹配程度。

根据输入的信息,Will 创建一个 32 位无符号整数,用作个人数据的掩码。为了计算一对夫妇之间的兼容性,Will 检查他们有多少相似的位。相似度越高匹配越好。还必须记住,异性相吸。

您的目标是设计一种算法,以显示具有最高兼容性级别和最低兼容性级别的情侣。

总结起来的问题是输入一些整数N,然后在输入N之后输入names的数量以及对应的32位无符号整数。

目标是设计一种算法,显示最高兼容级别和最低兼容级别的情侣。

您可以在链接中查看详细信息。阅读量不算太大。

考虑样本输入

5 
Danniel 116077289 
Bowman 2316887705 
Cleveland 2186347654 
Marinda 2662238983 
Viviana 3393681530

还有输出

Bowman Cleveland 
Marinda Viviana

我有点难以理解这个问题。它是告诉我根据数字的出现来匹配吗?基本上我是在跟踪一个 int 中有多少个整数在另一个名称 int 中是相同的?输出是否简单地通过使用具有最常见数字的一对名称来实现?然后是第二对最不常见的数字?

任何我如何理解这个问题的见解都会很好。到目前为止,我对它的印象是简单地将两个名称与出现次数最多的数字匹配。但是 32 位部分让我很困惑,我不确定这部分是什么意思。

我想用 javascript 或 java 来做这件事,但我认为一旦我完全理解了问题所要问的内容,编码就不是什么大问题了。

谢谢!

考虑这里的解决方案之一http://potw.quinnftw.com/solution/17/

我无法理解的部分是这个 for 循环

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;
    }

我很困惑为什么我在这里使用 xOR 并且每次迭代循环时都会跳水 2。

【问题讨论】:

  • 32 位仅表示 0 到 (2^32 - 1) 范围内的数字,尽管有符号整数使用负数,因此最大正数约为其一半。如果你必须用java写这个,使用long来保证足够的“空间”
  • 那么我应该如何匹配人名呢?我是否只查看每个数字中的哪些数字并将它们与具有最“相似数字”的另一个名称匹配?即 123456 最有可能与 1234567 匹配?
  • 您的问题不清楚是需要最匹配的十进制数字还是物理上最接近的数字。 (例如,如果我输入 3999 是最接近的 4000 或 3991)...但是网站链接是,哈哈,我没看到。您需要对值执行按位 &,然后获取设置的位数最多的结果(注意,不是最高位数)
  • @Olipro - 练习说 检查他们有多少相似的位,这很清楚他们想要比较二进制位。
  • @jfriend00 尝试阅读我的全部评论。当然,我确实编辑过它,但那是你回复前 5 分钟

标签: javascript java


【解决方案1】:

如果你仔细阅读这个问题,这是一个关于“两个 32 位数字有多少相同的位”的问题。您必须检查二进制形式的数字(您可以查看每个位)并比较它们之间的位代表每个人特征的数字。

所以,一个人的数字可以用巴里表示为

01001101110011010011010100101111 

另一个是

01111100010011010011010100010111 

要查看它们的匹配程度,您必须查看有多少位具有相同的值,其中相同对应位置的位既是0,又是1。对具有相同值的每个位的匹配分数加一。

由于这是某种学习练习,我不会只是将代码交给您,而是会指向您reference on manipulating bits in Javascript


事实证明,这个问题有多个阶段:

  1. 比较两个人的口罩编号并生成匹配分数。
  2. 对于所有可能的用户组合,计算所有匹配分数。
  3. 查找最高匹配分数,将它们添加到最终结果中,将它们从您目前拥有的所有匹配分数中删除,然后找到下一个最高匹配。重复,直到剩下 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";}
&lt;script src="http://files.the-friend-family.com/log.js"&gt;&lt;/script&gt;

比较位说明

关键部分是计算两个数字中相同的位:

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;
}

步骤:

  1. num1 XOR num2 - 如果 num1 和 num2 中的对应位具有相同的值,则生成一个位设置为 0 的结果,或者如果它们是不同的值,则设置为 1
  2. XOR 值与我们想要的相反,所以我们用~ 运算符取反。现在我们有了1,这两个数字的位数相等。
  3. 因为取反的值超过 32 位,我们用最大 32 位的值将高位清零。
  4. 然后因为 Javascript 只处理有符号值,但我们想要无符号值,我们使用了一个零填充右移的技巧,但使用0 移位值&gt;&gt;&gt; 0,这将清除符号位。
  5. 现在有一个匹配位列表。
  6. 然后,计算它们的一个非常简单的方法是转换为二进制的字符串表示并删除零。剩下的就是字符串中的1s,所以只要看看字符串有多长,这就是字符串中有多少1s。

【讨论】:

  • 我明白了。所以基本上我应该尝试将一个二进制数字数字与另一个数字匹配。我应该得到二进制数然后将每个数字拆分成一个数组还是什么?否则我将如何逐位比较 X 数量的名称值
  • @cresjoy - 您可以使用二元运算符直接评估/比较各个位。无需转换为数组中的二进制字符串(尽管您也可以这样做,但这不是必需的)。
  • 太好了,另一篇文章提到了 & 运算符,我会在明天尝试告诉你,如果你不介意的话,希望它是如何工作的!
  • 这在 javascript 中如何工作?它不像我可以像在java中那样通过扫描器询问用户是否想输入5个名字,我的意思是我可以要求调用一个函数,它接受你想输入的名字的数量,但我猜无论如何都必须手动输入这些名称..
  • @cresjoy - Anding 与 1 检查第一位。 Anding 与 2 检查第二位。与 4 的 Anding 检查第 3 位,与 8 的 Anding 检查第 4 位,依此类推,直到第 32 位。随身携带一个计数器,您就可以计算出所有匹配位的数量。
【解决方案2】:

“位”是“二进制数字”的缩写。因此,32 位整数是可以用 32 位二进制数字表示的整数。 “无符号”整数不能为负数,因此使用纯二进制而不是 two's complement 存储。

在二进制中,输入可能更有意义。

Danniel   00000110111010110011001011101001
Bowman    10001010000110001110011010011001
Cleveland 10000010010100010000010010000110
Marinda   10011110101011101000101100000111
Viviana   11001010010001110111100001111010

根据问题,兼容性是通过相同位数来衡量的。因此,您可以使用按位运算符&amp;。按位与通过逐位比较来组合两个数字。如果两个位相同,它将在结果中放置一个1。如果两个位不同,它将在结果中放入一个0

例如,&amp; 将 Danniel 与 Bowman 进行比较。

Danniel   00000110111010110011001011101001
Bowman    10001010000110001110011010011001

Result:   01110011000011000010101110001111

要找到兼容的情侣,请找到&amp;导致1s的情侣。

要找到最不兼容的情侣,请找到&amp;导致最少1s的情侣。

【讨论】:

  • 这篇文章帮助了我很多。我完全记得上学期参加了数字逻辑设计课程,我们做了门和其他东西,所有的知识现在都流回给我了:O
  • 如果你不介意我马上试试这个,把我编辑的代码贴在这里,如果我有麻烦你会再次帮助我吗?
  • 当然。让我给你一个提示。要计算结果中1s 的数量,请将结果转换为字符串。在 JavaScript 中使用 num.toString(2),在 Java 中使用 Integer.toBinaryString(num)
  • 如果你知道位移是如何工作的,你也可以用它来计算1s 的数量。当数字为奇数时,表示最右边的位是1
  • 我只是想首先考虑如何在 javascript 中完成此操作,因为我不能真正要求用户输入 5 个名称和值并使其动态化。我将不得不调用一个函数并自己写名字和ID......嗯......
【解决方案3】:

撞到我的头 5 小时后

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);

【讨论】:

  • 我建议将问题分解成更小的部分,您可以将它们放入函数中,然后您可以测试每个函数以查看它是否正在发挥作用。这更容易理解,查看中间结果并查看哪里出了问题。此外,这是一个最大的问题,这意味着您必须收集所有结果,然后找到唯一的最佳匹配。你不能同时做到这两点。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-31
  • 2013-06-21
  • 2019-05-30
  • 2012-07-27
  • 2011-06-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多