Similar to zngu's answer, I think better than just counting the number of characters would be calculating the character-entropy of the message:
public double CalculateEntropy(string entropyString)
{
Dictionary<char, int> characterCounts = new Dictionary<char, int>();
foreach(char c in entropyString.ToLower())
{
if(c == ' ') continue;
int currentCount;
characterCounts.TryGetValue(c, out currentCount);
characterCounts[c] = currentCount + 1;
}
IEnumerable<double> characterEntropies =
from c in characterCounts.Keys
let frequency = (double)characterCounts[c]/entropyString.Length
select -1*frequency*Math.Log(frequency);
return characterEntropies.Sum();
}
It seems to work well with both code and text, but note that it is not calculating the actual entropy of the string, only the entropy of the character-distribution; sorting the characters within the string should reduce the entropy of the string, but it does not reduce the result of this function.
Here are some tests:
private void CalculateEntropyTest(object sender, EventArgs e)
{
string[] testStrings = {
"Hello world!",
"This is a typical english sentence containing all the letters of the english language - The quick brown fox jumped over the lazy dogs",
String.Join("", "This is a typical english sentence containing all the letters of the english language - The quick brown fox jumped over the lazy dogs".ToCharArray().OrderBy(o => o).Select(o => o.ToString()).ToArray()),
"Won't this work too?\nstring name = \"lltt\";\nint uniqueCharacterCount = name.Distinct().Count();\nwill return 2",
"Pull the entropy finding source from any compression algotithm, i.e. Huffman",
"float CharacterEntropy(const char *str) {\n std::vector<unsigned> counts(256);\n for (const char *i = str; *i; ++i)\n ++counts[static_cast<unsigned char>(*i)];\n unsigned int total = 0;\n for (unsigned i = 0; i < 256; ++i)\n total += counts[i];\n float total_float = static_cast<float>(total);\n float ret = 0.0;\n for (unsigned i = 0; i < 256; ++i) {\n float p = static_cast<float>(counts[i]) / total_float;\n ret -= p * logf(p);\n }\n return p * M_LN2;\n}",
"~~~~~~No.~~~~~~",
"asdasdasdasdasdasd",
"abcdefghijklmnopqrstuvwxyz",
"Fuuuuuuu-------",
};
foreach(string str in testStrings)
{
Console.WriteLine("{0}\nEntropy: {1:0.000}\n", str, CalculateEntropy(str));
}
}
Results:
Hello world!
Entropy: 1.888
This is a typical english sentence containing all the letters of the english language - The quick brown fox jumped over the lazy dogs
Entropy: 2.593
-TTaaaaaaabccccddeeeeeeeeeeeeeeffgggggghhhhhhhiiiiiiiijk
lllllllmnnnnnnnnnooooooppqrrrsssssssttttttttuuuvwxyyz
Entropy: 2.593
Won't this work too?
string name = "lltt";
int uniqueCharacterCount = name.Distinct().Count();
will return 2
Entropy: 2.838
Pull the entropy finding source from any compression algotithm, i.e. Huffman
Entropy: 2.641
float CharacterEntropy(const char *str) {
std::vector counts(256);
for (const char *i = str; *i; ++i)
++counts[static_cast(*i)];
unsigned int total = 0;
for (unsigned i = 0; i < 256; ++i)
total += counts[i];
float total_float = static_cast(total);
float ret = 0.0;
for (unsigned i = 0; i < 256; ++i) {
float p = static_cast(counts[i]) / total_float;
ret -= p * logf(p);
}
return p * M_LN2;
}
Entropy: 2.866
~~~~~~No.~~~~~~
Entropy: 0.720
asdasdasdasdasdasd
Entropy: 1.099
abcdefghijklmnopqrstuvwxyz
Entropy: 3.258
Fuuuuuuu-------
Entropy: 0.892
Actually, I think it would be better to do some frequency analysis, but I don't know anything about the frequencies of symbols used in code. The best place to determine that would be the stackoverflow data-dump - I'll have to get back to you after it finishes downloading, in 2 years.