0
$\begingroup$

I'm doing some frequency analysis on an encrypted plaintext. Tally[] and Characters[] make the basic task quite easy. To count the number of times each character appears (sorted alphabetically; Plaintext has been pre-sanitized), I can carry out

Sort[Tally[Characters[Plaintext], #1[[2]] > #2[[2]] &]  

For a more nuanced cryptanalytic approach, I will also want to count the frequency of each digraph (pair of adjacent characters), trigraph (triple of same), ..., n-graph. Tally[] will still work fine here, but the original splitting task (previously performed by Characters[]) is slightly more complicated. I want a functionnGraphList[PTstring_String, Chunklength_Integer] that outputs a list (including repeats) of the Chunklength-graphs found in PTstring. When Chunklength = 1, the function should give identical output to Characters[].

Okay, so the problem isn't really that hard. A quick approach is

nGraphList[PTstring_String, Chunklength_Integer]:=
 Table[
   StringTake[PTstring, {k, k + Chunklength - 1}],
 {k, 1, StringLength[PTstring] - Chunklength + 1}]

The thing drives me crazy, though. It seems like a terribly easy string operation of the sort Mathematica would be able to do in a snap. My solution is clunky, and I can't find any better ideas in the documentation or here. Can you show any different ways to do this? Any smarter or prettier ways?

Related: Clif's question hints at possible usage of stream methods, but 1) I know little on the subject and 2) it looks equally if not more clunky.

$\endgroup$
2
  • $\begingroup$ I have marked this as a duplicate. Although slightly different the solutions are easily adaptable; e.g. use StringCases[string, Repeated[_, {n}], Overlaps -> All] for your application. $\endgroup$
    – Mr.Wizard
    May 4, 2014 at 10:57
  • $\begingroup$ Thanks, @Mr.Wizard. I did spend a while looking, but I never did find that one. Hopefully Rasher and I have been at least some help to posterity :) $\endgroup$ May 5, 2014 at 19:40

1 Answer 1

1
$\begingroup$

Given string and size, outputs list of character code lists of size, and corresponding x-graph string. Add white-space removal / tally / etc. if desired:

test = "This is a test"

xGraphs[string_, size_] := With[{chars = ToCharacterCode[string]},
  {p = Partition[chars, size, 1], FromCharacterCode /@ p}]

xGraphs[test, 2]
xGraphs[test, 4]

(*

{{{84, 104}, {104, 105}, {105, 115}, {115, 32}, {32, 105}, {105, 
   115}, {115, 32}, {32, 97}, {97, 32}, {32, 116}, {116, 101}, {101, 
   115}, {115, 116}}, {"Th", "hi", "is", "s ", " i", "is", "s ", " a",
   "a ", " t", "te", "es", "st"}}

{{{84, 104, 105, 115}, {104, 105, 115, 32}, {105, 115, 32, 105}, {115,
    32, 105, 115}, {32, 105, 115, 32}, {105, 115, 32, 97}, {115, 32, 
   97, 32}, {32, 97, 32, 116}, {97, 32, 116, 101}, {32, 116, 101, 
   115}, {116, 101, 115, 116}}, {"This", "his ", "is i", "s is", 
  " is ", "is a", "s a ", " a t", "a te", " tes", "test"}}

*)

This adds tally of both to output, trims whitespace unless third argument is False:

xGraphs[string_, size_, dropWhiteSpace_: True] := 
 Module[{chars = 
    ToCharacterCode[
     If[dropWhiteSpace, 
      StringReplace[string, WhitespaceCharacter -> ""], string]], p, s},
  {p = Partition[chars, size, 1], s = FromCharacterCode /@ p, 
   Tally[p], Tally[s]}]
$\endgroup$

Not the answer you're looking for? Browse other questions tagged or ask your own question.