Oblicz entropię bloku

12

Kiedyś musiałem napisać funkcję, która oblicza entropię bloku danej serii symboli dla danego rozmiaru bloku i byłem zaskoczony, jak krótki był wynik. Dlatego wzywam was do kodegolfa takiej funkcji. Nie mówię wam, co na razie zrobiłem (i w jakim języku), ale zrobię to za tydzień, jeśli nikt wcześniej nie wpadnie na takie same lub lepsze pomysły.

Definicja entropii bloku:

Biorąc pod uwagę sekwencję symboli A = A_1,…, A_n i rozmiar bloku m:

  • Blok o rozmiarze m jest segmentem m kolejnych elementów sekwencji symboli, tj. A_i,…, A_ (i + m-1) dla dowolnego odpowiedniego i.
  • Jeśli x jest sekwencją symboli o rozmiarze m, N (x) oznacza liczbę bloków A, które są identyczne z x.
  • p (x) jest prawdopodobieństwem, że blok z A jest identyczny z sekwencją symboli x o rozmiarze m, tj. p (x) = N (x) / (n − m + 1)
  • Wreszcie, entropia bloku dla rozmiaru bloku m A jest średnią −log (p (x)) dla wszystkich bloków x wielkości m w A lub (co jest równoważne) sumą −p (x) · log (p (x)) na każdy x rozmiaru m występujący w A. (Możesz wybrać dowolny rozsądny logarytm).

Ograniczenia i wyjaśnienia:

  • Twoja funkcja powinna przyjąć jako argument sekwencję symboli A oraz rozmiar bloku m.
  • Możesz założyć, że symbole są reprezentowane jako liczby całkowite od zera lub w innym wygodnym formacie.
  • Twój program powinien być w stanie przyjąć uzasadniony argument teoretycznie, a w rzeczywistości powinien być w stanie obliczyć przykładowy przypadek (patrz poniżej) na standardowym komputerze.
  • Wbudowane funkcje i biblioteki są dozwolone, o ile nie wykonują dużych części procedury w jednym wywołaniu, tj. Wyodrębniają wszystkie bloki o rozmiarze m z A, licząc liczbę wystąpień danego bloku x lub obliczając entropie z sekwencji wartości p - musisz to zrobić sam.

Test:

[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]

Pierwszymi entropiami blokowymi tej sekwencji są (dla logarytmu naturalnego):

  • m = 1: 1,599
  • m = 2: 3,065
  • m = 3: 4,067
  • m = 4: 4,412
  • m = 5: 4,535
  • m = 6: 4,554
Wrzlprmft
źródło
@ m.buettner: Jeśli weźmiesz pod uwagę granice rozwiązania dotyczące moich zasad, nadal możesz spróbować - naprawdę chcę tylko unikać rozwiązań zgodnych z entropy(probabilities(blocks(A,m))).
Wrzlprmft
Czy to nie jest zwyczajowo używać do tego bazy danych dziennika 2?
Jonathan Van Matre
Wartości entropii na końcu są dodatnie, ale logarytm prawdopodobieństwa jest ujemny lub zerowy. Dlatego we wzorze na entropię brakuje znaku ujemnego.
Heiko Oberdiek
@JathanathanVanMatre: O ile mi wiadomo, zależy to od dyscypliny, która jest najczęściej wykorzystywanym logarytmem. W każdym razie nie powinno to mieć większego znaczenia dla wyzwania, dlatego możesz użyć dowolnej bazy, jaką chcesz.
Wrzlprmft
@HeikoOberdiek: Dzięki, zapomniałem o tym.
Wrzlprmft

Odpowiedzi:

6

Mathematica - 81 78 75 72 67 65 62 56 bajtów

Nigdy wcześniej nie grałem w golfa w Mathematica, więc przypuszczam, że jest miejsce na poprawę. Ten nie jest do końca zgodny z regułami z powodu funkcji Partitioni Tally, ale jest całkiem fajny, więc pomyślałem, że i tak go opublikuję.

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

Działa to z dowolnym zestawem symboli i może być używane podobnie

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

Oto nieco nie golfowa wersja:

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

Prawdopodobnie będzie działać szybciej, jeśli zastosuję się Nbezpośrednio do wyniku Tally.

