Berechnen Sie die Blockentropie

12

Ich musste einmal eine Funktion schreiben, die die Blockentropie einer bestimmten Symbolserie für eine bestimmte Blockgröße berechnet, und war überrascht, wie kurz das Ergebnis war. Daher fordere ich Sie heraus, eine solche Funktion zu codegolfen. Ich erzähle Ihnen nicht, was ich im Moment getan habe (und in welcher Sprache), aber ich werde es in einer Woche oder so tun, wenn niemand zuerst die gleichen oder bessere Ideen hat.

Definition der Blockentropie:

Ausgehend von einer Symbolfolge A = A_1,…, A_n und einer Blockgröße m:

  • Ein Block der Größe m ist ein Segment von m aufeinanderfolgenden Elementen der Symbolsequenz, dh A_i, ..., A_ (i + m - 1) für jedes geeignete i.
  • Wenn x eine Symbolfolge der Größe m ist, bezeichnet N (x) die Anzahl von Blöcken von A, die mit x identisch sind.
  • p (x) ist die Wahrscheinlichkeit, dass ein Block aus A mit einer Symbolfolge x der Größe m identisch ist, dh p (x) = N (x) / (n - m + 1)
  • Schließlich ist die Blockentropie für die Blockgröße m von A der Durchschnitt von –log (p (x)) über alle Blöcke x der Größe m in A oder (was äquivalent ist) die Summe von –p (x) · log (p (x)) über jedes x der Größe m in A. (Sie können einen beliebigen sinnvollen Logarithmus wählen.)

Einschränkungen und Klarstellungen:

  • Ihre Funktion sollte die Symbolfolge A sowie die Blockgröße m als Argument verwenden.
  • Sie können davon ausgehen, dass die Symbole als Ganzzahlen auf Nullbasis oder in einem anderen geeigneten Format dargestellt werden.
  • Ihr Programm sollte in der Lage sein, theoretisch alle vernünftigen Argumente aufzunehmen, und in der Realität sollte es in der Lage sein, den Beispielfall (siehe unten) auf einem Standardcomputer zu berechnen.
  • Eingebaute Funktionen und Bibliotheken sind zulässig, solange sie nicht große Teile der Prozedur in einem Aufruf ausführen, dh alle Blöcke der Größe m aus A extrahieren, die Anzahl der Vorkommen eines bestimmten Blocks x zählen oder die Entropien berechnen aus einer Folge von p-Werten - diese Dinge müssen Sie selbst tun.

Prüfung:

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

Die ersten Blockentropien dieser Sequenz sind (für den natürlichen Logarithmus):

  • 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
quelle
@m.buettner: Wenn Sie Ihre Lösungsgrenze in Bezug auf meine Regeln berücksichtigen, können Sie es trotzdem versuchen - ich möchte wirklich nur Lösungen im Sinne von vermeiden entropy(probabilities(blocks(A,m))).
Wrzlprmft
Ist es nicht üblich, hierfür die Protokollbasis 2 zu verwenden?
Jonathan Van Matre
Die Werte für die Entropie am Ende sind positiv, aber der Logarithmus einer Wahrscheinlichkeit ist negativ oder Null. Daher fehlt in der Formel für die Entropie ein negatives Vorzeichen.
Heiko Oberdiek
@JonathanVanMatre: Soweit ich weiß, kommt es auf die Disziplin an, die vom Logarithmus am häufigsten verwendet wird. Auf jeden Fall sollte es für die Herausforderung nicht so wichtig sein, und Sie können jede Basis verwenden, die Sie für sinnvoll halten.
Wrzlprmft
@HeikoOberdiek: Danke, das habe ich vergessen.
Wrzlprmft

Antworten:

6

Mathematica - 81 78 75 72 67 65 62 56 Bytes

Ich habe noch nie in Mathematica Golf gespielt, daher gibt es wahrscheinlich noch Verbesserungspotenzial. Dieser entspricht aufgrund der Partitionund TallyFunktionen nicht ganz den Regeln , ist aber recht ordentlich, so dass ich dachte, ich würde ihn trotzdem posten.

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

Dies funktioniert mit jedem Satz von Symbolen und kann wie folgt verwendet werden

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

Hier ist eine etwas ungolfed Version:

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

Es wird wahrscheinlich schneller laufen, wenn ich mich Ndirekt auf das Ergebnis von bewerbe Tally.

