日曜日, 6月 23, 2013

Algorithm 問題を解く 6 - ハッシュ

ハッシュ

ハッシュはバイナリサーチよりも効率的に検索をしたい場合に必須になるアルゴリズムである.
手短にハッシュを説明すれば,

  1. 一程度のメモリ領域をハッシュバケットとして確保
  2. ハッシュコードを要素から算出し,そのハッシュコードを検索キーとしてハッシュバケット中の1スロットに挿入

となる.例えば以下のハッシュバケット領域があったとして
0x0000 .. NO DATA
0x0001 .. NO DATA
0x0002 .. NO DATA
あるデータ X を保存したいとする.Xのハッシュキーが2だったらば,上記のハッシュバケットは
0x0000 .. NO DATA
0x0001 .. NO DATA
0x0002 .. { X }
となる.
検索はハッシュキーを元にデータを検索し,計算量はO(1)であり,2分検索の平均O(logN)より効率的である.

ハッシュコリジョン

当然,別のデータが同じハッシュキーを持つことがある.そういった場合はそのハッシュキーのスロットは大抵は循環リストのデータ構造を持つか,または別の空きスロットを見つける(オープンアドレッシング)する.最悪の場合の計算量はO(n)となる.


問題

Given a dictionary of English words, return the set of all words grouped into subsets of words that are all anagrams of each other.

Algorithms for Interviews より引用.


解答

#include <iostream>
#include <map>
#include <list>
using namespace std;

list<string> dict;

void init_dict() {

 dict.push_back( string("dog") );
 dict.push_back( string("god") );
 dict.push_back( string("redrum") );
 dict.push_back( string("murder") );
 
}

void analyze( list<list<string>> ls ) {

 list<string> l;
 string s;
 map<int,list<string>> m;
 list<string>::iterator iter;

 for( iter = dict.begin();iter != dict.end();iter++ ) {

  int sum = 0;
  s = *iter;
  for( int i = 0;i < s.length();i++ ) {
   sum += s[i];
  }
  if( m.find(sum) == m.end() ) {
  
   l.clear();
   l.push_back(*iter);
   m[sum] = l;

  }
  else {

   l = m[sum];
   l.push_back(*iter);
   m[sum] = l;

  }

 }

 map<int,list<string>>::iterator mapIter;
 pair<int,list<string>> t;
 for( mapIter = m.begin();mapIter != m.end();mapIter++ ) {
  t = *mapIter;
  cout << t.first << ":{";
  list<string>::iterator li = t.second.begin();
  for(;li != t.second.end();li++)
   cout << li->data() << ",";
  cout << "}" << endl;

 }



}


int main() {

 init_dict();
 list<list<string>> ls;
 analyze(ls);
 list<string>::iterator iter;
 for( iter = dict.begin();iter != dict.end();iter++) {
  cout << (*iter).c_str() << endl;
 }

 return 0;
}


木曜日, 6月 20, 2013

C++ 標準関数 おさらい 1 .. strtol, strtod, strtof

充実のC++標準ライブラリ

C++標準ライブラリは大変優れています,というのはいうまでもない....勿論boostを使わないとできないことも多々あるが,それでもまずC由来のライブラリと合わせてこの標準ライブラリを使いこなせるようになれば大抵のことは出来るはず.

strtol, strtod, strtof - 文字列から数値型に変換

strtol, strtod, strtof -これらの関数はそれぞれconst char*をそれぞれlong型,double型,float型に変換する.strtolは指定した底から10進数に変換することもできる.似たような関数にatoi,atof,atolがある(atodはない,atofはdoubleを返す)が,N進数パース等といった機能も含めるとstrto系のほうがお勧めだ.ちなみにヘッダはcstdlid. long longに変換するstrtoll,long doubleへのstrtold, unsigned long longへのstrtoullもちゃんとある.

Javaでは

それぞれLong.parseLong(), Double.parseDouble(), Float.parseFloat().


サンプル

O'reillyのC++ Cookbookのサンプルコードをほぼ引用したうえで,自作のmyhex2int()で16進数->10進数変換するコードを足してみた.底変換のコードはもっときれいにかけるはずだがご愛嬌.

#include <iostream>
#include <string>
#include <cstdlib>
#include <cmath>
#include <map>

using namespace std;

static map<char,unsigned int> nummap;

long hex2int(const string& hexStr){

 char *offset;
 if(hexStr.length() > 2) {
  if(hexStr[0] == '0' && hexStr[1] == 'x')
   return strtol(hexStr.c_str(), &offset,0);

 }
 return strtol(hexStr.c_str(),&offset,16);
}

static void init_map() {

 for( char ch = 0x30;ch < 0x40;ch++ )
  nummap[ch] = ch-0x30;
 for( char ch = 0x61;ch < 0x67;ch++ )
  nummap[ch] = ch-0x31;

}

