Calcola l'entropia a blocchi


12

Una volta avevo bisogno di scrivere una funzione che calcola l'entropia di blocco di una determinata serie di simboli per una data dimensione di blocco e sono rimasto sorpreso dalla breve durata del risultato. Quindi ti sfido a codegolf tale funzione. Non ti sto dicendo quello che ho fatto per ora (e in quale lingua), ma lo farò tra una settimana o giù di lì, se nessuno ha avuto prima le stesse idee o le idee migliori.

Definizione dell'entropia a blocchi:

Data una sequenza di simboli A = A_1,…, A_n e una dimensione del blocco m:

  • Un blocco di dimensioni m è un segmento di m elementi consecutivi della sequenza di simboli, ovvero A_i,…, A_ (i + m − 1) per ogni i appropriato.
  • Se x è una sequenza di simboli di dimensione m, N (x) indica il numero di blocchi di A che sono identici a x.
  • p (x) è la probabilità che un blocco da A sia identico a una sequenza di simboli x di dimensione m, ovvero p (x) = N (x) / (n − m + 1)
  • Infine, l'entropia del blocco per la dimensione del blocco m di A è la media di −log (p (x)) su tutti i blocchi x della dimensione m in A o (che è equivalente) la somma di −p (x) · log (p (x)) su ogni x di dimensione m presente in A. (Puoi scegliere qualsiasi logaritmo ragionevole che desideri.)

Limitazioni e chiarimenti:

  • La tua funzione dovrebbe prendere come argomento la sequenza di simboli A e la dimensione del blocco m.
  • Si può presumere che i simboli siano rappresentati come numeri interi a base zero o in un altro formato conveniente.
  • Il tuo programma dovrebbe essere in grado di accettare qualsiasi argomento ragionevole in teoria e in realtà dovrebbe essere in grado di calcolare il caso di esempio (vedi sotto) su un computer standard.
  • Le funzioni e le librerie integrate sono consentite, purché non eseguano grandi parti della procedura in una chiamata, vale a dire, estraendo tutti i blocchi di dimensione m da A, contando il numero di occorrenze di un dato blocco x o calcolando le entropie da una sequenza di valori p: devi fare queste cose da solo.

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]

Le prime entropie a blocchi di questa sequenza sono (per il logaritmo naturale):

  • m = 1: 1.599
  • m = 2: 3.065
  • m = 3: 4.067
  • m = 4: 4.412
  • m = 5: 4.535
  • m = 6: 4.554

@ m.buettner: se consideri il limite della tua soluzione per quanto riguarda le mie regole, potresti ancora provarlo - Voglio davvero solo evitare soluzioni sulla falsariga di entropy(probabilities(blocks(A,m))).
Wrzlprmft

Non è consuetudine utilizzare log base 2 per questo?
Jonathan Van Matre,

I valori per l'entropia alla fine sono positivi, ma il logaritmo di una probabilità è negativo o zero. Pertanto nella formula per l'entropia manca un segno negativo.
Heiko Oberdiek,

@JonathanVanMatre: per quanto ne so, dipende dalla disciplina che è il passo più usato del logaritmo. Comunque, non dovrebbe importare molto per la sfida e quindi puoi usare qualunque base ragionevole tu voglia.
Wrzlprmft

@HeikoOberdiek: Grazie, l'ho dimenticato.
Wrzlprmft

Risposte:


6

Mathematica - 81 78 75 72 67 65 62 56 byte

Non ho mai giocato a golf in Mathematica prima, quindi suppongo che ci sia spazio per miglioramenti. Questo non è del tutto conforme alle regole a causa delle funzioni Partitione Tally, ma è abbastanza pulito, quindi ho pensato di pubblicarlo comunque.

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

Funziona con qualsiasi set di simboli e può essere usato come

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

Ecco una versione in qualche modo ungolfed:

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

Probabilmente funzionerà più velocemente se applico Ndirettamente al risultato di Tally.

A proposito, Mathematica ha effettivamente una Entropyfunzione, che la riduce a 28 byte , ma questo è decisamente contro le regole.

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

D'altra parte, ecco una versione da 128 byte che reimplementa Partitione 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]]
)

Partitione Tallynon sono casi limite, stanno infrangendo completamente le regole, poiché stanno "estraendo tutti i blocchi di dimensione m da A" e "contando il numero di occorrenze di un dato blocco x", rispettivamente, in una chiamata. Tuttavia, dopo tutto quello che so di Mathematica, non sarei sorpreso se ci fosse una soluzione decente senza di loro.
Wrzlprmft

1
@Wrzlprmft Ho aggiunto una versione non tanto giocata dove reimplemento Partitione Tally.
Martin Ender,

3

Perl, 140 byte

Il seguente script Perl definisce una funzione Eche accetta la sequenza di simboli, seguita dalla dimensione del segmento come argomenti.

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}

Versione non golfata con test

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;

Risultato:

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

simboli:

I simboli non sono limitati a numeri interi, poiché viene utilizzata la corrispondenza del modello basata sulle stringhe. La rappresentazione in forma di stringa di un simbolo non deve contenere la virgola, poiché viene utilizzata come delimitatore. Naturalmente, simboli diversi devono avere rappresentazioni di stringhe diverse.

Nella versione golfata, la rappresentazione in stringa dei simboli non deve contenere caratteri speciali di motivi. I quattro byte aggiuntivi \Q... \E non sono necessari per i numeri.


Può essere di circa 1/4 più breve: 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}; dove $sc'è un riferimento, $re %hsono ripristinati su undef con il 1o compito, gli elenchi sono chiavi hash (con poco aiuto di $;, e alcuni x- purtroppo), e un po 'meno complicato in generale, penso.
user2846289

@VadimR: Clever! A causa delle modifiche sostanziali che suggerisco, tu rispondi. Lo spazio in values %hnon è necessario, quindi la tua soluzione richiede solo 106 byte.
Heiko Oberdiek,

2

Pitone 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

Modificato per non infrangere più le regole e avere un algoritmo leggermente più carino. Regolato per essere più piccolo

Versione precedente:

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

Il mio primo script Python in assoluto! Guardalo in azione: http://pythonfiddle.com/entropy


Bello finora, ma sfortunatamente, l'uso della countfunzione è decisamente contrario alle regole, poiché sta "contando il numero di occorrenze di un dato blocco x".
Wrzlprmft

Inoltre, alcuni consigli sul golf: è possibile salvare alcuni personaggi stipando ogni riga tranne la prima in una (separate da ;se necessario). Anche le parentesi quadre nell'ultima riga non sono necessarie.
Wrzlprmft

Bella risposta. Ancora una volta, alcuni consigli sul golf: l'intera conversione da booleano a intero (cioè and 1 or 0) non è necessaria. Inoltre, puoi salvare alcuni personaggi predefinendo range(N).
Wrzlprmft,

1

Python con Numpy, 146 143 byte

Come promesso, ecco la mia soluzione. Richiede un input di numeri interi non negativi:

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)

Lo svantaggio è che questo esplode la tua memoria per un grande mo max(A).

Ecco la versione per lo più ungolf e commentata:

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

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
Benvenuto in PPCG.SE! Questa è una sfida del codice golf, in cui l'obiettivo è quello di risolvere un problema nel minor numero di caratteri possibile. Aggiungi una versione senza commenti, spazi minimi e nomi di variabili a carattere singolo (e qualsiasi altra scorciatoia a cui puoi pensare), nonché il numero di byte in quel codice.
Martin Ender,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.