【问题标题】:Comparing numbers of bits per position in two lists of integers比较两个整数列表中每个位置的位数
【发布时间】:2016-03-17 09:40:56
【问题描述】:

我遇到了一个问题,我可以通过不同地安排我的算法来规避,但这很有趣,也许你们中的一个人有一个好主意。

情况如下:我有两个无符号长整数列表,两个列表的大小相同,如果这有帮助,您可以假设这个大小是 2 的幂。这些列表的大小通常在数百个范围内。现在我想计算一个整数,该整数在第一个列表比第二个列表具有更多设置位的每个位置都有一个设置位。

速度就是一切。

Simplified example:

list1   list2

1010    0101
1111    0000
1100    0011
1010    0101

result: 1010
because of 4>0, 2<=2, 3>1, 1<=3

编辑:数据的替代排列将导致位向量包含现在在几个不同向量中某个位置的位。在那种情况下,我可以只使用 bit counting algorithm 然后进行比较,这将在两个列表中每 64 位少于 30 次操作。基本上我有一个位矩阵,我可以将位向量用于列或行。

附加结构:John Willemse 的评论让我意识到我可以计算第三个列表,以便这三个列表按位相互补充。虽然我不明白这会有什么帮助。

【问题讨论】:

  • 如果您当前的实现有效并且您正在寻找替代方案,那么 (1) 概述当前的算法,以及 (2) 删除标签 c 并添加 algorithm。
  • 查找表将数字映射到设置为 1 的位数怎么样?这可用于最多 16 位左右的数字,具体取决于规格限制等。请告诉我们更多有关该问题的信息。
  • 我还没有实现,我还在研究如何构造数据,如果我以某种方式构造数据,就会出现这个问题,这样可以节省一部分时间算法,但似乎在这里浪费时间。
  • 您的列表有多长,列表中的数字范围是多少?
  • 我的列表长度在几百范围内。数字不是真正的数字,它们是位向量,一切皆有可能。

标签: c bit-manipulation


【解决方案1】:

您可以使用转置计数器来做到这一点 - 不是为数据的每个位位置设置一个 int,而是为计数的每个位位置设置一个 uint。希望你不需要太多位..

然后您可以按照在位向量上定义它们的方式进行加法/减法,每个“位”实际上是所有计数中该位位置的一部分。

也许这听起来很模糊,所以让我们直接进入:(未测试)

// add in item from list2
carry0 = count0 & item2;
count0 ^= item2;
carry1 = count1 & carry0;
count1 ^= carry0;
.. etc for however many bits you need in your counters
// subtract item from list1
borrow0 = ~count0 & item1;
count0 ^= item1;
borrow1 = ~count1 & borrow0;
count1 ^= borrow0;
.. etc

结果是符号,所以你使用的最后一个计数器。


或者,完全不同:也许您可以使用 int、SWAR 样式的子字段。这仅在字段很小或不需要很多字段时才有效,因为没有太多空间。使用 4 位项目还不错,uint32_t 提供 4 个计数器,范围从 -128 到 127,这可能就足够了(最终的差异必须在该范围内,中间结果可以安全地换行)

无论如何,它的工作原理是使用查找表或pdep 将位分散开,(未测试)

uint32_t spread = _pdep_u32(item, 0x01010101);
// or
uint32_t table[] = {
    0x00000000, 0x00000001, 0x00000100, 0x00000101,
    0x00010000, 0x00010001, 0x00010100, 0x00000101,
    0x01000000, 0x01000001, 0x01000100, 0x00000101,
    0x01010000, 0x01010001, 0x01010100, 0x01010101 };
uint32_t spread = table[item];

然后做SWAR加减法,不过可以稍微优化一下,因为你知道它们是递增还是递减或者没有变化,(未测试)

// add in spread item 2
uint32_t H = 0x80808080;
count = ((count &~H) + sp2) ^ (count & H);
// subtract spread item 1
count = ((count | H) - sp1) ^ (~count & H);

结果是每个子字段的符号,很容易提取但压缩起来很麻烦(除非你有pext)。

【讨论】:

    【解决方案2】:

    这可能不是最有效的,但这是首先想到的解决方案,即 O(n)。

    int list1[4] = {10, 15, 12, 10};
    int list2[4] = {5, 0, 3, 5};
    
    int i, j;
    int result = 0;
    int num_bits = 4;
    int num_elements = 4;
    
    for (i = num_bits - 1; i >= 0; i--)
    {
        int bit_pos_ans = 0;
        for (j = 0; j < num_elements; j++)
        {
            /* This works by adding the 1s in list1, and subtracting the 1s in list 2 */
            bit_pos_ans += (((list1[j] >> i) & 0x1) - ((list2[j] >> i) & 0x1));
        }
    
        /* If there are more 1s in list1 and list2, then this bit position is a 1. */
        if (bit_pos_ans > 0)
        {
            result += 1;
        }
    
        /* Only shift if this is not calculating bit position 0 */
        if (i > 0)
        {
            result <<= 1;
        }
    }
    
    printf("%d", result);
    

    【讨论】:

      猜你喜欢
      • 2014-02-14
      • 2016-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-08-13
      相关资源
      最近更新 更多