シカクいアタマをマルくする問題 その1

清水川さんのブログに書いてあったこの問題をPythonで解いてみました。

回文数

"121" や "3333" のように右から読んでも左から読んでも同じ並びになる数値を回文数とよぶことにします。この回文数について以下の問いに答えなさい。

問い1

5を掛けると回文数となる、3桁の数値のうち最大の数値を答えなさい。

できた。

>>> [x for x in range(100,1000) if `x*5` == `x*5`[::-1]]
[101, 103, 105, 107, 109, 111, 113, 115, 117, 119]

後から確認したら,清水川さんのコメント欄に書いてあるのと,ほぼ一緒でした。でも,コメント欄をさらに読み進めていくと,次のようにした方が速いと書かれています。

>>> (x for x in xrange(999,99,-1) if `x*5` == `x*5`[::-1]).next()
119

たしかに今回は3桁だからいいけど,もっと大きな桁数になった場合は,いちいちリストを生成せずにジェネレータ式を使った方が効率がいいですね。ジェネレータオブジェクトは,遅延評価なので値が必要になった時にしか生成されません。

勉強になるなぁ。ぼくのアタマも少しだけマルくなりました。

[Python] Setの積は元から定義されていた

昨日の Rangeの積を求める なんですが, set のベースクラス BaseSet の定義を見てみたらこう書かれていました。

def __and__(self, other):
    """Return the intersection of two sets as a new set.
     (I.e. all elements that are in both sets.)
    """
    if not isinstance(other, BaseSet):
        return NotImplemented
    return self.intersection(other)

ということは,Set型は元から a & b という書き方ができるんですね。実際に試してみると,とても簡単に集合の積がとれました。

>>> a = range(1,7); b = range(3,10); c = range(4,12)

>>> print a,b,c
[1, 2, 3, 4, 5, 6] [3, 4, 5, 6, 7, 8, 9] [4, 5, 6, 7, 8, 9, 10, 11]

>>> set(a) & set(b)
set([3, 4, 5, 6])

>>> set(a) & set(b) & set(c)
set([4, 5, 6])

終了──── しかも,RubyのRangeって数値以外も扱えるらしいじゃないですか。
全然だめだ>自分

気を取り直して,list型に「 & 」の振る舞いを実装してみることにします。元のlist型には「 & 」は定義されていないので,当然ながら「 a & b 」という表記はエラーになります。

>>> a = [1,2,3]; b = [2,3,4]; c = [3,4]

>>> a & b
Traceback (most recent call last):
  File "", line 1, in 
TypeError: unsupported operand type(s) for &: 'list' and 'list'

& 演算子は,__and__メソッドを定義することで利用可能になるので,新たに list を継承した xlist クラスを作ります。

class xlist(list):
    def __and__(self, other):
        return xlist(filter(lambda x: x in other, self))
実行してみましょう。
>>> a = xlist([1,2,3,4]); b = xlist([2,3,4,5]); c = xlist([4,5,6])

>>> a & b
[2, 3, 4]

>>> a & b & c
[4]

できた。

り, Reduce 最高

[Python] Rangeの積を求める

ueBLOGさんのブログ経由で知ったこの Range の積を求めるというもの。

» [Ruby] Rangeの積を求める - masuidrive
» Life is beautiful: 教えながら学ぶRuby:「Rangeの積を求める」をやってみた

を Python で書いてみました。引数に3個以上とかのRangeを渡されても大丈夫です。
元ネタの Ruby は Range クラスを拡張していますが、 Python の Range はビルトイン関数なので関数定義で。

from sets import Set
def range2(*args):
    return reduce(Set.intersection, map(Set, args))

では、実行してみましょう。

>>> range2([1,2,3,4,5,6], [3,4,5,6,7,8,9], [4,5,6,7,8,9,10,11])
[4, 5, 6]
>>> range2([1,2,3,4,5,6], [3,4,5,6,7,8,9], [10,11])
[]
>>> range2(range(1,8), range(3,7), range(4, 200))
[4, 5, 6]

で、何が言いたいかというと、「Reduce なくさないでよ!」ということです。functools.reduce という感じで残るみたいですが、組み込み関数じゃなくなるのは悲しいなぁ。

便利べんり。 Reduce 最高。

Re: リスト内包表記キタコレ

a2cさんのブログに書いてあった Python に関するこの問題。
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
の配列を、リスト内包表記を使って次のように変換せよ。
[1, 2, 3, 4, 5, 6, 7, 8, 9]

前もって構造が分かっているという前提だと、こんな感じでしょうか。
List Comprehension を二重にするだけ。

