challenge コード中の文字の頻度分析

プログラムコード中の文字の頻度は言語によって相当にばらつきがあると思います。ある言語はピリオドが頻出するとか、別の言語はカッコの頻出頻度が高い、とか。そこで、

  • 文字の頻度解析をするプログラムを作成し、
  • 適当なプログラムに対して実行し、結果を出力して、そのような頻度になっている理由を教えてください。

(その言語で書かれた「典型的な」プログラムコード、といえるようなものがあると良いのですが・・)

簡単すぎるという方は、複数文字にしてみたり単語の頻度にしてみてください。

参考;Wikipedia 頻度分析

http://ja.wikipedia.org/wiki/%E9%A0%BB%E5%BA%A6%E5%88%86%E6%9E%90

出題者です。 こちらで用意していた回答は awk を使ったものでした。一応解説すると、組み込み変数FSを空にし、1行単位の文字毎に連想配列に格納しています。

1
2
3
4
5
6
7
8
9
# 1文字版
BEGIN { FS="" }
{ for (i=1; i<=NF; i++) ht[$i]++}
END { for (c in ht) print ht[c],c }

# 3文字版
BEGIN { FS="" }
{ for (i=1; i<=NF-2; i++) { ht[$i$(i+1)$(i+2)]++}}
END { for (c in ht) print ht[c],c }

Posted feedbacks - Flatten

Nested Hidden

Squeak Smalltalk で。

とりあえず、自身のソースコード文字列を得て解析結果を返すもの。

1
2
3
4
| sourceString |
sourceString := thisContext method decompileString.

^sourceString asBag sortedCounts

Squeak Smalltalk は、組み込みクラスのみならず処理系自体も Smalltalk 自身で記述されていて、その全ソースコードを処理系内からアクセスできるようになっています。そこで、これを解析してみました。

使用したコード(全クラスの全メソッドのソースを得てその文字列中の文字を bag に蓄積し、最後に頻度順にソート)と、全 13638278 文字中、1000 文字以上使われている文字の頻度を示します。

原則、Smalltalk は読み下したときに英文っぽくなるような命名をするので、結果、英単語における使用頻度(E、T、A、O、I、N…)に準ずる結果になりますが、記号については、キーワードメッセージで引数の前に使われるコロン、式の区切りのピリオドのほかにリテラルに使用する記号が比較的多くなるはずで、じっさいそのようになっています。

余談ですが、改行文字の数から、Squeak Smalltalk システムは約 30 万行の Smalltalk コードで記述されていることも分かります。これと同じことを一桁少ない行数で記述できる言語処理系を作るチャレンジが、今、アラン・ケイたちが取り組んでいるプロジェクトです。

http://vpri.org/html/work/ifnct.htm

1444427->Character space 1184081->$e 783875->$t 692975->$r 657882->$a 622461->$s 597060->$o 575035->$i 559122->$n 513017->Character tab 509706->$l 340916->Character cr 308729->$c 304427->$d 299400->$: 250870->$f 233671->$m 228063->$u 223102->$h 210051->$p 167306->$. 152494->$g 127057->$1 113908->$y 108249->$b 101596->$w 101168->$6 98043->$S 88101->$0 80005->$C 79576->$v 75559->$" 74041->$2 69815->$T 66799->$F 63860->$' 63131->$x 61565->$[ 61564->$] 59878->$A 57766->$3 57378->$) 57345->$( 55143->$4 52495->$7 51905->$k 49030->$5 48466->$P 48187->$N 46052->$B 45787->$= 44959->$M 40782->$8 40730->$I 38608->$^ 37685->$| 37462->$D 35494->$9 33225->$- 31526->$O 30804->$R 30157->$_ 29878->$E 25376->$z 24698->$L 23217->$# 20746->$, 19200->$W 18588->$V 14915->$j 13496->$U 12687->$; 11011->$H 10515->$/ 10070->$+ 9078->$q 7555->$> 7128->$G 6569->$K 6320->$@ 5752->$< 4510->$* 4043->$Y 3973->$X 3040->${ 3019->$} 2887->$$ 2624->Character lf 2586->$Q 2436->$J 2193->$Z 1361->$~ 1104->$ 1097->$? 1004->$!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
| bag |
bag := Bag new.
'Analyzing all source code...'
    displayProgressAt: Sensor cursorPoint
    from: 0 to: Smalltalk classNames size
    during: [:bar |
        | count |
        count := 0.
        SystemNavigation default allBehaviorsDo: [:class |
            bar value: (count := count + 1).
            class selectorsDo: [:sel | bag addAll: (class sourceCodeAt: sel)]]].
