Tính toán entropy khối


12

Tôi đã từng cần phải viết một hàm tính toán entropy khối của một chuỗi ký hiệu nhất định cho một kích thước khối nhất định và rất ngạc nhiên khi kết quả ngắn như thế nào. Vì vậy, tôi đang thách thức bạn để codegolf một chức năng như vậy. Tôi không nói cho bạn biết tôi đã làm gì bây giờ (và bằng ngôn ngữ nào), nhưng tôi sẽ trong một tuần hoặc lâu hơn, nếu không ai nghĩ ra ý tưởng tương tự hoặc tốt hơn trước.

Định nghĩa của entropy khối:

Cho một chuỗi ký hiệu A = A_1, Mạnh, A_n và kích thước khối m:

  • Một khối có kích thước m là một phân đoạn gồm các phần tử m liên tiếp của chuỗi ký hiệu, nghĩa là A_i, Vượt, A_ (i + m 1) cho bất kỳ i thích hợp nào.
  • Nếu x là một chuỗi ký hiệu có kích thước m, N (x) biểu thị số khối của A giống hệt với x.
  • p (x) là xác suất để một khối từ A giống hệt với chuỗi ký hiệu x có kích thước m, tức là p (x) = N (x) / (n m + 1)
  • Cuối cùng, entropy khối cho kích thước khối m của A là trung bình của −log (p (x)) trên tất cả các khối x có kích thước m trong A hoặc (tương đương) tổng của −p (x) · log (p (x)) trên mỗi x kích thước m xảy ra ở A. (Bạn có thể chọn bất kỳ logarit hợp lý nào bạn muốn.)

Hạn chế và làm rõ:

  • Hàm của bạn sẽ lấy chuỗi ký hiệu A cũng như kích thước khối m làm đối số.
  • Bạn có thể giả sử rằng các ký hiệu được biểu diễn dưới dạng số nguyên dựa trên số 0 hoặc ở định dạng thuận tiện khác.
  • Chương trình của bạn phải có khả năng đưa ra bất kỳ đối số hợp lý nào trong lý thuyết và trong thực tế sẽ có thể tính toán trường hợp ví dụ (xem bên dưới) trên một máy tính tiêu chuẩn.
  • Các hàm và thư viện tích hợp được cho phép, miễn là chúng không thực hiện các phần lớn của quy trình trong một cuộc gọi, nghĩa là trích xuất tất cả các khối có kích thước m từ A, đếm số lần xuất hiện của một khối x cho trước hoặc tính toán các entropi từ một chuỗi các giá trị p - bạn phải tự làm những việc đó.

Kiểm tra:

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

Các entropies khối đầu tiên của chuỗi này là (đối với logarit tự nhiên):

  • 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: Nếu bạn xem xét giới hạn giải pháp của mình liên quan đến các quy tắc của tôi, bạn vẫn có thể dùng thử - Tôi thực sự chỉ muốn tránh các giải pháp dọc theo dòng entropy(probabilities(blocks(A,m))).
Wrzlprmft

Không phải là thông thường để sử dụng cơ sở đăng nhập 2 cho việc này sao?
Jonathan Van Matre

Các giá trị của entropy ở cuối là dương, nhưng logarit của xác suất là âm hoặc bằng không. Do đó, một dấu âm bị thiếu trong công thức của entropy.
Heiko Oberdiek

@JonathanVanMatre: Theo như tôi biết, nó phụ thuộc vào ngành học được sử dụng nhiều nhất của logarit. Dù sao, nó không quan trọng lắm cho thử thách và do đó bạn có thể sử dụng bất cứ cơ sở nào hợp lý mà bạn muốn.
Wrzlprmft

@HeikoOberdiek: Cảm ơn, tôi đã quên điều đó.
Wrzlprmft

Câu trả lời:


6

Toán học - 81 78 75 72 67 65 62 56 byte

Trước đây tôi chưa chơi gôn ở Mathicala, vì vậy tôi cho rằng có chỗ để cải thiện. Điều này không hoàn toàn phù hợp với các quy tắc do Partitionvà các Tallychức năng, nhưng nó khá gọn gàng vì vậy tôi nghĩ rằng dù sao tôi cũng sẽ đăng nó.

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

Điều này hoạt động với bất kỳ bộ ký hiệu và có thể được sử dụng như

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

Đây là một phiên bản hơi vô căn cứ:

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

Nó có thể sẽ chạy nhanh hơn nếu tôi áp dụng Ntrực tiếp vào kết quả Tally.