>>> [x for y in arr for x in y]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
reduce()を使っていいならば、こっちのほうが自分は好きかも。
2.6からは使えなくなるみたいですが。
>>> reduce(lambda a,b: a+b, arr)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

リストが何重の入れ子になっているか分かっていない場合は、 List Comprehension だけでできるのかなぁ… 分かりません、誰か教えてください。

Playing with NodeBox

NodeBoxで遊ぶ。別に暇なわけじゃなくて、ちょっとした息抜きなのだ。



NodeBox/QtをLinuxで動かしてみた

Pythonスクリプトでグラフィックス/アニメーションをProcessing風に生成できるアプリケーション NodeBox のQt版を Linux (Ubuntu)で動かしてみました。

1. PyQt関連ツールのインストール

$ sudo aptitude install pyqt4-dev-tools

2. nodeboxのダウンロード

$ svn co http://dev.nodebox.net/svn/nodebox/branches/try-qt/
$ cd try-qt
$ python qtrun.py

NodeBoxの画面が立ち上がってきて、右上にスクリプトを打ち込んで Ctrl+R を押すと左側のペインに描画されます。こないだのPyDevCampで見たのと一緒ですね。たぶん、Core Image依存の部分は動かないかもしれませんが簡単な描画なら問題ないようです。


画像クリックで拡大

便利べんり。Ubuntu 最高。

Python Developers Camp 2008冬に参加してきました

3月7日〜9日の3日間にかけて、長野県松本市で開催されたPython Developers Camp 2008冬に参加してきました。北は北海道、南は九州まで全国からPythonistaが40人ほど集結しました。

PyDevCampは何度目かの参加になるのですが、今までのWeb開発中心の内容とは打って変わって、PythonからGAINERデバイスを操作したり、Nodeboxでグラフィックス処理とかFeliCaのデータを読み取ったりというのが目を引きました。僕は、Webとリアル世界との融合がこれからますます進んでいくと思っているのですが、それを先取りした形になっていて興味深かったです。Web2.0はWebサイト間のマッシュアップだったけど、Web3.0はリアル世界とのマッシュアップになるのではと勝手に予想しています。そして、その時このPythonの汎用さはきっとアドバンテージになるはず。

その他にもpyspecによるユニットテストやbuildbotによる自動化の方法、アクセスログ解析スクリプトなども大変参考になりました。また、今回Pylonsを初めてインストールして使ってみたのですが、なるほどデバッグが非常にやりやすくて、SQLAlchemyが標準っていうのがいいですね。テンプレートに関しては好みが分かれそうですが、いじっていたのが某JSONベースのアプリケーションなので気になりませんでした。こちらは近いうちに公開したいと思います(たぶん)。

幹事をしてくださった方、Pythonをこよなく愛する参加者の方々、本当にお疲れさまでした。また次回も懲りずに参加したいと思います。

参加された方々のブログ: カトキチさんのまとめが素晴らしい!

Python Developers Campの締め切り迫る

Python Developers Camp 2008冬が3月7日(金)〜9日(日)の日程で開催されます。これからPythonを初めてみたいという初心者向けの講座や、gainerのようなハードウェアデバイスをPythonから操作したりするマニアックなものまで盛り沢山です。そして、今回は「みんなのPython」の著者である柴田さんのMonty Python教室もあります。
現地参加の締め切りは、2月28日まで。参加したいと思っている方はお早めに!

■ 開催概要
  • 名称 Python Developers Camp 2008 冬
  • 日時 2008年3月7日(金)〜9日(日)
    二泊三日(土曜泊のみの方の参加も歓迎します)
  • 場所 松本ホテル花月
  • 対象 Pythonに興味のある方
  • 費用 交通費,宿泊費を含んだ実費
    宿泊費は交渉中ですが、1泊あたり1万円+会議室代1000円程度を予定
  • 主催 Python Developers Camp 2008 冬実行委員会

■ 応募方法

合宿の参加者用のメーリングリストが設置されています。参加希望者はこちらに加入してください。グループへの参加にはgoogleのアカウントが必要になります。

http://groups.google.co.jp/group/pydevcamp2008w/

※グーグルのアカウントはgmail以外のメールアドレスを使っても作成可能です。今年はリモート参加者については事前の登録は行いません。

2/23追記:
とても恥ずかしいミスをしていたので修正しました。見せしめのために貼り付けておきます。
pyhon

OpenCVを使った顔認識を試してみる