^bag sortedCounts

ファイル内のアスキー文字の出現回数をまとめて表示させるようにしました。 仕様上は文字の内部表現がアスキーコードとは限らないので手抜きといえば手抜きかもしれません。

これを使って手元にあった xyzzy のソース src/*.cc と lisp/*.l を比較してみました。

C++ ではループなどでよく使いそうな + と等号、不等号 が多いです。 ブロックに使う {} もかなりの差がありました。 またネーミングの慣習から _ でも大きな差が出ました。

Lisp では予想通り () が多く、割合が C++ の二倍を超えています。 またマクロ文字として使われる # やクオート、バッククオートがたくさん出てきます。 それから \ や ^ の数が多かったのですが、これは正規表現で多用されているようなのでサンプルの性質によるところが大きそうです。 それ以外ではシンボル名に多用される - で大きな差が付いています。 同じくシンボル名に使われる * や format 指定子に使われる ~ も多めでした。

その他の記号類と大文字は C++ の方が多めになるようです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
(defun frequency (files)
  (let ((v (make-array 127 :initial-element 0)))
    (dolist (file files v)
      (with-open-file (s file :direction :input)
        (loop
          as c = (read-char s nil)
          while (characterp c)
          if (< (char-code c) 127) do (incf (svref v (char-code c))))))))

(defun print-frequency (files)
  (let ((v (frequency files)))
    (format t "~{~4@{~10S ~6D~^ ~}~%~}"
            (loop for n across v and i from 0
              if (plusp n) nconc (list (code-char i) n)))
    (format t "~&Total: ~D characters~%" (reduce #'+ v))))

> Lisp では予想通り () が多く、割合が C++ の二倍を超えています。

へぇー。2倍程度なんだ。意外にC++ってかっこが多いんですね :)


表示が手抜きです

$ runhaskell 6382.hs < 6382.hs [('n',5),(' ',17),('&',3),('(',1),(')',1),('.',5),('=',3),('>',2),('A',1),('C',2),('D',1),('L',1),('a',5),('d',1),('e',6),('f',2),('g',3),('h',2),('i',5),('l',2),('m',4),('n',6),('o',8),('p',5),('q',2),('r',10),('s',3),('t',11),('u',1),('w',1)]

1
2
3
4
5
6
import Data.List
import Control.Arrow

main = getContents >>= print . freq

freq = map (head &&& length) . group . sort

httplib.pyを解析させました。

spaceが多いのはインデントのせいでしょう。

辞書いいです。

[nori@Asama]~/Desktop/study/python/code% python count.py /usr/lib64/python2.4/httplib.py

{'\n': 1370, ' ': 14394, '"': 262, '(': 385, '*': 15, ',': 317, '.': 707, '0': 131, '2': 46, '4': 36, '6': 16, '8': 6, ':': 337, '<': 12, '>': 23, 'B': 22, 'D': 55, 'F': 52, 'H': 93, 'L': 78, 'N': 210, 'P': 112, 'R': 148, 'T': 313, 'V': 13, 'X': 25, 'Z': 4, '\\': 22, '`': 5, 'b': 215, 'd': 859, 'f': 1013, 'h': 761, 'j': 24, 'l': 1444, 'n': 1682, 'p': 606, 'r': 1429, 't': 1968, 'v': 182, 'x': 68, 'z': 26, '|': 19, '~': 1, '!': 12, '#': 224, '%': 18, "'": 220, ')': 390, '+': 21, '-': 127, '/': 39, '1': 110, '3': 18, '5': 20, '7': 6, '9': 19, ';': 8, '=': 440, '?': 8, 'A': 84, 'C': 109, 'E': 195, 'G': 16, 'I': 106, 'K': 17, 'M': 31, 'O': 104, 'Q': 22, 'S': 163, 'U': 64, 'W': 23, 'Y': 12, '[': 53, ']': 54, '_': 615, 'a': 1247, 'c': 809, 'e': 3748, 'g': 253, 'i': 1345, 'k': 254, 'm': 324, 'o': 1443, 'q': 60, 's': 2200, 'u': 579, 'w': 206, 'y': 169, '{': 5, '}': 5}

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import sys

fn = sys.argv[1]

if not fn:
  sys.exit(1)

hist = dict()
f = file(fn)
buf = f.read(1000)
while buf:
  for ch in buf:
    count = hist.get(ch, 0)
    count +=1
    hist.update({ch:count})
  buf = f.read(1000)
print hist

PostScript で... 
自分自身を食わせると 
32 ( ) 293
10 [*] 61
101 (e) 51
114 (r) 48
116 (t) 47
111 (o) 42
112 (p) 34
くらいで、roll, copy, exch あたりのスタック操作命令が結構稼いでいるかと思います。
(t はコメントと変数名が...)
一般的な PostScript File の傾向は.... あまりに傾向が散らばりすぎてよくわかりません。
例えば PhotoShop で作成した巨大bitmap の eps file などでは、
コードよりも圧倒的に多量の embed されたデータが傾向を
決めることになってしまいます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
%!PS

/CompareVal { % [I1 V1] [I2 V2] CompareXY [I1 V1] [I2 V2] V2-V1
    2 copy 1 get exch 1 get sub 
} bind def

/Sort { % [[x y] [x1 y1] Array Data ] {CompareFunction}  Sort [ArrayData]
    cvx [ 3 -1 roll
    aload length
    % func -mark[- [] [] [] [] [] len
    -1 2 { % func -mark[- [] [] [] [] [] len2
        -1 2 {
            3 1 roll
            counttomark 1 add index exec  %% Compare
            0 lt { exch } if
            3 -1 roll
            1 roll
        } for
        counttomark 1 roll
    } for
    counttomark 1 roll
    ] exch pop
} bind def


/CountLetters {
    [ 0 1 255 { [ exch 0 ] } for ]
    {
        dup 2 index read
        {
            % array code
            get dup 1 get 1 add 1 exch put
        } {
            exit
        } ifelse
    } loop
    /CompareVal Sort
    exch pop exch pop
} bind def


/PrintResult {
    0 1 255 {
        2 copy get
        % [Array] i [I V]
        dup 0 get dup dup dup 3 string cvs print ( ) print
        32 ge exch 127 lt and {
            % [Array] i [I V] I 
            (( ) ) dup 1 4 -1 roll put print
        } {
            ([*] ) print
            pop
        } ifelse
        1 get =
        pop 
    } for
    pop
} bind def

%(countletter2.ps) (r) file CountLetters
(%stdin) (r) file CountLetters
PrintResult

JavaScriptの代表例ということで、prototype.jsを対象としました。
ついでにファイルの読み込みをAjaxで。

試すときは、以下をfreq.htmlなどの名前で保存し、prototype.jsを同じところに置いて、htmlを開くだけです。
IEで見る場合は、11行目をコメントにし、代わりに16行目をコメント解除してください。

<結果>
文字 Code 数 頻度
' ' 20 24616 0.19183441267466236
e 65 10917 0.08507703457788791
t 74 8301 0.06469034203820168
n 6e 6215 0.048433980938130755
r 72 4873 0.03797567000989721
o 6f 4204 0.03276210070215634
(省略)

結果は、・・・普通です。
プログラム言語は全般的に「 t 」をよく使うのでしょうか。
記号で多いのは、丸括弧が2616個、ドットが2534個、セミコロン1779個で、
以下、「=」1498、「,」1396、「-」1067、「’」1058、波括弧937と続きます。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<html>
<head>
<script type="text/javascript" src="prototype.js"></script>
<script type="text/javascript">
function log(s) {
    $('resultArea').innerHTML += (new Date().toString() + " " + s + '<br>\n');
}
/* 対象ファイルを要求する */
function init() {
    var ajax = new Ajax.Request(
       'prototype.js',   // 頻度分析対象ファイル
       /* IEの場合、XMLHttpRequestでローカルファイルにアクセスできないため
        * 自前のサーバに設置するか、
        * 以下のコメントを外して本家からお借りして下さい。
        *  (こっちはFirefoxでは動きません。。。) */
       // 'http://www.prototypejs.org/assets/2007/11/6/prototype.js',
       {
           method: 'get',
           onComplete: function(req) {
               log("Request completed");
               freq(req.responseText);
           }
       });
    log("Request sent");
}
/* 文字列中の文字頻度を測定し、結果を表示する */
function freq(s) {
    // 頻度の集計
    var hist = {};
    for (var i = 0; i < s.length; i++) {
        var ch = s.charCodeAt(i);
        if (hist[ch] == undefined)
            hist[ch] = { moji: ch, count:1 };
        else
            hist[ch].count++;
    }
    
    var report = "<table border=\"1\">\n<tr>"
               + "<td>文字</td>"
               + "<td>Code</td>"
               + "<td>数</td>"
               + "<td>頻度</td>\n";
    // 頻度を出現回数の昇順でならびかえ、表で表示
    Object.values(hist)
        .sort(function(a, b) { return b.count - a.count; })
        .each(function(o) {
            report += "<tr><td>" + String.fromCharCode(o.moji) + "</td>";
            report += "<td>" + o.moji.toString(16) + "</td>";
            report += "<td style=\"text-align: right;\">" + o.count + "</td>";
            report += "<td>" + (o.count / s.length)  + "</td></tr>\n";
        });
    report += "</table>";
    log(report);
}
</script></head>
<body onload="init();">
<div id="resultArea"></div>
</body>

