Обчисліть блокову ентропію


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, а також розмір блоку m.
  • Можна припустити, що символи представлені у вигляді нульових цілих чисел або в іншому зручному форматі.
  • Ваша програма повинна бути здатна приймати будь-які обґрунтовані аргументи теоретично, а в реальності повинна мати можливість обчислити приклад (див. Нижче) на стандартному комп'ютері.
  • Вбудовані функції та бібліотеки дозволені до тих пір, поки вони не виконують великі частини процедури за один виклик, тобто витягують всі блоки розміром m від A, підраховуючи кількість входів даного блоку x або обчислюючи ентропії з послідовності значень p - ви повинні робити ці речі самостійно.

Тест:

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

Першими блоковими ентропіями цієї послідовності є (для природного логарифму):

  • m = 1: 1,599
  • m = 2: 3.065
  • m = 3: 4,067
  • m = 4: 4.412
  • m = 5: 4,535
  • m = 6: 4,555

@ m.buettner: Якщо ви розглядаєте ваше рішення щодо кордону щодо моїх правил, ви все одно можете спробувати - я дійсно хочу лише уникати рішень у порядку entropy(probabilities(blocks(A,m))).
Wrzlprmft

Чи не прийнято використовувати для цього базу журналів 2?
Джонатан Ван Матре

Значення ентропії в кінці є позитивними, але логарифм ймовірності від'ємний або нульовий. Тому у формулі ентропії відсутній негативний знак.
Хайко Обердік

@JonathanVanMatre: Наскільки я знаю, це залежить від дисципліни, яка є найбільш часто використовуваною темою логарифму. У будь-якому випадку, це не має великого значення для виклику, і, таким чином, ви можете використовувати будь-яку розумну базу.
Wrzlprmft

@HeikoOberdiek: Спасибі, я це забув.
Wrzlprmft

Відповіді:


6

Математика - 81 78 75 72 67 65 62 56 байт

Раніше я нічого не займався гольфами в Математиці, тому, мабуть, є місце для вдосконалення. Це один не цілком відповідають правилам з - за 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]&

Безголівки:

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- на жаль), і трохи менш складні в цілому, я думаю.
користувач2846289

@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


Наразі добре, але, на жаль, використання 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

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! Це виклик з кодом для гольфу, де мета - вирішити проблему якомога менше символів. Будь ласка, додайте версію без коментарів, мінімальний пробіл та назви однозначних змінних (та будь-які інші ярлики, які ви можете придумати), а також кількість байтів у цьому коді.
Мартін Ендер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.