Вычислить оценку энтропии гистограммы строки


19

Напишите программу или функцию, которая оценивает энтропию Шеннона данной строки.

Если строка содержит n символов, d различных символов, x i - это i- й отдельный символ, а P (x i ) - вероятность того, что этот символ встречается в строке, то наша оценка энтропии Шеннона для этой строки определяется как:

H = -n \ sum \ limit_ {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


Безопасно ли использовать ASCII для печати для входной строки?
AdmBorkBork

@TimmyD Нет. Любая строка, которую поддерживает тип строки вашего языка.
orlp

К сожалению, Mathematica Entropyсчитает биты на символ, а не сумму для строки; о хорошо ...
2012rcampion

Ответы:



11

Python 3.3+, 64 байта

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

Получил math.log2из решения mbomb007 .


Значит, @orlp не дал нам полностью упрощенную формулу, а ...?
mbomb007

@ 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не работает (во всяком случае, в Strawberry), потому что включает в себя $/.
msh210

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
Я не думаю, что это считается функцией. Если вы присваиваете код переменной, он делает что-то совершенно другое.
Деннис

@ Денис Из того, что я собираю, кажется, что J интерпретирует это как цепочку композиций, использование 3 : '... y'с тем же синтаксисом было бы правильным способом определить его как функцию. J утверждает, что он оценивается справа налево, поэтому я реорганизовал свой код как поезд. Я не люблю кепки, [:но я не могу найти другой способ сделать поезд.
миль


0

Джольф, 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

Вам не нужно хранить nпеременную сейчас, когда вы используете ее только один раз.
Maltysen

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 байт

: с

#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))))

Перевод моего факторного ответа, так что это косвенный перевод ответа Кенни Лау на Python.


0

к (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]))

объяснение

Возьмите ввод из stdin и разбейте его на список символов. (Этот неуклюжий синтаксис - вот почему проблемы с гольфом в R настолько сложны в R ...)

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

Это назначение скрыто внутри lengthкоманды, поэтому мы получаем два назначения по цене одного. У нас есть iсписок символов и lего длина.

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

Теперь вычислим энтропию. R имеет приятную функцию, tableкоторая возвращает количество всех уникальных значений. Для ввода This is a test, table(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


0

C #, 159 байт

Golfed:

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)+"";}}

Ungolfed:

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.