アプリケーションのソースじゃなくて、
cygwin g++ の C++ ヘッダファイル群(全218個)を分析してみました。

  1:0x20  548301 (0.20355)
  2:   e  189982 (0.07053)
  3:   t  166220 (0.06171)
  4:   _  144935 (0.05380)
  5:   r  117461 (0.04361)
  6:   a  113395 (0.04210)
  7:   i  111175 (0.04127)
  8:   s  103785 (0.03853)
  9:   o   99888 (0.03708)
 10:   n   98172 (0.03644)
 11: 0xa   81784 (0.03036)
 12:   l   62672 (0.02327)
 13:   c   61995 (0.02301)
 14:   p   55391 (0.02056)
 15:   u   44881 (0.01666)
<以下省略>

スペースや改行は置いておくとして。
上位10位以内で、int や iterator が
(もうちょっと広げるとconst_iteratorも)
作れるのが興味深いところかと。

"_" が多いのはヘビ記法とローカル用識別子の影響かな。

括弧の類を見てみると、
"(", ")" はそれぞれ24,25位(0.9%),
templateにも使用する"<", ">" は それぞれ 38,33位 (0.4~0.5%),
"{", "}" に至っては 44,45位(0.26%) となっていました。

意外に括弧使ってないのね。。

ちなみに最下位は "$" (8件) でしたが、コメントの中かな...
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <map>
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <vector>
#include <functional>
#include <fstream>
#include <stdexcept>
#include <numeric>
#include <cctype>
#include <iterator>
#include <utility>

