Рассчитать блок энтропии

12

Однажды мне нужно было написать функцию, которая вычисляет энтропию блоков для данной серии символов для данного размера блока, и был удивлен, насколько коротким был результат. Таким образом, я призываю вас кодировать такую ​​функцию. Я не скажу вам, что я сделал сейчас (и на каком языке), но я сделаю это через неделю или около того, если никто не придумал те же или лучшие идеи в первую очередь.

Определение блока энтропии:

Для данной последовательности символов A = A_1,…, A_n и размера блока m:

  • Блок размером m является сегментом из m последовательных элементов последовательности символов, то есть A_i,…, A_ (i + m − 1) для любого подходящего i.
  • Если x является символьной последовательностью размера m, N (x) обозначает количество блоков A, которые идентичны x.
  • p (x) - вероятность того, что блок из A идентичен последовательности символов x размера m, т. е. p (x) = N (x) / (n − m + 1)
  • Наконец, энтропия блока для размера блока m из A является средним значением -log (p (x)) по всем блокам x размера m в A или (что эквивалентно) сумме -p (x) · log (p) (x)) по каждому x размера m, встречающемуся в A. (Вы можете выбрать любой разумный логарифм, который хотите.)

Ограничения и уточнения:

  • Ваша функция должна принимать последовательность символов A, а также размер блока m в качестве аргумента.
  • Вы можете предположить, что символы представлены как целые числа, начинающиеся с нуля, или в другом удобном формате.
  • Ваша программа должна быть способна принимать любые разумные аргументы в теории и в действительности должна быть способна рассчитать пример (см. Ниже) на стандартном компьютере.
  • Разрешены встроенные функции и библиотеки, если они не выполняют большие части процедуры за один вызов, то есть извлекают все блоки размером m из A, подсчитывают количество вхождений данного блока x или вычисляют энтропии. из последовательности значений р - вы должны сделать эти вещи самостоятельно.

Тестовое задание:

[2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, 2, 2, 4, 4, 4, 1, 1, 1, 0, 4, 1,
2, 2, 4, 0, 1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 0, 2, 1, 4, 3,
0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, 2, 3, 0, 0, 4, 4, 2, 3, 1, 3, 1, 1,
3, 1, 3, 1, 0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 3, 0, 0, 4, 4, 1, 0,
2, 3, 0, 0, 1, 4, 4, 3]

Первые блок-энтропии этой последовательности (для натурального логарифма):

  • м = 1: 1,599
  • м = 2: 3,065
  • м = 3: 4,067
  • м = 4: 4,412
  • m = 5: 4,535
  • м = 6: 4,554
Wrzlprmft
источник
@ m.buettner: Если вы считаете, что ваше решение граничит с моими правилами, вы все равно можете попробовать - я действительно хочу только избегать решений в духе entropy(probabilities(blocks(A,m))).
Wrzlprmft
Разве не принято для этого использовать базу журнала 2?
Джонатан Ван Матре
Значения для энтропии в конце положительны, но логарифм вероятности отрицателен или равен нулю. Поэтому в формуле энтропии отсутствует отрицательный знак.
Хайко Oberdiek
@JonathanVanMatre: Насколько я знаю, это зависит от дисциплины, которая наиболее часто используется в логарифме. В любом случае, это не должно иметь большого значения для решения проблемы, и поэтому вы можете использовать любую разумную базу, какую захотите.
Wrzlprmft
@HeikoOberdiek: Спасибо, я забыл это.
Wrzlprmft

Ответы:

6

Mathematica - 81 78 75 72 67 65 62 56 байт

Я ничего не играл в Mathematica раньше, поэтому я полагаю, что есть возможности для улучшения. Это один не вполне соответствуют правилам из - за Partitionи Tallyфункции, но это довольно опрятно , поэтому я думал , что я отправлю его в любом случае.

