フィボナッチ数列のうち、各桁の数字を足した数で割り切れる(ry

たまたま見つけたネタ。

 cf. フィボナッチ数列 – utthi_fumiの日記

タイトルが長くなったので省略しちゃったけど、ちゃんと引用すると、こう。


フィボナッチ数列のうち、各桁の数字を足した数で割り切れる数を以下の例に続けて小さいほうから5個求めてください。

2 → 2 ÷ 2

3 → 3 ÷ 3

5 → 5 ÷ 5

8 → 8 ÷ 8

21 → 21 ÷ 3・・・2 + 1= 3で割る

144 → 144 ÷ 9・・・1 + 4 + 4 = 9で割る

引用してもわかりにくいな。要するに、フィボナッチ数列を {Fn} (n=0,1,2…)として、Fn の各桁の数を足し合わせたものを S(Fn) とすると、S(Fn) で割り切れる Fn を小さい順に5つ(ただし、例に出ている 114 までを除く)求めよ、ってことだ。

「 これはメモ化と大きな桁を扱う事についての問題です 」なんて書いてあるけど、大きな桁はその通りだけど、n から Fn を求めようとするからメモ化なんかが必要になるんであって、この問題では小さい順に調べればいいんだから素直にフィボナッチ数列の頭から生成すればいい。

というわけで、Go でやってみた。ただやってみるだけじゃなくて、あんまり使ったことのないゴルーチンとチャネルを使ってみたよ。

まずは単純にフィボナッチ数列を生成するところから。

package main

import (
	"fmt"
)

func main() {
	s := makeFib()
	for i := 0; i < 10; i++ {
		f := <- s
		fmt.Println(f)
	}
}

type Stream chan int

func makeFib() Stream {
	s := make(Stream)
	go func() {
		a, b := 0, 1
		for {
			f := a
			a, b = b, a + b
			s <- f
		}
	}()
	return s
}

関数 makeFib は、チャネルを返す。で、このチャネルからフィボナッチ数列を一つずつ取り出せる。makeFib の中のゴルーチンは無限ループになっているので、(アルゴリズム上は)無限に取り出すことができる(実際はどうだかわからない)。実行結果は次の通り。

^o^ > go run fib_chan.go
0
1
1
2
3
5
8
13
21
34

さて、それじゃ本題に入ろう。といってもフィボナッチ数列はもう得られているんだから、あとは条件に合う数 Fn を5つ出力するだけだ。こうなった。

package main

import (
	"fmt"
)

func main() {
	s := makeFib()
	i := 0
	for i < 5 {
		f := <- s
		if f > 144 && isDivisible(f) {
			i++
			fmt.Println(f)
		}
	}
}

type Stream chan int

func makeFib() Stream {
	s := make(Stream)
	go func() {
		a, b := 0, 1
		for {
			f := a
			a, b = b, a + b
			s <- f
		}
	}()
	return s
}

func sumOfPlaces(n int) int {
	sum := 0
	for n > 0 {
		sum += n % 10
		n /= 10
	}
	return sum
}

func isDivisible(n int) bool {
	return (n % sumOfPlaces(n)) == 0
}

実行結果。

^o^ > go run fib_divisible.go
2584
14930352
86267571272
498454011879264
160500643816367088

OK。ちゃんとできた。

Pythonでwheelパッケージとその中のコマンドのバージョンを合わせる方法

タイトルが長いな。

コマンドを wheel パッケージにしたときに、そのコマンドとパッケージのバージョンを合わせたい、という当然の欲求を満足するための、(現在のところ)最良と思われる方法を見つけたのでメモしておく。

コマンドでバージョンを出力するには、コマンドのスクリプト中でバージョンがわからなければならないし、パッケージングするためには setup.py の中でバージョンがわからなければならない。当然のことながら、両方にバージョンを書くのは愚の骨頂。だからどこか1ヵ所でバージョンを定義して、それをコマンドと setup.py から参照することにする。

で、それはどこなのかというと、モジュールの __init__.py の中だ。この定義をコマンドと setup.py の両方から参照するんだけど、ちょっと工夫が必要だった。

ここでは、簡単な Hello world コマンドを例に説明する。ファイル構成はこんな感じ。

^o^ > tree /f .
フォルダー パスの一覧
ボリューム シリアル番号は 666A-93A9 です
C:\USERS\TAKATOH\DOCUMENTS\SANDBOX\HELLOPY
│ setup.py

└─hello
hello.py
__init__.py

hello ディレクトリがモジュールになっていて、コマンドの実体(関数)は hello/hello.py ファイルに書いてある。

バージョンの定義は、次の通り、hello/__init__.py の中。

__version__ = '0.3.2'

バージョン番号はいろいろ試行錯誤した結果を反映している。

これを setup.py から参照するのは簡単。モジュールをインポートして変数を参照するだけ。

import setuptools
import hello


setuptools.setup(
    name='hellopy',
    description='hello app.',
    version=hello.__version__,
    author='takatoh',
    author_email='takatoh.m@gmail.com',
    packages=setuptools.find_packages(),
    classifiers=[
        'License :: OSI Approved :: MIT License',
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
    ],
    entry_points={
        'console_scripts': [
            'hellopy=hello.hello:main',
        ],
    }
)

コマンドから参照するにはもうちょっと工夫が必要。こうなった。

import os
import argparse


here = os.path.abspath(os.path.dirname(__file__))
about = {}
with open(os.path.join(here, '__init__.py')) as f:
    exec(f.read(), about)
VERSION = 'v' + about['__version__']


def main():
    parser = argparse.ArgumentParser(description='Hello tool.')
    parser.add_argument('name', metavar='NAME', type=str, nargs='?', default='world')
    parser.add_argument('-v', '--version', action='version', version=VERSION)
    args = parser.parse_args()

    print('Hello, {name}!'.format(name=args.name))


if __name__ == '__main__':
    main()

hello/__init__.py を読み込んで exec で評価して変数への参照を得ている。直接 hello モジュールをインポートできれば楽なんだけど、このコマンド(スクリプト)自体が hello モジュールに属しているので、それはできないみたいだ。

というわけで、この方法が一番いいようだ。もっといい方法が見つかるまではこれで行こうと思う。

最後に、実際に wheel パッケージを作って、インストールして試してみよう。

^o^ > python setup.py bdist_wheel
^o^ > pip install dist/hellopy-0.3.2-py3-none-any.whl
^o^ > hellopy --version
v0.3.2

^o^ > hellopy
Hello, world!

^o^ > hellopy Andy
Hello, Andy!

うまくいってる。

UbuntuにRMagickをインストール

環境

takatoh@apostrophe $ cat /etc/os-release
NAME="Ubuntu"
VERSION="16.04.5 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.5 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
takatoh@apostrophe $ ruby -v
ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu]

↓このページによると、ImageMagick がバージョン7以降だと RMagick は使えないらしい。

 cf. RMagickのインストールで奈落に落ちた – Qiita

takatoh@apostrophe $ convert -version
Version: ImageMagick 6.8.9-9 Q16 x86_64 2018-09-28 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2014 ImageMagick Studio LLC
Features: DPC Modules OpenMP
Delegates: bzlib cairo djvu fftw fontconfig freetype jbig jng jpeg lcms lqr ltdl lzma openexr pangocairo png rsvg tiff wmf x xml zlib

6.8.9 だ、ラッキー。

依存パッケージとRMagickのインストール

まずは依存パッケージから。

takatoh@apostrophe $ sudo apt install libmagickcore-dev libmagickwand-dev

そして、RMagick。

takatoh@apostrophe $ sudo gem install rmagick
Building native extensions. This could take a while…
Successfully installed rmagick-2.16.0
Parsing documentation for rmagick-2.16.0
Installing ri documentation for rmagick-2.16.0
Done installing documentation for rmagick after 4 seconds
1 gem installed

完了。