typedef std::map<char, int> freq_t;
typedef std::vector< std::pair<char, int> > freqv_t;

struct calc_freq
: std::unary_function< freq_t, char* >
{
  freq_t operator()(char* filename) const
  {
    std::ifstream ifs(filename);
    if ( !ifs )
      throw std::runtime_error(std::string("failed to open : ")+filename);

    freq_t f;
    while ( ifs )
    {
      int c = ifs.get();
      if (c >= 0) ++f[c];
    }

    return f;
  }
};

struct merge_freq
: std::binary_function<freq_t, freq_t, freq_t>
{
  freq_t& operator()(freq_t& lhs, const freq_t& rhs) const
  {
    for ( freq_t::const_iterator it = rhs.begin();
        it != rhs.end(); ++it )
      lhs[it->first] += it->second;

    return lhs;
  }
};

struct count_total
: public std::binary_function< int, int, freq_t::value_type >
{
  int& operator()(int& total, const freq_t::value_type& v) const
  {
    return total += v.second;
  }
};

template<class C>
struct freq_print_each
: std::unary_function< void, typename C::value_type >
{
  std::size_t total_;
  mutable std::size_t i_;
  freq_print_each<C>(std::size_t total) : total_(total), i_(0) {}

  void operator()(const typename C::value_type& v) const
  {
    std::cout << std::setw(3) << ++i_ << ":" << std::setw(4);
    if ( !isprint(v.first) || isspace(v.first) )
      std::cout << std::showbase << std::hex
                 << static_cast<int>(v.first) << std::dec;
    else
      std::cout << v.first;

    std::cout << std::setw(8)
      << v.second << " ("
      << std::setprecision(5) << std::fixed
      << static_cast<double>(v.second)/total_
      << ")\n";
  }
};

