计算字符串的直方图熵估计


19

编写一个程序或函数来估计给定字符串的香农熵。

如果一个字符串有n个字符,d个 不同的字符,x i是第i个不同的字符,P(x i是该字符出现在字符串中的概率,则我们对该字符串的香农熵估计为:

H = -n \ sum \ limits_ {i = 1} ^ d P(x_i)\ log_2 P(x_i)

对于此挑战中的估计,我们假设一个字符出现在字符串中的概率就是该字符出现的次数除以字符总数。

句点后,您的答案必须准确到至少3位数字。


测试用例:

"This is a test.", 45.094
"00001111", 8.000
"cwmfjordbankglyphsvextquiz", 122.211
"             ", 0.0

与我通常遇到的挑战相反,这看起来很复杂,但实际上非常简单:)
orlp 2016年


假定输入字符串可打印的ASCII是否安全?
AdmBorkBork '16

@TimmyD否。您语言的字符串类型支持的任何字符串。
orlp

不幸的是,Mathematica Entropy对每个字符的位数进行计数,而不是字符串的总数。噢...
2012rcampion '16

Answers:



11

Python 3.3 +,64个字节

import math
lambda s:sum(math.log2(len(s)/s.count(c))for c in s)

math.log2mbomb007的解决方案中得到


所以@orlp没有给我们一个完全简化的公式,是吗?
mbomb007 '16

@ mbomb007取决于您要简化的目的。根据概率和不同的字符来编写它是很自然的定义,但是对于高尔夫来说,计算计数和遍历所有字符的时间要短一些。
xnor

1
使用您的公式的Pyth答案:pyth.herokuapp.com/…8字节
Maltysen

2

APL,18 14字节

+/2⍟≢÷(+/∘.=⍨)

这是未命名的单子函数序列,它接受右侧的字符串并返回实数。

就像生活中所有美好的事物一样,它使用xnor公式。我们使用来获得与字符串中每个字符的出现相对应的布尔值矩阵,将其∘.=⍨沿第一个轴(+/)求和以得到每个字符的出现数,将字符串的长度除以每个字符,然后取对数为2 (2⍟)和。

在这里尝试

感谢丹尼斯节省了4个字节!



1

JavaScript(ES6),67个字节

s=>[...s].map(c=>t+=Math.log2(s.length/~-s.split(c).length),t=0)&&t

我需要使用,~-s.split因为它接受字符串而不是正则表达式。像往常一样,map节拍reduce一个字节。

s=>[...s].reduce((t,c)=>t+Math.log2(s.length/~-s.split(c).length),0)

1

Perl 5,58个字节

子程序:

{for$a(@a=split'',pop){$t+=(log@a/grep/\Q$a/,@a)/log 2}$t}

我的帽子向xnor透露了配方。


-F不起作用(无论如何在草莓中),因为它包含$/
msh210 '16

1

MATL,14个字节

!Gu=stGn/Zl*s|

在线尝试!

!      % transpose implicit input into column vector
Gu     % row vector with unique elements of input
=      % test for equality, element-wise with broadcast
s      % sum of each column
tGn/   % duplicate. Divide by number of input characters
Zl     % binary logarithm
*      % element-wise multiplication
s      % sum of array
|      % absolute value. Display implicitly


1

J- 18 16 14个字节

1#.2^.#%1#.=/~

使用丹尼斯方法中的想法进行了缩短。

用法

   f =: 1#.2^.#%1#.=/~
   f 'This is a test.'
45.0936
   f '00001111'
8
   f 'cwmfjordbankglyphsvextquiz'
122.211
   f '             '
0

说明

1#.2^.#%1#.=/~  Input: string S
           =/~  Create a table testing for equality
        1#.     Convert each row from a list of base 1 digits to decimal
                This is equivalent to taking the sum and forms a list of tallies
      #         Get the length of S
       %        Divide the length by each tally
   2^.          Log base 2 of each
1#.             "Sum" those values and return

1
我认为这不算功能。如果将代码分配给变量,它会执行完全不同的操作。
丹尼斯

