范围域转化为前缀域

范围域转化为前缀域

给定一个范围,将其转化成有数字和掩码组成的前缀域。

例:

[45,208][0b00101101,0b11010000]转化为前缀域表达为:

00101101/11111111
00101110/11111110
00110000/11110000
01000000/11000000
10000000/11000000
11000000/11110000
11010000/11111111

思路1:

两两合并直到直至不能合并为止

[7,13]为例

7
0111/1111
0111/1111
0111/1111
8
1000/1111
1000/1110
1000/1100
9
1001/1111
10
1010/1111
1010/1110
11
1011/1111
12
1100/1111
1100/1110
1100/1110
13
1101/1111

[7,13]以前缀表示为

0111/1111
1000/1100
1100/1110

题解1:

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdlib.h>



void printbits(uint16_t v, int size)
{
    for (int i = size - 1; i > -1; i--)
    {
        printf("%d", (v >> i) & 1);
    }
}

void mask(const uint16_t a, const uint16_t b)
{
    uint16_t value[65536], mask[65536];

    uint32_t index = 0;
    uint32_t count = b - a + 1;
    printf("count%d\n", count);
    memset(mask, 0xFFU, 65536 * sizeof(uint16_t));
    for (uint32_t i = 0, j = a; i < count; i++, j++)
    {
        value[i] = j;
    }

    for (uint32_t k = 0; k < 16; k++)
    {
        index = 0;
        for (uint32_t i = 0, j = 1; i < count; i++, j++) //单次循环
        {
            if ((j < count) && (mask[i] == mask[j]) && ((value[i] & (mask[i] << 1U)) == (value[j] & (mask[j] << 1U))))
            {
                value[index] = value[i];
                mask[index] = mask[i] << 1U;
                i++;
                j++;
            }
            else
            {
                value[index] = value[i];
                mask[index] = mask[i];
            }
            index++;
        }
        if (count == index)
        {
            break;
        }
        else
        {
            count = index;
        }
    }

    printf("[%u,%u]\n", a, b);
    for (int i = 0; i < count; i++)
    {
        printbits(value[i], 16);
        printf(" / ");
        printbits(mask[i], 16);
        printf("\n");
    }
}

int main(int argc, char *argv[])
{
    mask((uint16_t)atoi(argv[1]), (uint16_t)atoi(argv[2]));
    return 0;
}

题解2:

上个版本中只是两两合并,这次使用贪婪合并,能合并8个就不合并4个

具体思路:

看当前数值的二进制表示时,末位有几个0
有0个0 至多可以合并2^0=1项,即当前是最简的,无法合并的
有1个0 至多可以合并2^1=2项
有2个0 至多可以合并2^2=4项

为什么说至多,是因为如果只有1个数据,就算末位有再多0,也无法合并了
只有三个数据,也是无法合并四项的,有可能是能合并两项的
000
001
010

[0,3]

那么现在考虑怎么实现
首先数据都是连续的,这是后续操作的基础

当前数据是v,末位有n个0
v + 2^n - 1是否<=b,是的话就代表能合并,当前值存起来,计算掩码为1…100…00 (16-n个1,n个0) 然后继续计算下个值
不是的话,将n–,再次判断,直至找到符合的,因为最少也会有一个 所以算法是收敛的

#include <stdio.h>
#include <stdlib.h>

#define VAILDWIDTH 16
#define MAX(a, b) (a > b ? a : b)
#define BIT(value, index) ((value >> index) & 1)

#define PRINTBITS(value)                    \
    {                                       \
        int size = VAILDWIDTH;              \
        while (size--)                      \
        {                                   \
            printf("%d", BIT(value, size)); \
        }                                   \
    }

int tail0nums(int num)
{
    int count = 0;
    while (!BIT(num, count) && count <= VAILDWIDTH)
    {
        num >> 1;
        count++;
    }
    return count;
}

void range2prefix(const int a, const int b)
{
    int index = 0;
    int tail_zero_num = 0;
    struct
    {
        int value[128]; // 16位的时候其实至多只会有30个,组合是[1,65534]
        int mask[128];
        int count;
    } result;
    result.count = 0;

    for (index = a; index <= b; index++)
    {
        tail_zero_num = tail0nums(index);
        do
        {
            if (0 == tail_zero_num) /*末位没有0的操作*/
            {
                result.value[result.count] = index;
                result.mask[result.count] = tail_zero_num;
                result.count++;
            }
            else if ((2 << (tail_zero_num - 1)) <= (b - index + 1)) //(index + (2 << (tail_zero_num - 1)) - 1 <= b) /*末位有0的操作*/
            {
                result.value[result.count] = index;
                result.mask[result.count] = tail_zero_num;
                result.count++;
                index += (2 << (tail_zero_num - 1)) - 1;
                break; //打破while循环
            }
        } while (tail_zero_num--);
    }

    printf("[%5u,%5x]  %u\n", a, b, result.count);

    printf("%u\n", result.count);
    for (int i = 0; i < result.count; i++)
    {
        PRINTBITS(result.value[i]);
        printf("/%u\n", VAILDWIDTH - result.mask[i]);
    }
}

int main(int argc, char *argv[])
{
    int a, b;
    a = atoi(argv[1]);
    b = atoi(argv[2]);
    {
        int a = 90;
    }
    if (argc > 2 && -1 < a && a <= b && b < (1 << VAILDWIDTH))
    {
        range2prefix(a, b);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值