template <class C>
struct sorter
: std::binary_function<bool, typename C::value_type, typename C::value_type>
{
  typedef typename C::value_type value_type;
  bool operator()(const value_type& lhs, const value_type& rhs) const
  {
    return lhs.second > rhs.second;
  }
};

int main(int c, char** v)
{
  try {
    std::vector< freq_t > freqs;
    std::transform(v+1, v+c, std::back_inserter(freqs), calc_freq());

    const freq_t all_freq = std::accumulate(freqs.begin(), freqs.end(), freq_t(), merge_freq());
    const int total = std::accumulate(all_freq.begin(), all_freq.end(), 0, count_total());

    freqv_t all_freq2;
    all_freq2.reserve(all_freq.size());

    std::copy(all_freq.begin(), all_freq.end(), std::back_inserter(all_freq2));
    std::sort(all_freq2.begin(), all_freq2.end(), sorter<freqv_t>());

    std::for_each(all_freq2.begin(), all_freq2.end(), freq_print_each<freqv_t>(total));
  }
  catch (const std::exception& e) {
    std::cerr << e.what() << std::endl;
  }

  return 0;
}

自分自身を解析した結果(上位10件)。 ' ':305 /'e':104 /'t':93 /0ah:76 /'r':72 /'s':61 /'i':54 /':':52 /'a':51 /'o':50

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include <iostream>
#include <iomanip>
#include <fstream>
#include <map>
#include <list>
#include <algorithm>
#include <iterator>
#include <cctype>

typedef std::map<char, int> freq_type;

class Loader
{
public:
    explicit Loader(freq_type& freq) : freq_(freq) {}

    void operator () (const char* filename)
    {
        std::ifstream src(filename);
        char c;
        while(src.get(c))
        {
            ++freq_[c];
        }
    }

private:
    freq_type& freq_;
};

bool comp(const freq_type::value_type& lhs, const freq_type::value_type& rhs)
{
    return (lhs.second != rhs.second) ? (lhs.second > rhs.second) : (lhs.first < rhs.first);
}

std::ostream& operator << (std::ostream& out, const freq_type::value_type& value)
{
    if(std::isprint(value.first))
    {
        out << "'" << value.first << "'";
    }
    else
    {
        int fill = out.fill('0');
        out << std::hex << std::setw(2) << (static_cast<unsigned int>(value.first) & 0xffu) << 'h' << std::dec;
        out.fill(fill);
    }
    return out << ":" << value.second;
}

int main(int argc, char* argv[])
{
    // 読み込み
    freq_type freq;
    std::for_each(argv + 1, argv + argc, Loader(freq));

    // 出現頻度で並べ替え
    std::list<freq_type::value_type> f(freq.begin(), freq.end());
    f.sort(comp);

    // 出力

//  エラーになる理由がわからず…(;_;)マダマダ、ミジュクモノ…
//  std::copy(f.begin(), f.end(), std::ostream_iterator<freq_type::value_type>(std::cout, "\n"));

    for(std::list<freq_type::value_type>::const_iterator i = f.begin(); i != f.end(); ++i)
    {
        std::cout << *i << std::endl;
    }

    return 0;
}