インテルが開発・公開しているオープンソースのコンピュータビジョンのライブラリ OpenCV を試してみました。コンピュータビジョン(computer vision)とは、「ロボットの目」を作る研究分野だそうです。画像処理や構造解析、物体追跡などたくさんの機能が実装されていますが、その中でも多くの人が試している顔認識をやってみました。


Photo by weboo

Fedora 8だとパッケージが用意されているので、下記のコマンドでインストールできます。

# yum install opencv opencv-python

そして、使ったプログラムがこれ。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from opencv.cv import *
from opencv.highgui import *

def faceDetect(imgfile):
  # 画像を読み込む
  src_img = cvLoadImage(imgfile, CV_LOAD_IMAGE_COLOR)
  src_gray = cvCreateImage(cvSize(src_img.width, src_img.height),
                             IPL_DEPTH_8U, 1)

  # ブーストされた分類器のカスケードを読み込む
  cascade_name = "haarcascade_frontalface_default.xml"
  cascade = cvLoadHaarClassifierCascade(cascade_name, cvSize(1,1))
  
  # メモリを確保し,読み込んだ画像のグレースケール化,ヒストグラムの均一化を行う
  storage = cvCreateMemStorage(0)
  cvClearMemStorage(storage)
  cvCvtColor(src_img, src_gray, CV_BGR2GRAY)
  cvEqualizeHist(src_gray, src_gray)

  # 顔検出
  faces = cvHaarDetectObjects(src_gray, cascade, storage,
                              1.11, 4, 0, cvSize(40, 40))

  # 検出された全ての顔位置に枠を描画する
  for c, i in enumerate(faces):
    pt1 = cvPoint(int(i.x), int(i.y))
    pt2 = cvPoint(int(i.x + i.width), int(i.y + i.height))
    cvRectangle(src_img, pt1, pt2, CV_RGB(255,0,0), 3, 8, 0);

  return src_img

if __name__ == '__main__':
  if len(sys.argv) < 2:
    print "Usage: facedetect <filename>\n" ;
    sys.exit(-1)
  img = faceDetect(sys.argv[1])

  # 画像を出力
  cvSaveImage("output.jpg", img)

これはいろいろ応用ができそうで面白いですね。haarcascade_frontalface_default.xml のパターンデータが肝なんだけど、自分で学習させようとする場合は、背景画像が3,000通り、対象物の画像が7,000通りくらい必要だそう。ただし、トレーニング用の画像を自動生成することも可能なようなので頑張ればできなくもなさそうですね。

その後、erogeekな人のプロジェクトはどうなったんだろう…

OpenCV プログラミングブック
奈良先端科学技術大学院大学
毎日コミュニケーションズ (2007/09/22)
売り上げランキング: 4367

 

関連リンク:
Open Source Computer Vision Library
opencv.jp - OpenCV サンプルコード
OpenCV for Linux

Python でローカルのアドレスを取得する

ifconfigの出力をsedでパース — ありえるえりあのようにLinuxだとSocketを叩くことでIPアドレスを取得できます。以下、Pythonで取得するサンプル。

#!/usr/bin/env python
import socket
import fcntl
import sys

def ifconfig(ifname):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        result = fcntl.ioctl(s.fileno(), 0x8915 #SIOCGIFADDR,
                             (ifname+'\0'*32)[:32])
    except IOError:
        return None

    return socket.inet_ntoa(result[20:24])

if __name__ == '__main__':
    print ifconfig(sys.argv[1])
実行例:
$ python ifconfig.py eth0
192.168.0.1

また、loを省くために元記事ではsedの正規表現を使っているのだけど、D-BUSが使える今時の環境ならば次のように書けます。サブネットマスクやDNSサーバ、デフォルトゲートウェイ、有線か無線かなども取得できます。

#!/usr/bin/env python
import dbus

NM_SERVICE = 'org.freedesktop.NetworkManager'
NM_OPATH   = '/org/freedesktop/NetworkManager'
NM_DSERVICE = "%s.Devices" % NM_SERVICE

def getAllDevices():
    bus = dbus.SystemBus()

    proxy = bus.get_object(NM_SERVICE, NM_OPATH)
    devs = proxy.getDevices(dbus_interface=NM_SERVICE)

    devices = []
    for dev in devs:
        device = bus.get_object(NM_SERVICE, dev)
        devices.append(device.getProperties(NM_DSERVICE))

    return devices

def getDefaultDevice():
    ds = getAllDevices()
    for d in ds:
        if d[10] != '0.0.0.0':
            return d
    
if __name__ == "__main__":
    d = getDefaultDevice()
    print '%s: %s/%s' % (d[1], d[6], d[7])
実行例:
$ python ifconfig2.py
eth0: 192.168.0.1/255.255.255.0