Übrigens hat Mathematica tatsächlich eine EntropyFunktion, die dies auf 28 Byte reduziert , aber das verstößt definitiv gegen die Regeln.

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

Auf der anderen Seite gibt es hier eine 128-Byte- Version, die neu implementiert Partitionund 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]]
)
Martin Ender
quelle
Partitionund Tallysind keine Grenzfälle, sie brechen die Regeln geradezu, da sie "alle Blöcke der Größe m aus A extrahieren" und "die Anzahl der Vorkommen eines bestimmten Blocks x zählen" in einem Aufruf. Nach allem, was ich über Mathematica weiß, wäre ich nicht überrascht, wenn es ohne sie eine vernünftige Lösung gäbe.
Wrzlprmft
1
@Wrzlprmft Ich habe eine nicht so golfene Version hinzugefügt, in der ich neu implementiere Partitionund Tally.
Martin Ender
3

Perl, 140 Bytes

Das folgende Perl-Skript definiert eine Funktion E, die die Symbolsequenz gefolgt von der Segmentgröße als Argument verwendet.

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}

Ungolfed Version mit 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;

Ergebnis:

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

Symbole:

Die Symbole sind nicht auf Ganzzahlen beschränkt, da ein auf Zeichenfolgen basierender Mustervergleich verwendet wird. Die Zeichenfolgendarstellung eines Symbols darf kein Komma enthalten, da es als Trennzeichen verwendet wird. Natürlich müssen verschiedene Symbole unterschiedliche Zeichenfolgendarstellungen haben.

In der Golfversion sollte die Zeichenfolgendarstellung der Symbole keine Sonderzeichen von Mustern enthalten. Die zusätzlichen vier Bytes \Q... \E werden für Zahlen nicht benötigt.

Heiko Oberdiek
quelle
Kann sein , etwa ein Viertel kürzer: 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}; wo $sist ein Verweis, $rund %hwerden mit 1. Zuweisung auf undef zurückgesetzt , Listen sind Hash-Schlüssel (mit etwas Hilfe von $;und einigen x- leider) und etwas weniger kompliziert im Allgemeinen, denke ich.
user2846289
@VadimR: Clever! Aufgrund der wesentlichen Änderungen, die ich vorschlage, geben Sie eine Antwort. Der Platz in values %hwird nicht benötigt, daher benötigt Ihre Lösung nur 106 Bytes.
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

Angepasst, um die Regeln nicht mehr zu brechen und einen etwas niedlicheren Algorithmus zu haben. Angepasst, um kleiner zu sein

Ältere Version:

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

Mein erstes Python-Skript! Sehen Sie es in Aktion: http://pythonfiddle.com/entropy

Alexander-Brett
quelle
Bislang countist das nett , aber leider verstößt die Verwendung der Funktion direkt gegen die Regeln, da sie "die Anzahl der Vorkommen eines bestimmten Blocks x zählt".
Wrzlprmft
Außerdem einige Golftipps: Sie können einige Zeichen speichern, indem Sie jede Zeile mit Ausnahme der ersten in eine Zeile packen ( ;ggf. durch Trennzeichen getrennt ). Auch die eckigen Klammern in der letzten Zeile werden nicht benötigt.
Wrzlprmft
Gute Antwort. Nochmals einige Golftipps: Die gesamte Konvertierung von Boolean zu Integer (dh and 1 or 0) ist nicht erforderlich. Sie können auch einige Zeichen speichern, indem Sie diese vordefinieren range(N).
Wrzlprmft
1

Python mit Numpy, 146 143 Bytes

Wie versprochen, hier ist meine eigene Lösung. Es erfordert die Eingabe von nicht negativen ganzen Zahlen:

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)

Der Nachteil ist, dass dies Ihr Gedächtnis für eine große moder platzt max(A).

Hier ist die meist ungolfed und kommentierte Version:

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
quelle
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
quelle
3
Willkommen bei PPCG.SE! Hierbei handelt es sich um eine Code-Golf-Herausforderung, bei der es darum geht, ein Problem in möglichst wenigen Zeichen zu lösen. Bitte fügen Sie eine Version ohne Kommentare, minimale Leerzeichen und Namen von Variablen mit einem Zeichen (und alle anderen denkbaren Verknüpfungen) sowie die Anzahl der Bytes in diesem Code hinzu.
Martin Ender