自身の文字頻度の上位10個は以下のようになりました。
ソース中に英単語が多く現れているため、英文の文字頻度と近い気がします。

e	75
t	59
SPACE	54
r	54
TAB	52
a	48
n	34
i	32
u	28
CR	27
p	24
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.TreeMap;

public class Frequency {
    public static void main(String[] aArguments) throws IOException {
        Map<Character, Integer> tFrequencies = new TreeMap<Character, Integer>();
        InputStreamReader tReader = new InputStreamReader(new BufferedInputStream(new FileInputStream(aArguments[0])));
        try {
            int tChar;
            while ((tChar = tReader.read()) >= 0) {
                Integer tFrequency = tFrequencies.get((char) tChar);
                if (tFrequency == null) {
                    tFrequencies.put((char) tChar, 1);
                } else {
                    tFrequencies.put((char) tChar, tFrequency + 1);
                }
            }
        } finally {
            tReader.close();
        }
        System.out.println(tFrequencies);
    }
}

どういう訳かまだRubyがないので。

net/http.rbを対象にしてみました。
空白を別にすれば,e が飛び抜けて多いのは end が多いのも影響
してるんでしょうか。


^o^ >ruby hist_char.rb c:/usr/ruby/lib/ruby/1.8/net/http.rb
" ": 18072
"e": 4839
"t": 2982
"s": 2574
"r": 2525
"n": 2290
"\n": 2274
"o": 2245
"a": 1945
"d": 1757
"i": 1700
"T": 1216
"l": 1183
"p": 1125
"#": 1106
(以下略)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
src = File.read(ARGV.shift)

hist = {}

src.each_byte do |c|
  c = c.chr
  if hist.member?(c)
    hist[c] += 1
  else
    hist[c] = 1
  end
end

hist.to_a.sort{|a,b| b[1] <=> a[1]}.each do |pair|
  puts "#{pair[0].inspect}: #{pair[1]}"
end

Javaはすでに出ていますが,以下のように改良してあります。
・コメント内の文字は集計に含めるとおかしくなりそうなので含めない
・JavaのソースコードはZIPファイルで提供されることが多いので,指定されたZIPファイル内のすべてのJavaソースファイルに対して集計を行う

JDK 1.6に付属のsrc.zipを集計してみました。
' ' 9328666
'e' 2074804
't' 1744299
'n' 1284657
'i' 1246055
'r' 1217281
'a' 1167409
'o' 1041531
'l' 873609
's' 851094
'c' 728897
'u' 631196
'p' 571200
'(' 514249
')' 514079
'.' 493002
'd' 466367
'm' 422504
';' 418324
'g' 417286
\u0009 403663
'f' 339805
'b' 311888
'h' 272053
'S' 271704
'=' 271260
'E' 267436
',' 224844
'C' 214651
'I' 210502
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class CharCounter {
    public static void main(String[] args) throws Exception {
        Hashtable<Character, Count> counts = new Hashtable<Character, Count>();
        ZipFile file = new ZipFile(args[0]);
        Enumeration<? extends ZipEntry> entries = file.entries();
        while (entries.hasMoreElements()) {
            ZipEntry entry = entries.nextElement();
            if (!entry.getName().endsWith(".java")) continue;
            Reader src = new InputStreamReader(file.getInputStream(entry));
            StreamTokenizer tokenizer = new StreamTokenizer(src);
            tokenizer.resetSyntax();
            tokenizer.slashSlashComments(true);
            tokenizer.slashStarComments(true);
            while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
                if (tokenizer.ttype == StreamTokenizer.TT_EOL) continue;
                char c = (char)tokenizer.ttype;
                Count count = counts.get(c);
                if (count == null) {
                    count = new Count(c);
                    counts.put(c, count);
                }
                count.n++;
            }
        }
        Vector<Count> ranking = new Vector<Count>();
        ranking.addAll(counts.values());
        Collections.sort(ranking);
        for (int i = 0; i < ranking.size(); i++) {
            System.out.println(ranking.get(i));
        }
    }
    static class Count implements Comparable<Count> {
        char c;
        int n = 0;
        Count(char c) {
            this.c = c;
        }
        public int compareTo(Count o) {
            return (o.n == this.n)? 0 : (o.n < this.n)? -1 : 1;
        }
        public String toString() {
            if (c < ' ') {
                return String.format("\\u%04x", (int)c) + "\t" + n;
            }
            return "'" + c + "'\t" + n;
        }
    }
}