long myhex2int(const string& hexStr){

 double repeat = 0;
 int t = 0;

 init_map();
 int sum = 0;

 if(hexStr.length() > 2 && hexStr[0] == '0' && hexStr[1] == 'x') {
  for(int i = hexStr.length() - 1,repeat = 0;i > 1;i--,repeat++){
   sum += ( nummap[hexStr[i]] * pow(16.0,repeat) );
  }
 }
 else {
  for( int i = hexStr.length() - 1,repeat = 0;i >= 0;i--,repeat++){
   sum += ( nummap[hexStr[i]] * pow(16.0,repeat) );
  }
 }
 return sum;

}

int main() {

 string str1 = "0x1234";
 cout << hex2int(str1) << endl;
 string str2 = "1234";
 cout << hex2int(str2) << endl;
 string str3 = "QAFG";
 cout << hex2int(str3) << endl;
 cout << "--- my(hex) ---" << endl;
 cout << myhex2int(str1) << endl;
 cout << myhex2int(str2) << endl;
 
        return EXIT_SUCCESS;
}

土曜日, 6月 15, 2013

UNIXコマンド One liner 4

あるディレクトリ以下の,rootユーザ又はルートグループが所有しているファイル又はディレクトリを再帰的に探し表示
この結果はノイズを含む可能性がある.

find *|xargs ls -al|egrep root

月曜日, 6月 10, 2013

TopコマンドのRES,VIRT,SHR

Topコマンド

管理者のみならず開発者,そして私のようなアーキテクトにとっても良い友人のTopコマンド.その友人がもたらしてくれる情報の内,VIRT,RESそしてSHRの意味合いをもう一度おさらいしておく.

VIRT (Virtual Image )

VIRTはプロセスの
  • コード
  • データ
  • ライブラリ
  • スワップアウトしたページ
  • メモリマップドファイル
の内,まだ使用されていないものの合計であるわけで,全てがメモリを指しているわけではないので注意.C/C++コードでただnew或いはmallocしただけだと,確保した分がVIRTのサイズに上乗せされる.Windowsでいうところのコミットチャージか.

RES (Resident Memory)

RESは実際にプロセスに利用されているデータの総計を示している.Javaで大きなヒープを確保していたり,C/C++でたくさんnewあるいはmallocしているとRESとVIRTの差が大きくなる.Windowsでいうところのワーキングセットである.

SHR (Shared Memory)

ライブラリなど,他のアプリケーションからも利用される可能性のあるメモリの総計である.

蛇足 - PSで指定したプロセスの情報だけ見る

ps -pオプションで指定したプロセスの情報を見ることが出来る.

UNIXコマンド One liner 3

特定のプロセス(ここではbigというプログラム)のPIDとパスをreport.txtにダンプし表示

egrep -vでps -ef中のegrepのPIDを除外.


ps -ef|egrep ./big|egrep -v egrep|awk 'print{$2,$8}' > report.txt;cat report.txt

UNIXコマンド One liner 2

特定のワード(ここではGETをPOSTに)を置換して別名ファイルとして保存

rm -fr conv;mkdir conv;for f in `find * -prune -type f`;do sed -e 's/GET/POST' $f > conv/%$f.conv;done

火曜日, 6月 04, 2013

UNIXコマンド One liner 1

UNIXシェルは奥深い

UNIXシェルの奥の深さは底知れない.あなたがもし何かの処理の為のスモールプログラムを書くつもりならば,UNIXシェルだけで95%のことは出来る,ということをまず考えたほうが良い.何十行のプログラムやスクリプトでできることはシェルの一行でできるはずだ.

一行で色々やってみる

以下は一行で色々やってみたという参考例.実際に私の仕事のうち,データ解析,大量のファイルデータ処理等はR/SQL/スクリプトを書く以前に,大概ワンライナーシェルスクリプトの組み合わせである程度まで出来る.

特定のファイルのサイズ総計を表示

find *Neko*|xargs ls -al|gawk '{s+=$5} END {print s}'

全ての.txtファイルのサイズが大きい順トップ10のファイル名とサイズを表示

find **/*.txt|xargs ls -alSr|awk '{print $9,$5}'|tail -10

全ての.txtファイルのサイズが小さい順トップ10のファイル名とサイズを表示

find **/*.txt|xargs ls -alSr|awk '{print $9,$5}'|head -10

又は

