我正在编写一个程序,读取一个包含200,000个字典单词的文本文档。我会将它与2,000,000个单词的另一个文档进行比较,以计算字典中没有的行。我只会存储字母字符a-z
(26个字符)。因此,我只需要5位来表示每一个。角色的最大长度为29
(30 including the null character)
。如果字典中只包含小于7 characters
的单词,我还需要考虑。
此系统的条件是它有自己的必须使用的自定义分配器。所以每当我使用" new" (或任何其他类型的堆内存分配)动态分配内存,每次使用32 bytes
关键字时,它将耗尽至少 new
内存。为了节省内存,我需要初始化更大的char arrays
。
我也不允许直接读取文件中的字典单词。它们将在一个名为dictionaryWords
的数组中提供给我,并在构建完哈希表后将被销毁。所以指向给我的话并不合理。
该程序必须以低于1 second
的速度运行。
即
Dictionary:
abc
lkas
ddjjsa
dada
Word Doc:
abc
lkas
weee
dada
jajaja
Wrong Numbers List: 3, 5
它会将数字3和5放入Word Doc中不在字典中的行号的数组中。
我将200,000
字典单词保留在哈希表中,因此从单词文档到哈希表的查找O(n)
为n
个单词。问题是我的哈希表需要使用0.5
的加载因子进行重新散列,所以它必须重新散列并且只要半满时就加倍。这给我留下了浪费的200,000
空哈希条目,占用了内存。由于最大字长为30
,而字符值为1 byte
,因此我的哈希条目中每个字的费用为30 bytes
。程序运行时,我在堆中浪费30 characters * 1 byte * 200,000 empty entries = 6000000 bytes / 6MB
个内存空间。我想尽可能地减少空间使用,但能够在O(n)时间内运行。
这是我的哈希表的哈希条目。你可以在这里看到,对于200,000字的表大小,我需要400,000个这样的条目以保持0.5的负载系数
struct HashElement
{
char element[30];
HashElement(char * word)
{
memset(element, '\0', 30); //Set all bits to NULL for collision checking
if (e != NULL)
{
strcpy(element, word);
}
}
};
如果我只用5位代表每个角色,我就可以节省3/8的浪费空间。
我已经考虑过我的哈希条目的指针方法:
struct HashElement
{
char * element;
HashElement(char * word)
{
element = NULL;
if (e != NULL)
{
element = word;
}
}
};
但是使用new
关键字将耗尽AT LEAST
32 bytes
用于此系统。因此,每次初始化哈希条目的元素时,无论字长大小是多少,都将花费32 bytes
。如果我想将此问题分散到word文档中每行只包含7个字符,我会遇到麻烦,因为我只需要8 bytes
每个单词,而我使用的是32 bytes
。
答案 0 :(得分:2)
当您需要首先解决许多简单问题时,您正在解决一个难题。解决所有容易出现的问题可能就足够了,因此您无需处理将位压缩为更少字节的难题。
为什么分配30个字节只是因为您可能需要多达30个字节?
为什么要为空哈希桶分配元素?
答案 1 :(得分:1)
这是一个简单的字符串压缩器类,它将在'a'和'z'之间包含一串字符,并将每个8位表示压缩为5位并将得到的二进制分解为7位表示。由于每个字符仍由唯一编号表示,因此该字的散列仍应与预期一样唯一:
class StringCompressor
{
public:
static string Compress( string );
static string ToBinary( long long input , int length );
static int ToInt( string input );
static string Decompress( string );
};
string StringCompressor::Compress( string input )
{
stringstream ss;
for ( char c : input )
{
string temp = ToBinary( ( c - 'a' ) , 5 );
ss << temp;
}
ss << string( ( 7 - ( ss.str().length() % 7 ) ) , '0' );
string temp = ss.str();
ss.str( "" );
for ( int i = 0; i < temp.length(); i += 7 )
{
string temp2 = temp.substr( i , 7 );
ss << (char)ToInt( temp2 );
}
return ss.str();
}
string StringCompressor::Decompress( string input )
{
stringstream ss;
for ( char c : input )
{
string temp = ToBinary( c , 7 );
ss << temp;
}
string temp = ss.str().substr( 0 , ss.str().length() - ( ss.str().length() % 5 ) );
ss.str( "" );
for ( int i = 0; i < temp.length(); i += 5 )
{
ss << (char)( ( ToInt( temp.substr( i , 5 ) ) ) + 'a' );
}
return ss.str();
}
string StringCompressor::ToBinary( long long input , int length )
{
string output( length , '0' );
for ( int i = length - 1; i >= 0; i-- )
{
long long test = pow( 2.0 , i );
if ( input >= test )
{
output[( length - 1 ) - i] = '1';
input -= test;
}
}
return output;
}
//Take a string representation of a binary number and return the base10 representation of it.
//There's no validation of the string
int StringCompressor::ToInt( string input )
{
int length = input.length();
int output = 0;
double temp = 0;
for ( int i = 0; i < length; i++ )
{
temp = ( input[( length - 1 ) - i] - '0' );
output += pow( 2.0 , i ) * temp;
}
return output;
}