Calcola la stima dell'entropia dell'istogramma di una stringa


19

Scrivi un programma o una funzione che stima l'entropia di Shannon di una determinata stringa.

Se una stringa ha n caratteri, d caratteri distinti , x i è il primo carattere distinto e P (x i ) è la probabilità che quel carattere si verifichi nella stringa, la nostra stima dell'entropia di Shannon per quella stringa è data da:

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

Per la stima in questa sfida ipotizziamo che la probabilità che un personaggio si presenti in una stringa è semplicemente il numero di volte in cui si presenta diviso per il numero totale di caratteri.

La tua risposta deve essere accurata con almeno 3 cifre dopo il periodo.


Casi test:

"This is a test.", 45.094
"00001111", 8.000
"cwmfjordbankglyphsvextquiz", 122.211
"             ", 0.0

Contrariamente alle mie solite sfide, questa sembra complicata, ma in realtà è abbastanza semplice :)
orlp


È sicuro assumere ASCII stampabile per la stringa di input?
AdmBorkBork,

@TimmyD No. Qualsiasi stringa supportata dal tipo di stringa della tua lingua.
orlp,

Sfortunatamente, Mathematica Entropyconta i bit per carattere, non il totale per la stringa; vabbè ...
2012rcampion

Risposte:


2

Gelatina, 11 8 byte

ċЀ÷Ll.S

Provalo online!


Posso chiederti come inserisci questi personaggi? Con copia e incolla?
Bálint,

Almeno su Linux, possono essere tutti digitati sulla tastiera internazionale degli Stati Uniti.
Dennis,

11

Python 3.3+, 64 byte

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

Ottenuto math.log2dalla soluzione di mbomb007 .


Quindi @orlp non ci ha dato una formula completamente semplificata, eh ...?
mbomb007,

@ mbomb007 Dipende dallo scopo che stai semplificando. Scriverlo in termini di probabilità e caratteri distinti è naturale come una definizione, ma per il golf è più breve lavorare con conteggi e iterare su tutti i personaggi.
xnor

1
Pyth rispondi con la tua formula: pyth.herokuapp.com/… 8 byte
Maltysen

2

APL, 18 14 byte

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

Questo è un treno di funzioni monadico senza nome che accetta una stringa a destra e restituisce un valore reale.

Come tutte le cose buone della vita, questo usa la formula di xnor . Otteniamo una matrice di valori booleani corrispondenti alle occorrenze di ciascun carattere nella stringa usando ∘.=⍨, somma questo lungo il primo asse ( +/) per ottenere il numero di occorrenze di ciascun carattere, dividere la lunghezza della stringa per ciascuno, quindi prendere la base di registro 2 ( 2⍟) e somma.

Provalo qui

Risparmiato 4 byte grazie a Dennis!



1

JavaScript (ES6), 67 byte

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

Devo usare ~-s.splitperché accetta stringhe piuttosto che regexps. Come al solito, mapbatte reducedi un byte.

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

1

Perl 5, 58 byte

Una subroutine:

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

Una punta del mio cappello per xnor per la formula.


-Fnon funziona (in Strawberry, comunque) perché include il file $/.
msh210,

1

MATL , 14 byte

!Gu=stGn/Zl*s|

Provalo 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


1

J - 18 16 14 byte

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

Abbreviato usando l'idea nel metodo di Dennis.

uso

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

Spiegazione

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

1
Non penso che questo valga come una funzione. Se assegni il codice a una variabile, fa qualcosa di completamente diverso.
Dennis,

@Dennis Da quello che raccolgo, sembra che J lo interpreti come una catena di composizioni, usando 3 : '... y'con la stessa sintassi sarebbe un modo valido per definirlo come una funzione. J afferma che valuta da destra a sinistra, quindi ho modificato il mio codice come un treno. Non mi piacciono i cappellini [:ma non riesco a trovare un altro modo per fare un treno.
miglia,


0

Jolf, 26 byte

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

Provalo qui! (Si noti che la funzione della suite di test è interrotta.)

Spiegazione

_*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

0

Python 3.3+, 95 91 89 85 byte

Soluzione semplice. Per utilizzare è necessaria la versione 3.3 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))

Provalo online


Pensi che ci sia qualcosa di inutile qui? n*sum(s.count(c)/n
orlp,

@orlp Grazie. Inizialmente avevo una funzione separata per trovare la probabilità, ma l'avevo incollata due volte all'interno e l'avevo cancellata per salvare i caratteri.
mbomb007,

Non è necessario archiviare nuna variabile ora che la si utilizza solo una volta.
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;}

Prova dettagliata 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;
}

0

Fattore, 98 byte

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

Questa è una traduzione diretta di questa risposta Python . Aggiungerò una spiegazione durante la cena.


0

Racchetta, 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))))

Traduzione della mia risposta Factor, quindi è una traduzione indiretta della risposta Python di Kenny Lau.


0

k (32 byte)

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

O in q, la traduzione non è poi così breve ma più chiara:

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

0

Mathematica, 45 byte

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

uso

Questo restituisce risultati esatti, quindi li approssimiamo con 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.

0

R, 67 byte

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

Spiegazione

Prendi input da stdin e dividilo in un elenco di caratteri. (Questa sintassi ingombrante è il motivo per cui le sfide del golf string sono così difficili in R ...)

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

Questa assegnazione è nascosta all'interno di un lengthcomando, quindi otteniamo due assegnazioni al prezzo di una. Abbiamo il'elenco dei personaggi e la lsua lunghezza.

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

Ora calcoliamo l'entropia. R ha una bella funzione tableche restituisce i conteggi di tutti i valori univoci. Per input This is a test, table(i)ritorni

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

Questo è indicizzato da caratteri, il che è carino, poiché possiamo quindi usare icome indice per ottenere il conteggio di ogni personaggio, in questo modo:

> 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 

Il resto del codice è quindi una semplice implementazione della formula entropica, capovolta un po '.

                                           -sum(log2(l/table(i)[i]))

Salva due byte (anche il tuo invio non funziona su TIO)
JayCe


0

C #, 159 byte

golfed:

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)+"";}}

Ungolfed:

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) + "";
}

Test:

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

0

Groovy, 100 byte

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

test:

This is a test. = 45.09358393449714
00001111 = 8.0
cwmfjordbankglyphsvextquiz = 122.21143275636976
aaaaaaaa = -0.0
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.