使用c和位移位来解决特定需求

使用c和位移位来解决特定需求,c,algorithm,bit-shift,C,Algorithm,Bit Shift,我有一个16个字母的字母表。给定一个句子,我想计算每个字母的频率,然后使用巧妙的位移位将所有频率封装在一个数字中。假设这些句子都是100个字母,假设没有字母出现超过31次,我想这样: A: occurs 2 times -> 0010 B: occurs 10 times -> 1010 C: occurs 7 times -> 0111 等等 现在,我想把这些连接起来: 001010100111 我只是集中了上面的频率。为了方便地存储数字,我想将上面的二进制文件转换为64位

我有一个16个字母的字母表。给定一个句子,我想计算每个字母的频率,然后使用巧妙的位移位将所有频率封装在一个数字中。假设这些句子都是100个字母,假设没有字母出现超过31次,我想这样:

A: occurs 2 times -> 0010
B: occurs 10 times -> 1010
C: occurs 7 times -> 0111
等等

现在,我想把这些连接起来: 001010100111

我只是集中了上面的频率。为了方便地存储数字,我想将上面的二进制文件转换为64位无符号整数

我的另一个要求是有那么长的时间,并重新提取每个字母的频率。因此,我需要能够生成十进制数,然后将其解析为各个频率位

在c语言中我该怎么做?我可以对这些频率进行位移位和加法,但这意味着我是重叠频率。另一个问题是在提取频率时,我如何知道要移动多少位,因为尾随的0不重要,也不保存在十进制中,但它们在我的算法中非常重要


有什么好主意吗?多谢各位

你有两个问题:一个数学问题和一个编码问题

让我们暂时忽略这道数学题。您可以构建一个包含16个整数的数组,并在扫描文本时统计每个字母的出现次数。如果您假设没有字母出现超过15次,那么您就不必担心溢出,您可以很容易地将计数放入64位整数中。你会写:

int counts[16];  // has the counts
unsigned long long freqs;  // this holds the encoded value

// after you compute the counts
freqs = 0;
for (int i = 0; i < 16; ++i)
{
    freqs <<= 4;
    freqs |= (counts[i] & 0xF);
}
int计数[16];//有计数吗
无符号长-长频率;//它保存编码的值
//在你计算计数之后
频率=0;
对于(int i=0;i<16;++i)
{

freqs试试这个,优点是不需要中间数组来计算字母:

int ch_to_index(char ch) { return ch-'A'; }

unsigned long long get_freq(unsigned long long freq, int index)
{
    return (freq>>(4*index))&0x0f;
}


unsigned long long set_freq(unsigned long long freq, int index, unsigned long val)
{
    return (  ((val&0x0fULL)<<(4*index)) | (freq & (0xffffffffffffffffULL ^ (0xfULL<<(4*index)))) );
}

unsigned long long inc_freq(unsigned long long freq, int index)
{
    return set_freq(freq, index, get_freq(freq, index) +1) ;
}

int main()
{
    int i;
    unsigned long long freq=0;
    freq = inc_freq(freq, ch_to_index('A'));
    freq = inc_freq(freq, ch_to_index('A'));
    freq = inc_freq(freq, ch_to_index('B'));

    for(i=0;i<16;i++)
    {
        printf("%i = %i\n", i, (int)get_freq(freq, i));
    }
}
int chu to_index(char ch){return ch-'A';}
无符号long-long-get\u-freq(无符号long-long-freq,int-index)
{
返回(频率>>(4*索引))&0x0f;
}
无符号长集\频率(无符号长频率、整数索引、无符号长值)
{

return(((val&0x0fULL)这样的内容就足够了:

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

const static int  SIZE       = 16;
const static char ALPHABET[] = "0123456789ABCDEF";

char* getFrequency(char* str);
uint64_t getFrequencyNumber(char* freq);

int main() {
  char*    str     = "1337CODE";
  uint64_t freqNum = getFrequencyNumber(getFrequency(str));
  printf("%llu\n",freqNum);
  return 0;
}

char* getFrequency(char* str) {
  int i,j;
  char* freq = (char*) calloc(SIZE, sizeof(char));
  for(i=0; str[i]; ++i)
    for(j=0; j<SIZE; ++j)
      if(str[i] == ALPHABET[j])
        if(freq[i] < 15) //ignore overflow
          (freq[j])++;
  return freq;
}

uint64_t getFrequencyNumber(char* freq) {
  uint64_t i,num;
  for(i=num=0; i<SIZE; ++i)
    num |= freq[i] << (4*i); //use bit shifting to concatenate 4 bit values
  return num;
}
#包括
#包括
#包括
常量静态int SIZE=16;
常量静态字符字母[]=“0123456789ABCDEF”;
char*getFrequency(char*str);
uint64_t getFrequencyNumber(字符*频率);
int main(){
char*str=“1337代码”;
uint64_t frequenum=getFrequencyNumber(getFrequency(str));
printf(“%llu\n”,freqNum);
返回0;
}
char*getFrequency(char*str){
int i,j;
char*freq=(char*)calloc(SIZE,sizeof(char));
对于(i=0;str[i];++i)

对于(j=0;j而言,现有答案是好的;不过,也许下面的答案更好

只需使用一个64位数字,就可以轻松地增加其中的单个4位部分

例如,下面增加了第3、第5和第13个字母的计数器(从0开始计算):

要打印:

char c;
for (c = 'a'; c <= 'p'; ++c)
{
    int index = c - 'a';
    int counter = (int)((my_counters >> (4 * index)) & 0xf);
    printf("Letter %c, count %d\n", c, counter);
}
charc;
对于(c='a';c>(4*索引))&0xf);
printf(“字母%c,计数%d\n”,c,计数器);
}

注意:我的代码以与您想要的相反的顺序连接位;这种方式似乎更清楚。如果您将
4*索引
替换为
60-4*索引

,则可以颠倒顺序,最多31表示存储一个字母的计数需要5位,乘以16个字母表示80位-您在嗯。可以限制每个字母15个吗?或者只有12个字母?您的规范最多只允许字母表中的一个字母出现15次。对不起,是的,我的意思是最多15次。一个解决方案可以满足您最初的16个字母、31个最大用法、100个长句子的要求,方法是使用汉明码来匹配64位整数。这不是你建议的串联,但可行。有趣的是,我从未想过这种方法!太好了!我只是认为有必要去掉一个大数组。有时,你可以通过将所有数据放入一个或多个适合一个CPU寄存器的变量来优化代码的速度。这些变量不需要访问缓存或R或者,当为只有1k或更少RAM的MCU编码时,位优化可能会很有帮助。有趣的方法,如果你能保证值会留在寄存器中,可能会非常快。如果你确定计数不会超过15,这个解决方案可以很好地工作,但你需要更改行:'return((val&0x0f)
uint64_t my_counters = 0;
size_t i;
for (i = 0; str[i] != '\0'; ++i)
{
    int index = str[i] - 'a';
    my_counters += (uint64_t)1 << (4 * index);
}
char c;
for (c = 'a'; c <= 'p'; ++c)
{
    int index = c - 'a';
    int counter = (int)((my_counters >> (4 * index)) & 0xf);
    printf("Letter %c, count %d\n", c, counter);
}