jdk1.6に付属されていた サンプルソース「Intro.java」を解析しました。

[a] = 1557 [e] = 2746 [i] = 2116 [l] = 1008 [n] = 1981 [r] = 1314 [s] = 1209 [t] = 2340

e,s,tが多いのは、 setter、getterメソッドのためでしょうか。 int,String などもキーワードからも、 i,r,tは多いのでしょうか。

1
2
3
4
5
6
7
8
9
def text = new File("c:/Intro.java").text

(0..(text.size()-1)).collect{
    text[it]
}.unique().sort().each{
    def key = "[${it}]".padLeft(5)
    def value = text.count(it).toString().padLeft(7)
    println "${key} = ${value}"
}

出題者です。 こちらで用意していた回答は awk を使ったものでした。一応解説すると、組み込み変数FSを空にし、1行単位の文字毎に連想配列に格納しています。

1
2
3
4
5
6
7
8
9
# 1文字版
BEGIN { FS="" }
{ for (i=1; i<=NF; i++) ht[$i]++}
END { for (c in ht) print ht[c],c }

# 3文字版
BEGIN { FS="" }
{ for (i=1; i<=NF-2; i++) { ht[$i$(i+1)$(i+2)]++}}
END { for (c in ht) print ht[c],c }

以下でもOKですね。

hist = Hash.new(0)
src.each_byte do |c|
  hist[c.chr] += 1
end


文字数カウントは既に投稿されていたので、半角スペース・タブ・改行で区切って似非単語分割してみました。
typedefが少し汚いです・・・。
分割はboost::tokenizerを使ってますが、ここをmecab等に置き換えればもう少し遊べるかもしれません。

