Hitung estimasi entropi histogram string

19

Tulis program atau fungsi yang memperkirakan entropi Shannon dari string yang diberikan.

Jika string memiliki n karakter, d karakter berbeda , x i adalah karakter berbeda ke- i , dan P (x i ) adalah probabilitas karakter tersebut muncul dalam string, maka estimasi entropi Shannon kami untuk string tersebut diberikan oleh:

H = -n \ jumlah \ limit_ {i = 1} ^ d P (x_i) \ log_2 P (x_i)

Untuk estimasi dalam tantangan ini, kita mengasumsikan bahwa probabilitas suatu karakter yang muncul dalam sebuah string hanyalah berapa kali itu terjadi dibagi dengan jumlah total karakter.

Jawaban Anda harus akurat setidaknya 3 digit setelah periode.


Kasus uji:

"This is a test.", 45.094
"00001111", 8.000
"cwmfjordbankglyphsvextquiz", 122.211
"             ", 0.0
orlp
sumber
Menentang tantangan yang biasa saya lakukan, yang ini terlihat rumit, tetapi sebenarnya cukup sederhana :)
orlp
Apakah aman untuk mengasumsikan ASCII yang dapat dicetak untuk string input?
AdmBorkBork
@TimmyD Tidak. Semua string yang didukung oleh tipe string bahasa Anda.
orlp
Sayangnya, Mathematica Entropymenghitung bit per karakter, bukan total untuk string; oh well ...
2012rcampion

Jawaban:

2

Jelly, 11 8 byte

ċЀ÷Ll.S

Cobalah online!

Dennis
sumber
Bisakah saya bertanya, bagaimana Anda memasukkan karakter-karakter itu? Dengan salin dan tempel?
Bálint
Setidaknya di Linux, semuanya dapat diketik di papan ketik internasional AS.
Dennis
11

Python 3.3+, 64 byte

import math
lambda s:sum(math.log2(len(s)/s.count(c))for c in s)

Dapatkan math.log2dari solusi mbomb007 .

Tidak
sumber
Jadi @orlp tidak memberi kami formula yang disederhanakan sepenuhnya, eh ...?
mbomb007
@ mbomb007 Tergantung untuk tujuan apa yang Anda sederhanakan. Menulis dalam hal probabilitas dan karakter yang berbeda adalah wajar sebagai definisi, tetapi untuk bermain golf lebih pendek untuk bekerja dengan jumlah dan mengulangi semua karakter.
xnor
1
Pyth menjawab dengan rumus Anda: pyth.herokuapp.com/... 8 bytes
Maltysen
2

APL, 18 14 byte

+/2⍟≢÷(+/∘.=⍨)

Ini adalah kereta fungsi monadik tanpa nama yang menerima string di sebelah kanan dan mengembalikan yang asli.

Seperti semua hal baik dalam hidup, ini menggunakan rumus xnor . Kami mendapatkan matriks boolean yang sesuai dengan kemunculan setiap karakter dalam string menggunakan ∘.=⍨, jumlah ini sepanjang sumbu pertama ( +/) untuk mendapatkan jumlah kemunculan masing-masing karakter, bagi panjang string dengan masing-masing, kemudian ambil basis log 2 ( 2⍟) dan jumlah.

Coba di sini

Disimpan 4 byte berkat Dennis!

Alex A.
sumber
1

MATL, 17 byte

S4#Y'ts/tZl*sGn_*

Cobalah online!

gelas kimia
sumber
Anda mungkin dapat menyimpan beberapa byte denganYm
Luis Mendo
1

JavaScript (ES6), 67 byte

s=>[...s].map(c=>t+=Math.log2(s.length/~-s.split(c).length),t=0)&&t

Saya perlu menggunakan ~-s.splitkarena menerima string daripada regexps. Seperti biasa, mapketukan reducesatu byte.

s=>[...s].reduce((t,c)=>t+Math.log2(s.length/~-s.split(c).length),0)
Neil
sumber
1

Perl 5, 58 byte

Subrutin:

{for$a(@a=split'',pop){$t+=(log@a/grep/\Q$a/,@a)/log 2}$t}

Ujung topi saya untuk xnor untuk formula.

msh210
sumber
-Ftidak berfungsi (dalam Strawberry, lagi pula) karena termasuk $/.
msh210
1