@Dennis根据我的收集,看来J将其解释为一连串的构成,使用3 : '... y'相同的语法将其定义为函数是一种有效的方法。J指出它是从右到左求值的,因此我将代码重构为火车。我不喜欢戴帽,[:但找不到其他方法可以乘火车。
英里


0

Jolf,26个字节

_*liuΜGμiEd*γ/l miLeHlimzγ

在这里尝试!(请注意,测试套件功能很糟糕。)

说明

_*liuΜGμiEd*γ/l miLeHlimzγ
       μi                   unique members of i
      G  E                  split on ""
     Μ    d                 map over function
               _miLeH       match i with regex escaped member
             /l      li     divide length of (^) by length of i
            γ               γ = (^)
           *           mzγ  (^) * log_2(γ)
 *li                        (^) * length of i
_                           negate

0

Python 3.3 +,95 91 89 85字节

简单的解决方案。必须使用3.3版math.log2

import math
def f(s):C=s.count;return-sum(C(x)*math.log2(C(x)/len(s))for x in set(s))

在线尝试


您认为这里有什么不必要的吗?n*sum(s.count(c)/n
orlp

@orlp谢谢。我最初有一个单独的函数来查找概率,但是将其粘贴到内部两次并删除以保存字符。
mbomb007 '16

n现在您只需要使用一次就不必将其存储在变量中。
Maltysen '16

0

Java 7,207字节

double C(String x,Map<Character,Integer>f){double H=0,g;for(char c:x.toCharArray())f.put(c,f.containsKey(c)?f.get(c)+1:1);for(char c:f.keySet()){g=f.get(c);H+=g*Math.log(g/x.length())/Math.log(2);}return-H;}

详细 在线试用

double log2(double d) { return Math.log(d) / Math.log(2); }

double C(String x, Map<Character,Integer>f)
{
    double H=0,g;

    // frequency
    for(char c : x.toCharArray())
    {
        f.put(c, f.containsKey(c) ? f.get(c)+1 : 1);
    }

    // calculate entropy
    for(char c : f.keySet())
    {
        g = f.get(c);
        H += g * log2(g / x.length());
    }

    return -H;
}

0

因子,98字节

[ [ length ] [ dup [ [ = ] curry dupd count ] { } map-as nip ] bi [ / log 2 log / ] with map sum ]

这是此Python答案的直接翻译。我将在晚餐时添加一个说明。


0

球拍,130字节

:C

#lang racket
(require math)(λ(S)(let([s(string->list S)])(sum(map(λ(c)(/(log(/(length s)(count(λ(x)(char=? c x))s)))(log 2)))s))))

我的Factor答案的翻译,因此是Kenny Lau的Python答案的间接翻译。


0

k(32字节)

{-+/c*(log c%n:+/c:#:'=x)%log 2}

或者在中q,翻译的内容并不那么简短,但更清晰:

{neg sum c*2 xlog c%n:sum c:count each group x}

0

Mathematica,45个字节

Tr[Log[2,Tr@#/#]#]&@Values@CharacterCounts@#&

用法

这将返回精确的结果,因此我们用来近似它们N

  f = Tr[Log[2,Tr@#/#]#]&@Values@CharacterCounts@#&
  f["This is a test."]//N
45.0936
  f["00001111"]//N
8.
  f["cwmfjordbankglyphsvextquiz"]//N
122.211
  f["             "]//N
0.

0

R,67个字节

l=length(i<-strsplit(readline(),"")[[1]]);-sum(log2(l/table(i)[i]))

说明

从标准输入中获取输入并将其拆分为字符列表。(这种笨拙的语法是为什么在R中线高尔夫球挑战如此艰巨的原因...)

         i<-strsplit(readline(),"")[[1]])

此分配隐藏在length命令内部,因此我们以一个价格获得了两个分配。我们有i,字符列表和l,其长度。

l=length(i<-strsplit(readline(),"")[[1]]);

现在我们计算熵。R有一个不错的函数table,它返回所有唯一值的计数。对于输入This is a testtable(i)返回

> table(i)
i
  . a e h i s t T 
3 1 1 1 1 2 3 2 1

这是由字符索引的,这很好,因为我们可以将其i用作索引来获取每个字符的计数,如下所示:

> table(i)[i]
i
T h i s   i s   a   t e s t . 
1 1 2 3 3 2 3 3 1 3 2 1 3 2 1 

然后,剩下的代码就是熵公式的简单实现,稍微翻转一下即可。

                                           -sum(log2(l/table(i)[i]))

保存两个字节(您的提交也不会在TIO上运行)
JayCe


0

C#,159个字节

打高尔夫球:

string f(string s){var l=s.Length;double sum=0;foreach(var item in s.GroupBy(o=>o)){double p=(double)item.Count()/l;sum+=p*Math.Log(p,2);}return(sum*=-l)+"";}}

取消高尔夫:

string f(string s)
{
  var l = s.Length;
  double sum = 0;
  foreach (var item in s.GroupBy(o => o))
  {
    double p = (double)item.Count() / l;
    sum += p * Math.Log(p, 2);
  }
  return (sum *= -l) + "";
}

测试:

var codeGolf = new StringHistogramEntropyEstimation();
    Console.WriteLine(codeGolf.f("This is a test.")); //45.0935839298008
    Console.WriteLine(codeGolf.f("00001111")); //8
    Console.WriteLine(codeGolf.f("cwmfjordbankglyphsvextquiz")); //122.211432671668
    Console.WriteLine(codeGolf.f("             ")); //0

0

Groovy,100字节

{a->n=a.size();a.toList().unique().collect{p=a.count(it)/n;p*(Math.log(p)/Math.log(2.0f))}.sum()*-n}

测试:

This is a test. = 45.09358393449714
00001111 = 8.0
cwmfjordbankglyphsvextquiz = 122.21143275636976
aaaaaaaa = -0.0
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.