Nhân tiện, Mathicala thực sự có một Entropyhàm, làm giảm hàm này xuống còn 28 byte , nhưng điều đó chắc chắn là trái với quy tắc.

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

Mặt khác, đây là phiên bản 128 byte sắp xếp lại PartitionTally:

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

Ung dung:

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

PartitionTallykhông phải là trường hợp đường biên, họ đang phá vỡ các quy tắc hoàn toàn, vì họ đang trích xuất tất cả các khối có kích thước m từ A và và đếm số lần xuất hiện của một khối x nhất định, trong một cuộc gọi. Tuy nhiên, sau tất cả những gì tôi biết về Mathematica, tôi sẽ không ngạc nhiên, nếu có một giải pháp hợp lý mà không có chúng.
Wrzlprmft

1
@Wrzlprmft Tôi đã thêm một phiên bản không được chơi gôn, nơi tôi thực hiện lại PartitionTally.
Martin Ender

3

Perl, 140 byte

Kịch bản Perl sau đây định nghĩa một hàm Elấy chuỗi ký hiệu, theo sau là kích thước phân đoạn làm đối số.

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}

Phiên bản thử nghiệm với thử nghiệm

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;

Kết quả:

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

Biểu tượng:

Các biểu tượng không bị giới hạn đối với số nguyên, vì khớp mẫu dựa trên chuỗi được sử dụng. Biểu diễn chuỗi của ký hiệu không được chứa dấu phẩy, vì nó được sử dụng làm dấu phân cách. Tất nhiên, các biểu tượng khác nhau phải có biểu diễn chuỗi khác nhau.

Trong phiên bản chơi gôn, biểu diễn chuỗi của các ký hiệu không được chứa các ký tự đặc biệt của các mẫu. Bốn byte bổ sung \Q... \E không cần thiết cho các số.


Có thể ngắn hơn khoảng 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}; trong đó $slà một tham chiếu $r%hđược đặt lại thành undef với phép gán thứ nhất, các danh sách là các khóa băm (với một chút trợ giúp $;và một số x- không may), nói chung và hơi phức tạp hơn một chút.
dùng2846289

@VadimR: Thông minh! Vì những thay đổi đáng kể mà tôi đề xuất, bạn đưa ra câu trả lời. Không gian trong values %hkhông cần thiết, do đó giải pháp của bạn chỉ cần 106 byte.
Heiko Oberdiek

2

Con trăn 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

Điều chỉnh để không phá vỡ các quy tắc nữa và có một thuật toán nhẹ hơn. Điều chỉnh để nhỏ hơn

Phiên bản cũ hơ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

Kịch bản Python đầu tiên của tôi! Xem nó trong hành động: http://pythonfiddle.com/entropy


Đẹp cho đến nay, nhưng thật không may, việc sử dụng countchức năng này hoàn toàn trái với quy tắc, vì nó là số đếm số lần xuất hiện của một khối x x nhất định.
Wrzlprmft

Ngoài ra, một số mẹo chơi gôn: Bạn có thể lưu một số ký tự bằng cách nhồi nhét mọi dòng trừ dòng đầu tiên thành một (cách nhau ;nếu cần thiết). Ngoài ra, dấu ngoặc vuông trong dòng cuối cùng là không cần thiết.
Wrzlprmft

Câu trả lời tốt đẹp. Một lần nữa, một số mẹo chơi gôn: Toàn bộ chuyển đổi từ boolean sang số nguyên (nghĩa là and 1 or 0) là không cần thiết. Ngoài ra, bạn có thể lưu một số ký tự bằng cách xác định trước range(N).
Wrzlprmft

1

Python với Numpy, 146 143 byte

Như đã hứa, đây là giải pháp của riêng tôi. Nó đòi hỏi đầu vào của các số nguyên không âm:

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)

Những bất lợi là điều này vỡ ký ức của bạn cho một lượng lớn mhoặcmax(A) .

Đây là phiên bản chủ yếu là vô căn cứ và nhận xét:

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
Chào mừng đến với PPCG.SE! Đây là một thử thách golf mã, trong đó mục tiêu là giải quyết vấn đề ở càng ít nhân vật càng tốt. Vui lòng thêm một phiên bản không có nhận xét, khoảng trắng tối thiểu và tên biến ký tự đơn (và bất kỳ phím tắt nào khác bạn có thể nghĩ ra) cũng như số byte trong mã đó.
Martin Ender
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.