MATL , 14 byte

!Gu=stGn/Zl*s|

Cobalah online!

!      % transpose implicit input into column vector
Gu     % row vector with unique elements of input
=      % test for equality, element-wise with broadcast
s      % sum of each column
tGn/   % duplicate. Divide by number of input characters
Zl     % binary logarithm
*      % element-wise multiplication
s      % sum of array
|      % absolute value. Display implicitly
Luis Mendo
sumber
1

Julia, 37 byte

x->sum(log2(endof(x)./sum(x.==x',1)))

Mengambil array karakter sebagai input. Cobalah online!

Dennis
sumber
1

J - 18 16 14 byte

1#.2^.#%1#.=/~

Dipersingkat menggunakan ide dalam metode Dennis.

Pemakaian

   f =: 1#.2^.#%1#.=/~
   f 'This is a test.'
45.0936
   f '00001111'
8
   f 'cwmfjordbankglyphsvextquiz'
122.211
   f '             '
0

Penjelasan

1#.2^.#%1#.=/~  Input: string S
           =/~  Create a table testing for equality
        1#.     Convert each row from a list of base 1 digits to decimal
                This is equivalent to taking the sum and forms a list of tallies
      #         Get the length of S
       %        Divide the length by each tally
   2^.          Log base 2 of each
1#.             "Sum" those values and return
mil
sumber
1
Saya tidak berpikir ini dianggap sebagai fungsi. Jika Anda menetapkan kode ke variabel, ia melakukan sesuatu yang sama sekali berbeda.
Dennis
@ Dennis Dari apa yang saya kumpulkan, tampaknya J menafsirkannya sebagai rantai komposisi, menggunakan 3 : '... y'dengan sintaks yang sama akan menjadi cara yang valid untuk mendefinisikannya sebagai fungsi. J menyatakan bahwa itu mengevaluasi dari kanan ke kiri, jadi saya telah refactored kode saya sebagai kereta. Saya tidak suka topi [:tapi saya tidak bisa menemukan cara lain untuk membuat kereta.
mil
0

Jolf, 26 byte

_*liuΜGμiEd*γ/l miLeHlimzγ

Coba di sini! (Perhatikan bahwa fungsi test suite borked.)

Penjelasan

_*liuΜGμiEd*γ/l miLeHlimzγ
       μi                   unique members of i
      G  E                  split on ""
     Μ    d                 map over function
               _miLeH       match i with regex escaped member
             /l      li     divide length of (^) by length of i
            γ               γ = (^)
           *           mzγ  (^) * log_2(γ)
 *li                        (^) * length of i
_                           negate
Conor O'Brien
sumber
0

Python 3.3+, 95 91 89 85 byte

Solusi sederhana. Versi 3.3 diperlukan untuk digunakan math.log2.

import math
def f(s):C=s.count;return-sum(C(x)*math.log2(C(x)/len(s))for x in set(s))

Cobalah online

mbomb007
sumber
Apakah Anda pikir ada sesuatu yang tidak perlu di sini? n*sum(s.count(c)/n
orlp
@ orlp Terima kasih. Saya awalnya memiliki fungsi terpisah untuk menemukan probabilitas, tetapi telah menyisipkannya di dalam dua kali dan menghapusnya untuk menghemat karakter.
mbomb007
Anda tidak harus menyimpan ndalam variabel sekarang karena Anda hanya menggunakannya sekali.
Maltysen
0

Java 7, 207 byte

double C(String x,Map<Character,Integer>f){double H=0,g;for(char c:x.toCharArray())f.put(c,f.containsKey(c)?f.get(c)+1:1);for(char c:f.keySet()){g=f.get(c);H+=g*Math.log(g/x.length())/Math.log(2);}return-H;}

Detail coba online

double log2(double d) { return Math.log(d) / Math.log(2); }

double C(String x, Map<Character,Integer>f)
{
    double H=0,g;

    // frequency
    for(char c : x.toCharArray())
    {
        f.put(c, f.containsKey(c) ? f.get(c)+1 : 1);
    }

    // calculate entropy
    for(char c : f.keySet())
    {
        g = f.get(c);
        H += g * log2(g / x.length());
    }

    return -H;
}
Khaled.K
sumber
0

Faktor, 98 byte

[ [ length ] [ dup [ [ = ] curry dupd count ] { } map-as nip ] bi [ / log 2 log / ] with map sum ]

Ini adalah terjemahan langsung dari jawaban Python ini . Saya akan menambahkan penjelasan saat makan malam.

kucing
sumber
0

Racket, 130 byte

: c

#lang racket
(require math)(λ(S)(let([s(string->list S)])(sum(map(λ(c)(/(log(/(length s)(count(λ(x)(char=? c x))s)))(log 2)))s))))

Terjemahan dari jawaban Factor saya, jadi ini adalah terjemahan tidak langsung dari jawaban Python Kenny Lau.

kucing
sumber
0

k (32 byte)

{-+/c*(log c%n:+/c:#:'=x)%log 2}

Atau dalam q, terjemahannya tidak sesingkat itu tetapi lebih jelas:

{neg sum c*2 xlog c%n:sum c:count each group x}
skeevey
sumber
0

Mathematica, 45 byte

Tr[Log[2,Tr@#/#]#]&@Values@CharacterCounts@#&

Pemakaian

Ini mengembalikan hasil yang tepat sehingga kami memperkirakannya dengan N.

  f = Tr[Log[2,Tr@#/#]#]&@Values@CharacterCounts@#&
  f["This is a test."]//N
45.0936
  f["00001111"]//N
8.
  f["cwmfjordbankglyphsvextquiz"]//N
122.211
  f["             "]//N
0.
mil
sumber
0

R, 67 byte

l=length(i<-strsplit(readline(),"")[[1]]);-sum(log2(l/table(i)[i]))

Penjelasan

Ambil input dari stdin dan bagi menjadi daftar karakter. (Sintaks kikuk inilah yang menyebabkan tantangan golf sangat sulit di R ...)

         i<-strsplit(readline(),"")[[1]])

Tugas ini disembunyikan di dalam sebuah lengthperintah, jadi kami mendapatkan dua tugas dengan harga satu. Kami punya i, daftar karakter, dan lpanjangnya.

l=length(i<-strsplit(readline(),"")[[1]]);

Sekarang kita menghitung entropi. R memiliki fungsi tableyang bagus yang mengembalikan jumlah semua nilai unik. Untuk input This is a test, table(i)kembali

> table(i)
i
  . a e h i s t T 
3 1 1 1 1 2 3 2 1

Ini diindeks oleh karakter, yang bagus, karena kita dapat menggunakan isebagai indeks untuk mendapatkan hitungan setiap karakter, seperti:

> table(i)[i]
i
T h i s   i s   a   t e s t . 
1 1 2 3 3 2 3 3 1 3 2 1 3 2 1 

Sisa kode selanjutnya merupakan implementasi sederhana dari rumus entropi, diputar sedikit.

                                           -sum(log2(l/table(i)[i]))
rturnbull
sumber
Simpan dua byte (juga kiriman Anda tidak berfungsi pada TIO)
JayCe
0

C #, 159 byte

Golf:

string f(string s){var l=s.Length;double sum=0;foreach(var item in s.GroupBy(o=>o)){double p=(double)item.Count()/l;sum+=p*Math.Log(p,2);}return(sum*=-l)+"";}}

Tidak Disatukan:

string f(string s)
{
  var l = s.Length;
  double sum = 0;
  foreach (var item in s.GroupBy(o => o))
  {
    double p = (double)item.Count() / l;
    sum += p * Math.Log(p, 2);
  }
  return (sum *= -l) + "";
}

Uji:

var codeGolf = new StringHistogramEntropyEstimation();
    Console.WriteLine(codeGolf.f("This is a test.")); //45.0935839298008
    Console.WriteLine(codeGolf.f("00001111")); //8
    Console.WriteLine(codeGolf.f("cwmfjordbankglyphsvextquiz")); //122.211432671668
    Console.WriteLine(codeGolf.f("             ")); //0
Pete Arden
sumber
0

Groovy, 100 Bytes

{a->n=a.size();a.toList().unique().collect{p=a.count(it)/n;p*(Math.log(p)/Math.log(2.0f))}.sum()*-n}

Tes:

This is a test. = 45.09358393449714
00001111 = 8.0
cwmfjordbankglyphsvextquiz = 122.21143275636976
aaaaaaaa = -0.0
Guci Gurita Ajaib
sumber