Nawiasem mówiąc, Mathematica faktycznie ma Entropyfunkcję, która redukuje to do 28 bajtów , ale to zdecydowanie niezgodne z regułami.

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

Z drugiej strony, tutaj jest wersja 128-bajtowa, która reimplementuje Partitioni 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]&

Nie golfowany:

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]]
)
Martin Ender
źródło
Partitioni Tallynie są przypadkami granicznymi, wprost łamią zasady, ponieważ „wydobywają wszystkie bloki wielkości mz A” i „zliczają odpowiednio liczbę wystąpień danego bloku x” w jednym wywołaniu. Mimo wszystko, mimo wszystko, co wiem o Mathematica, nie zdziwiłbym się, gdyby bez nich istniało przyzwoite rozwiązanie.
Wrzlprmft
1
@Wrzlprmft Dodałem wersję nie tak golfową, w której reimplementuję Partitioni Tally.
Martin Ender
3

Perl, 140 bajtów

Poniższy skrypt Perla definiuje funkcję, Ektóra przyjmuje sekwencję symboli, a następnie wielkość segmentu jako argumenty.

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}

Wersja bez golfa z testem

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;

Wynik:

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

Symbolika:

Symbole nie są ograniczone do liczb całkowitych, ponieważ stosowane jest dopasowanie wzorca oparte na ciągach znaków. Ciąg znaków reprezentujący symbol nie może zawierać przecinka, ponieważ jest używany jako separator. Oczywiście różne symbole muszą mieć różne reprezentacje ciągów.

W wersji golfowej reprezentacja ciągu symboli nie powinna zawierać znaków specjalnych wzorów. Dodatkowe cztery bajty \Q... \E nie są potrzebne dla liczb.

Heiko Oberdiek
źródło
Może być o 1/4 krótszym: 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}; gdzie $sjest odwołanie $ri %hsą resetowane do undef przy pierwszym przypisaniu, listy są kluczami skrótu (z niewielką pomocą $;, a niektóre x- niestety) i, ogólnie rzecz biorąc, nieco mniej skomplikowanymi.
user2846289,
@VadimR: Clever! Z powodu znacznych zmian, które sugeruję, udzielisz odpowiedzi. Miejsce w values %hnie jest potrzebne, dlatego twoje rozwiązanie potrzebuje tylko 106 bajtów.
Heiko Oberdiek
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

Dostosowano tak, aby nie łamał już reguł i ma nieco bardziej skomplikowany algorytm. Dostosowano tak, aby był mniejszy

Starsza wersja:

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

Mój pierwszy skrypt w języku Python! Zobacz to w akcji: http://pythonfiddle.com/entropy

Alexander-Brett
źródło
Jak dotąd fajnie, ale niestety użycie countfunkcji jest wręcz sprzeczne z regułami, ponieważ „zlicza liczbę wystąpień danego bloku x”.
Wrzlprmft,
Kilka wskazówek golfowych: Możesz uratować niektóre postacie, wciskając wszystkie linie oprócz pierwszej w jedną (oddzielając je, ;jeśli to konieczne). Również nawiasy kwadratowe w ostatnim wierszu nie są potrzebne.
Wrzlprmft,
Niezła odpowiedź. Ponownie, kilka wskazówek golfowych: Cała konwersja z wartości logicznej na liczbę całkowitą (tj. and 1 or 0) Jest niepotrzebna. Możesz także zapisać niektóre postacie, wstępnie definiując range(N).
Wrzlprmft
1

Python z Numpy, 146 143 bajtów

Tak jak obiecałem, oto moje własne rozwiązanie. Wymaga wprowadzenia nieujemnych liczb całkowitych:

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)

Wadą jest to, że powoduje to pęknięcie pamięci na dużą mlub max(A).

Oto najczęściej nieoznaczona i skomentowana wersja:

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
źródło
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
Alexander E. Hillaris
źródło
3
Witamy w PPCG.SE! Jest to wyzwanie polegające na kodzie golfowym, którego celem jest rozwiązanie problemu z jak najmniejszą liczbą postaci. Dodaj wersję bez komentarzy, minimalne białe znaki i jednoznakowe nazwy zmiennych (i wszelkie inne skróty, jakie możesz wymyślić), a także liczbę bajtów w tym kodzie.
Martin Ender