find **/*.txt|xargs ls -alS|awk '{print $9,$5}'|tail -10

あるgzipされたファイルの,パイプで区切られたデータの4行目の内容を使ってソート,多かったデータの上位5件を表示

zcat data.txt.tar.gz|cut -f4 -d"|"|uniq -c|sort -nr|head -5


月曜日, 6月 03, 2013

sudo設定1 .. 権限の委譲とパスワードプロンプトの抑制

権限移譲は慎重に

sudoを効果的に使えることは,ある程度以上の規模のサーバ管理を複数人で行うとすれば必要になってくるのだろう.今回は権限の委譲とパスワードプロンプト抑制というテーマである.
ターゲットは極私的な,しかも完全に安全なところに配置されているラボラトリサーバである.会社や大学の一般のサーバ上では権限の委譲はある程度良いにしてもパスワードプロンプト抑制はおこなわないように.

グループ作成

ここではADM_LIMITEDというグループを作成し,特定のパワーユーザをこのグループに属させ,そのグループを経由してroot権限を委譲する.

まずは以下のコマンドでグループ ADM_LIMITEDを作成,そしてユーザをそのグループに属させる.

groupadd ADM_LIMITED
usermod -G ADM_LIMITED tanuchan


新規ユーザをそのままグループに属させたいならばuseraddの-gオプションを使う.
とりあえずidコマンドでユーザのグループ情報を確認しておこう.グループは基本は追加すること.リプレースは危険だ.

sudoviで編集

sudoの設定ファイルは/etc/sudoersであるが,直接ファイルを編集すべきでない.代わりにvisudoコマンドを使うべきである.visudoコマンドは編集後自動的にバリデーションを行ってくれて,バリデーションに失敗した時は what now? と編集に戻るかどうか聞いてくれるので安全だ.ちなみにrootまたはそれに準ずる権限でvisudoコマンドを実行すること.


権限移譲とパスワードプロンプト抑制

visudoコマンド実行後,sudoersファイルが開かれる.ポインタをずっと下げていくと Allows people in group wheel to run .. というセクションが見つかる. そこらへんに以下のような行を追加.
%ADM_LIMITED ALL=(ALL) ALL
%ADM_LIMITED ALL=(ALL) NOPASSWD: ALL

これを保存すれば,全てのADM_LIMITEDユーザがパスワード入力なしで任意のコマンドをsudo経由で実行できる.sudo (ver 1.8.6p7)は毎回/etc/sudoersを読みにいくので何かのプロセスを再起動等は必要ない.ただし,新しいグループに対する変更はどうやらサーバのリブートが必要かもしれない.

個人にroot権限を付与できるが

これは出来るだけやめておこう.極私的なサーバで,一切公開の予定無しという環境であれば問題ないが,拡張性等の点からいってもベストプラクティスではないのでできるならグループ経由で権限移譲をしよう.

日曜日, 6月 02, 2013

Sambaサーバのセットアップメモ

Sambaサーバ

最近sambaサーバを再インストールするはめになったので,ついでにメモを当ブログにアップロードしておく.
ちなみにこのメモはFedora18を対象としている.他のディストリビューションとは手順4のステップが恐らくことなるだろう.

手順1. インストール

sudo yum install samba

手順2. smb.conf編集

cd /etc/samba,そしてsmb.confを編集.

workgroup = [任意のワークグループ]
netbios name = [任意の名前]
hosts allow = [アクセスを許可するホストのIPaddress, 192.168.1. で192.168.1/24ネット全体を許可 )


手順3. share directoryを作成

シェアしたいディレクトリを作成し,そのエントリをsmb.confに追加

[shareddir]
comment = shared dir
path = /home/[userdir]/shared
public = yes
writable = yes
printable = no
browsable = yes

手順4. サービスの起動及び登録

Fedora18はinit.dではなくsystemdをデフォルトでつかっているようである.
なので以下のコマンドを発行.


systemctl start smb.service
systemctl start nmb.service
systemctl enable smb.service
systemctl enable nmb.service


土曜日, 6月 01, 2013

C++ - コンソールのプログレス表示

コンソールでプログレスを表示するには

rpmのvオプションをつけた時やyumの出力等で見かける,同じラインで進捗状況を出力する機能.コマンドラインユーティリティを作る際は遅かれ早かれ必要になることも多いはず.個人的には多いのでいちおうメモということで.

ちなみにC++と題してはいるがこの方法は他の言語でも使えるはず.

コード

肝心なポイントは \r(キャリッジリターン)だけを表示すること,そしてバッファをマニュアルでフラッシュすること.少なくともgnu g++のcoutは改行をしないとバッファを標準出力にフラッシュしない.
#include <iostream>
#include <unistd.h>
#include <cstdlib>
using namespace std;

int main() {

    string progress = "progress..";
    cout << "starting.." << endl; 
    for( int i = 0;i < 10;i++ ) {

        progress += "#"; 
        cout << progress << "\r" << flush;
        sleep( 1 );
    }

    cout << endl << "done." << endl;

    return EXIT_SUCCESS;

}