f=N@Tr[-Log[p=#2/Length@b&@@@Tally[b=##~Partition~1]]p]&

Это работает с любым набором символов и может использоваться как

sequence = {2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, 2, 2, 4, 4, 4, 1, 1, 
   1, 0, 4, 1, 2, 2, 4, 0, 1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 
   0, 2, 1, 4, 3, 0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, 2, 3, 0, 0, 4, 4, 
   2, 3, 1, 3, 1, 1, 3, 1, 3, 1, 0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 
   3, 0, 0, 4, 4, 1, 0, 2, 3, 0, 0, 1, 4, 4, 3};
f[sequence, 3]

> 4.06663

Вот несколько нелепая версия:

f[sequence_, m_] := (
    blocks = Partition[sequence, m, 1];
    probabilities = Apply[#2/Length[blocks] &, Tally[blocks], {1}];
    N[Tr[-Log[probabilities]*probabilities]]
)

Это, вероятно, будет работать быстрее, если я применю Nнепосредственно к результату Tally.

Кстати, у Mathematica действительно есть Entropyфункция, которая уменьшает ее до 28 байт , но это определенно противоречит правилам.

f=N@Entropy@Partition[##,1]&

С другой стороны, вот 128-байтовая версия, которая переопределяет Partitionи Tally:

f=N@Tr[-Log[p=#2/n&@@@({#[[i;;i+#2-1]],1}~Table~{i,1,(n=Length@#-#2+1)}//.{p___,{s_,x_},q___,{s_,y_},r___}:>{p,{s,x+y},q,r})]p]&

Ungolfed:

f[sequence_, m_] := (
    n = Length[sequence]-m+1; (*number of blocks*)
    blocks = Table[{Take[sequence, {i, i+m-1}], 1},
                   {i, 1, n}];
    blocks = b //. {p___, {s_, x_}, q___, {s_, y_}, r___} :> {p,{s,x+y},q,r};
    probabilities = Apply[#2/n &, blocks, {1}];
    N[Tr[-Log[probabilities]*probabilities]]
)
Мартин Эндер
источник
Partitionи Tallyне являются пограничными случаями, они прямо нарушают правила, поскольку они «извлекают все блоки размера m из A» и «подсчитывают количество вхождений данного блока x», соответственно, в одном вызове. Тем не менее, после всего, что я знаю о Mathematica, я не удивлюсь, если бы без них было достойное решение.
Wrzlprmft
1
@Wrzlprmft Я добавил не очень удачную версию, в которой я переопределил Partitionи Tally.
Мартин Эндер
3

Perl, 140 байт

Следующий скрипт Perl определяет функцию, Eкоторая принимает последовательность символов, за которой следует размер сегмента в качестве аргументов.

sub E{$m=pop;$E=0;%h=();$"=',';$_=",@_,";for$i(0..@_-$m){next
if$h{$s=",@_[$i..$i+$m-1],"}++;$E-=($p=s|(?=$s)||g/(@_-$m+1))*log$p;}return$E}

Неуправляемая версия с тестом

sub E { # E for "entropy"
    # E takes the sequence and segment size as arguments
    # and returns the calculated entropy.
    $m = pop;    # get segment size (last argument)
    $E = 0;      # initialize entropy
    %h = ();     # hash that remembers already calculated segments
    $" = ',';#"  # comma is used as separator
    $_ = ",@_,"; # $_ takes sequence as string, with comma as delimiters
    for $i (0 .. @_-$m) {
        $s = ",@_[$i..$i+$m-1],"; # segment
        next if$h{$s}++;          # check, if this segment is already calculated
        $p = s|(?=\Q$s\E)||g / (@_ - $m + 1); # calculate probability
             # N(x) is calculated using the substitution operator
             # with a zero-width look-ahead pattern
             # (golfed version without "\Q...\E", see below)
        $E -= $p * log($p); # update entropy
    }
    return $E
}

# Test

my @A = (
    2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, 2, 2, 4, 4, 4, 1, 1, 1, 0, 4, 1,
    2, 2, 4, 0, 1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 0, 2, 1, 4, 3,
    0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, 2, 3, 0, 0, 4, 4, 2, 3, 1, 3, 1, 1,
    3, 1, 3, 1, 0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 3, 0, 0, 4, 4, 1, 0,
    2, 3, 0, 0, 1, 4, 4, 3
);

print "m = $_: ", E(@A, $_), "\n" for 1 .. @A;

Результат:

m = 1: 1.59938036027528
m = 2: 3.06545141203611
m = 3: 4.06663334311518
m = 4: 4.41210802885304
m = 5: 4.53546705894451
m = 6: 4.55387689160055
m = 7: 4.54329478227001
m = 8: 4.53259949315326
m = 9: 4.52178857704904
...
m = 97: 1.38629436111989
m = 98: 1.09861228866811
m = 99: 0.693147180559945
m = 100: 0

Условные обозначения:

Символы не ограничиваются целыми числами, потому что используется сопоставление с образцом на основе строк. Строковое представление символа не должно содержать запятую, поскольку оно используется в качестве разделителя. Разные символы должны иметь разные строковые представления.

В версии для гольфа строковое представление символов не должно содержать специальных символов шаблонов. Дополнительные четыре байта \Q... \E не нужны для чисел.

Хайко Обердиек
источник
Может быть около 1/4 коротких: sub f{($s,$m,$r,%h)=@_;$h{x,@$s[$_..$_+$m-1]}++for 0..@$s-$m;$r-=($_/=@$s-$m+1)*log for values %h;return$r}; где $s- ссылка $rи %hсбрасываются на undef с 1-м присваиванием, списки - это хэш-ключи (с небольшой помощью $;, а некоторые x- к сожалению), и, как мне кажется, они немного менее сложны.
user2846289
@VadimR: Умно! Из-за существенных изменений, которые я предлагаю, вы делаете ответ. Пространство в values %hне требуется, поэтому вашему решению нужно всего лишь 106 байтов.
Хайко Обердик,
2

Python 127 152B 138B

import math
def E(A,m):N=len(A)-m+1;R=range(N);return sum(math.log(float(N)/b) for b in [sum(A[i:i+m]==A[j:j+m] for i in R) for j in R])/N

С поправкой на то, чтобы больше не нарушать правила и иметь немного более симпатичный алгоритм. Скорректировано, чтобы быть меньше

Старая версия:

import math
def E(A,m):
 N=len(A)-m+1
 B=[A[i:i+m] for i in range(N)]
 return sum([math.log(float(N)/B.count(b)) for b in B])/N

Мой первый скрипт Python! Посмотрите это в действии: http://pythonfiddle.com/entropy

александр-Brett
источник
Пока неплохо, но, к сожалению, использование countфункции прямо противоречит правилам, поскольку она «считает количество вхождений данного блока x».
Wrzlprmft
Кроме того, несколько советов по игре в гольф: Вы можете сохранить некоторых персонажей, перепихивая каждую линию, кроме первой, в одну (разделенную ;при необходимости). Также квадратные скобки в последней строке не нужны.
Wrzlprmft
Хороший ответ. Опять же, несколько советов по игре в гольф: полное преобразование из логического в целое (т. Е.) Не требуется and 1 or 0. Кроме того, вы можете сохранить некоторые символы, предварительно определив range(N).
Wrzlprmft
1

Питон с Numpy, 146 143 байта

Как и было обещано, вот мое собственное решение. Требуется ввод неотрицательных целых чисел:

from numpy import*
def e(A,m):
    B=zeros(m*[max(A)+1]);j=0
    while~len(A)<-j-m:B[tuple(A[j:j+m])]+=1;j+=1
    return -sum(x*log(x)for x in B[B>0]/j)

Недостатком является то, что это разрывает вашу память на большой mили max(A).

Вот в основном версия без комментариев и комментариев:

from numpy import *
def e(A,m):
    B = zeros(m*[max(A)+1])          # Generate (max(A)+1)^m-Array of zeroes for counting.
    for j in range(len(A)-m+1):
        B[tuple(A[j:j+m])] += 1      # Do the counting by directly using the array slice
                                     # for indexing.
    C = B[B>0]/(len(A)-m+1)          # Flatten array, take only non-zero entries,
                                     # divide for probability.
    return -sum(x*log(x) for x in C) # Calculate entropy
Wrzlprmft
источник
1

MATLAB

function E =BlockEntropy01(Series,Window,Base )

%-----------------------------------------------------------
% Calculates BLOCK ENTROPY of Series
% Series: a Vector of numbers
% Base: 2 or 10 (affects logarithm of the Calculation)
% for 2 we use log2, for 10 log10
% Windows: Length of the "Sliding" BLOCK
% E: Entropy
%-----------------------------------------------------------
% For the ENTROPY Calculations
% http://matlabdatamining.blogspot.gr/2006/....
% 11/introduction-to-entropy.html
% BlogSpot: Will Dwinnell
%-----------------------------------------------------------
% For the BLOCK ENTROPY
% http://codegolf.stackexchange.com/...
% questions/24316/calculate-the-block-entropy
%-----------------------------------------------------------
% Test (Base=10)
% Series=[2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, ....
%     2, 2, 4, 4, 4, 1, 1, 1, 0, 4, 1,2, 2, 4, 0, ....
%     1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 0, ....
%     2, 1, 4, 3,0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, ....
%     2, 3, 0, 0, 4, 4, 2, 3, 1, 3, 1, 1,3, 1, 3, 1, ....
%     0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 3, 0, 0, 4, ...
%     4, 1, 0,2, 3, 0, 0, 1, 4, 4, 3]';
%
% Results 
%
% Window=1: 1.599
% Window=2: 3.065
% Window=3: 4.067
% Window=4: 4.412
% Window=5: 4.535
% Window=6: 4.554
%-----------------------------------------------------------
n=length(Series);
D=zeros(n,Window); % Pre Allocate Memory
for k=1:Window;    D(:,k)=circshift(Series,1-k);end
D=D(1:end-Window+1,:); % Truncate Last Part
%
% Repace each Row with a "SYMBOL"
% in this Case a Number ...............
[K l]=size(D);
for k=1:K; MyData(k)=polyval(D(k,:),Base);end
clear D
%-----------------------------------------------------------
% ENTROPY CALCULATIONS on MyData
% following  Will Dwinnell
%-----------------------------------------------------------
UniqueMyData = unique(MyData);
nUniqueMyData = length(UniqueMyData);
FreqMyData = zeros(nUniqueMyData,1); % Initialization
for i = 1:nUniqueMyData
    FreqMyData(i) = ....
        sum(double(MyData == UniqueMyData(i)));
end
% Calculate sample class probabilities
P = FreqMyData / sum(FreqMyData);
% Calculate entropy in bits
% Note: floating point underflow is never an issue since we are
%   dealing only with the observed alphabet
if Base==10
    E= -sum(P .* log(P));
elseif BASE==2
    E= -sum(P .* log2(P));
else
end
end

WITH TEST SCRIPT 
%-----------------------------------------------------------
Series=[2, 3, 4, 1, 2, 3, 0, 0, 3, 2, 3, 0, ....
    2, 2, 4, 4, 4, 1, 1, 1, 0, 4, 1,2, 2, 4, 0, ....
    1, 2, 3, 0, 2, 3, 2, 3, 2, 0, 1, 3, 4, 4, 0, ....
    2, 1, 4, 3,0, 2, 4, 1, 0, 4, 0, 0, 2, 2, 0, ....
    2, 3, 0, 0, 4, 4, 2, 3, 1, 3, 1, 1,3, 1, 3, 1, ....
    0, 0, 2, 2, 4, 0, 3, 2, 2, 3, 0, 3, 3, 0, 0, 4, ...
    4, 1, 0,2, 3, 0, 0, 1, 4, 4, 3]';
Base=10;
%-----------------------------------------------------------
for Window=1:6
    E =BlockEntropy01(Series,Window,Base )
end
Александр Е. Хилларис
источник
3
Добро пожаловать в PPCG.SE! Это задача для игры в гольф, цель которой состоит в том, чтобы решить задачу как можно меньшим количеством персонажей. Пожалуйста, добавьте версию без комментариев, минимальный пробел и имена односимвольных переменных (и любые другие ярлыки, которые вы можете придумать), а также количество байтов в этом коде.
Мартин Эндер