自分自身を分析:
} :: 8
#include :: 7
typedef :: 7
i :: 6
// :: 5
= :: 4
<< :: 4
!= :: 4
i++){ :: 3
> :: 3
return :: 2
FreqmapCIter& :: 2
count(std::string :: 2
Freqmap* :: 2
str, :: 2
{ :: 2
" :: 2
int :: 2
void :: 2
operator()(const :: 1
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// frequency analysis

#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <boost/tokenizer.hpp>


typedef std::map< std::string, int > Freqmap;
typedef Freqmap::value_type FreqmapType; 
typedef Freqmap::const_iterator FreqmapCIter;
typedef std::vector< FreqmapCIter > FreqmapCIterVec;
typedef FreqmapCIterVec::const_iterator FreqmapCIterVecCIter;


struct FreqmapCIterVecSort{
    bool operator()(const FreqmapCIter& lhs, const FreqmapCIter& rhs){
    return (lhs->second > rhs->second);// ordered by desc
    }
};


void count(std::string str, Freqmap* vec);


int main(int argc, char* argv[])
{
    // open
    std::ifstream ifs(argv[1]);

    // count
    Freqmap map;
    std::string strbuf;
    while(ifs && getline(ifs, strbuf)){
    count(strbuf, &map);
    }
    ifs.close();


    // sort
    FreqmapCIterVec mapvec;
    for(FreqmapCIter i = map.begin(); i != map.end(); i++){
    mapvec.push_back(i);
    }
    std::sort(mapvec.begin(), mapvec.end(), FreqmapCIterVecSort());

    // output
    for(FreqmapCIterVecCIter i = mapvec.begin(); i != mapvec.end(); i++){
    std::cout << (*i)->first << " :: " << (*i)->second << std::endl;
    }

    return 0;
}



typedef boost::char_separator<char> Sep;
typedef boost::tokenizer<Sep> Tok;


void count(std::string str, Freqmap* map)
{
    Sep sep(" \t\n");
    Tok tok(str, sep);
    Freqmap::iterator mapiter;

    for(Tok::iterator i = tok.begin(); i != tok.end(); i++){
    mapiter = map->find(*i);
    if(mapiter != map->end()){
        mapiter->second++;
    }else{
        map->insert(std::pair<std::string, int>(*i, 1));
    }
    }
}

Brainf*ckで。文字列を入力し、最後に「$」を入力するとヒストグラムを表示します。

コメントなし版を入力した結果
++++++++++++++++++++++++++++++++++++++
,
-------------------------
..
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[
]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]

記号が多いですね(笑)
「>」69、「<」57、「+」38、角括弧30、「-」25。
入出力(.,)はごく少数。

ほとんどチューリングマシンなのでポインタの移動が多くなります。+はデータの作成に多用します。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
●コメント版
+[>                        # while ({0})  do
  +>,<                     #   {1} = 1 ; {2} = getchar()
  >                        # ** dollar check **
   >>++++++[-<++++++>]<<   #   {3} = 36 dollar
                           # ** compare {3} & {4} **
   [->>+>+<<<]             #   {@2:@3} = !{@0}
   >>>[-<<<+>>>]<<<        #   {@0} = !{@3}
   >[->->+<<]<             #   {@2} = {@2} minus {@1} ; {@3} = !{@1}
   >>>[-<<+>>]<<<          #   {@1} = !{@3}
   >>
   >+<[[-]>-<]>[-<+>]<     # ** invert result **
   [[-]<<[-]<[-]<[-]>>>>]  #   {2:1:0} = 0
   <[-]>                   #   {3} = 0
   <<
  <
  [>                       # ** count up if input char is not dollar **
   [->>>>>>+<<<<<<]        #   {8} = !{2}
   >>>>[-]>[-]>            #   {6:7} = 0 ; move to {8}
   [- [->>+<<]+>> ]        # ** extend arm to {8 plus (charcode times 2)} **
   >+<                     # ** count up **
   <<[[-]<<]               # ** reduce arm **
   <<<<
   [-]<[-]>                #   {2:1} = 0
  <]
<]                         # end   ** next char **
# show result
>>>>>>>> >>+
[
  >[[-<.>]++++++++++.[-]]<
  [->>+<<]>>+
]

●コメントなし版
+[>+>,<>>>++++++[-<++++++>]<<[->>+>+<<<]>>>[-<<<+>>>]<<<>[->->+<<]<>>>[-<<+>>]<<<>>>+<[[-]>-<]>[-<+>]<[[-]<<[-]<[-]<[-]>>>>]<[-]><<<[>[->>>>>>+<<<<<<]>>>>[-]>[-]>[-[->>+<<]+>>]>+<<<[[-]<<]<<<<[-]<[-]><]<]>>>>>>>>>>+[>[[-<.>]++++++++++.[-]]<[->>+<<]>>+]

Perlで。思っていたよりは記号が少ないです。

自身を入力にした結果:
Total: 681 character(s)
Character Count Frequency[%]
' '       111    16.30      
'!'         2     0.29      
'"'         8     1.17      
'#'         1     0.15      
'$'        29     4.26      
'%'         4     0.59      
'''         2     0.29      
'('         6     0.88      
')'         6     0.88      
'*'         2     0.29      
...
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/perl

use strict;
use warnings;
use utf8;

use Text::Table;

my $src_file;
if (@ARGV) { open $src_file, "<:utf8", shift or die $!; }
else { $src_file = \*STDIN; }

my $src = do { local $/ = undef; <$src_file> };
my $total_chars = 0;
my %chars;

for my $char ( split //, $src ) {
  $char =~ s/\n/\\n/;
  $char =~ s/\t/\\t/;
  if ( exists $chars{$char} ) { $chars{$char