7は孤独な数字
森博嗣さんの『すべてがFになる』という作品の冒頭で、こんな会話があります。
「1から10までの数字を二組に分けてごらんなさい。そして、両方とも、グループの数字を全部かけ合わせるの。二つの積が等しくなることがありますか?」
「ありません」萌絵は即答した。「片方のグループには7がありますから、積は7の倍数になりますけど、もう片方には7がないから、等しくはなりません」
「ほら、7だけが孤独でしょう?」真賀田女史が言った。
もし二つのグループの積が等しいなら、全ての数字の総積は必ず平方数になります。
7は素数だし、1から10までの中で自分自身以外の倍数はないので、二つのグループの積が等しくなることはありません。
それなら、7を除くと、積が等しくなることはありますか?と、自然と考えたくなりますね。
検証してみましょう。
確かに真賀田四季が言った通りですw
ちょっと考えてみたら、実はこの問題はある種の Subset sum problem の 積バージョンとなり、NP完全問題ですね。
Google ColabでMeCabを使ってみよう
詳しくは MeCab 公式ホームページをご参照ください。
- mecab-python3 のインストール
!pip install mecab-python3
- mecab-ipadic-NEologd のインストール
!apt install mecab libmecab-dev git make curl xz-utils file !git clone --depth 1 https://github.com/neologd/mecab-unidic-neologd.git !echo yes | mecab-unidic-neologd/bin/install-mecab-unidic-neologd -n
MeCab.Tagger クラスに-d
オプションで NEologd 辞書のパスを指定することができます。
import MeCab tagger = MeCab.Tagger("-d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-unidic-neologd") print(tagger.parse('フシギダネは不思議だね。'))
- 実行結果
AtCoder Regular Contest 077 C - pushpush
数列 a の長さが奇数の時、反転した偶数 index の要素 + 奇数 index の要素が数列 b になります。
数列 a の長さが偶数の時、反転した奇数 index の要素 + 偶数 index の要素が数列 b になります。
例えば、
b | index |
---|---|
3 1 2 | 2 0 1 |
4 2 1 3 | 3 1 0 2 |
5 3 1 2 4 | 4 2 0 1 3 |
n = int(input()) a = list(map(int, input().split())) b = [] odd_idx_list = a[1::2] even_idx_list = a[::2] if n % 2== 0: odd_idx_list.reverse() else: even_idx_list.reverse() b = even_idx_list + odd_idx_list print(*b)
PythonとHerokuでKindle日替わりセールの情報を送ってくれるslackbotを作ってみる
まずはslackbot
ライブラリとスクレイピングするために必要なライブラリ(requests
とBeautiful Soup
)をインストールする。
$ pip install slackbot $ pip install requests beautifulsoup4
ディレクトリ構造
$ tree . ├── Procfile ├── libs │ ├── __init__.py │ └── my_functions.py ├── plugins │ ├── __init__.py │ └── my_mention.py ├── requirements.txt ├── run.py └── slackbot_settings.py
実装
run.py
とslackbot_settings.py
# -*- coding: utf-8 -*- from slackbot.bot import Bot def main(): bot = Bot() bot.run() if __name__ == "__main__": print('start slackbot') main()
# -*- coding: utf-8 -*- import os # botアカウントのトークンを指定 API_TOKEN = os.environ.get('SLACKBOT_API_TOKEN') # このbot宛のメッセージで、どの応答にも当てはまらない場合の応答文字列 DEFAULT_REPLY = "何言ってんだこいつ" # プラグインスクリプトを置いてあるサブディレクトリ名のリスト PLUGINS = ['plugins']
my_functions.py
は以下の通りです。
# -*- coding: utf-8 -*- import re import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry from bs4 import BeautifulSoup headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'} def kindle_daily_deal(url): text = '' s = requests.Session() retries = Retry(total=5, backoff_factor=0.1, status_forcelist=[500, 502, 503, 504]) s.mount("https://", HTTPAdapter(max_retries=retries)) html = s.get(url, headers=headers).text.encode('utf-8') soup = BeautifulSoup(html, features="html.parser") # CSSセレクターを使って日替わりセールリストを取得する carousel_list = soup.find('ol', attrs={'class': 'a-carousel'}) # 本のタイトルと著者名を取得する title_author_list = carousel_list.select("[class='a-truncate-full']") # 本の価格を取得する price_list = carousel_list.select("[class='a-price-whole']") res = {} res['title'] = ['『{}』'.format(t.get_text()) for i, t in enumerate(title_author_list) if i % 2 == 0] res['author'] = [re.sub(r"[\n ]", "", a.get_text()) for i, a in enumerate(title_author_list) if i % 2 == 1] res['price'] = ['{}\n'.format(p.get_text()) for p in price_list] for i in range(len(price_list)): text += '📚 '+ res['title'][i] + '\n著者:' + res['author'][i] + '\n価格:' + res['price'][i] return text
my_mention.py
kindleというキーワードに反応して、今日のKindle日替わりセール情報を返事する。
# -*- coding: utf-8 -*- import re from slackbot.bot import respond_to # @botname: で反応するデコーダ from slackbot.bot import listen_to # チャネル内発言で反応するデコーダ from libs import my_functions # 外部関数の読み込み @respond_to(r'kindle', re.I) @listen_to(r'kindle', re.I) def kindle(message): url = 'https://www.amazon.co.jp/Kindle%E6%97%A5%E6%9B%BF%E3%82%8F%E3%82%8A/b/?ie=UTF8&node=3338926051&ref_=sv_nav_ebook_4' text = my_functions.kindle_daily_deal(url) msg = 'ほら!今日のKindle日替わりセールだぞ\n```' + text + '```' message.reply(msg)
デプロイ
requirements.txt
とHerokuの設定ファイルProcfile
を用意する。
$ pip freeze > requirements.txt
Procfile
worker: python run.py
$ heroku config:set SLACKBOT_API_TOKEN=xoxb-xxxxx
環境変数の確認。
$ heroku config
Settingsタブでpython buildpackを追加する。
あとは、Deployタブで書いた通りにコマンドを実行する。
$ heroku login $ git init $ heroku git:remote -a xxx $ git add . $ git commit -am "make it better" $ git push heroku master
さて、プロセスを起動してみる。
$ heroku ps:scale worker=1
プロセスを確認する。
$ heroku ps
プロセスを停止するには、以下のコマンドを実行すれば良い。
$ heroku ps:scale worker=0
ログを見る。
$ heroku logs --tail
定期投稿
日替わりセールなので、やっぱり毎日指定した時間にメッセージを投稿したいです。 Herokuでの定期実行はHeroku Schedulerというアドオンが提供しているが、今回はpython-crontabというライブラリを用いて定期実行を行う。
python-crontabライブラリをインストールする(croniterのインストールも必要になる)。
$ pip install python-crontab $ pip install croniter
cron.py
HerokuのタイムゾーンはUTC
なので、9時間の時差がある。
# -*- coding: utf-8 -*- from crontab import CronTab def main(): cron = CronTab() job1 = cron.new('python3 kindle.py') # 毎日の20:00(JST)に実行する job1.setall('00 11 * * *') for res in cron.run_scheduler(): print('The schedule has been executed.') if __name__ == "__main__": main()
kindle.py
# -*- coding: utf-8 -*- import re import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry from slacker import Slacker from bs4 import BeautifulSoup from slackbot_settings import API_TOKEN headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'} def kindle_daily_deal(url): # 省略 def main(): url = 'https://www.amazon.co.jp/Kindle%E6%97%A5%E6%9B%BF%E3%82%8F%E3%82%8A/b/?ie=UTF8&node=3338926051&ref_=sv_nav_ebook_4' text = kindle_daily_deal(url) msg = 'ほら!今日のKindle日替わりセールだぞ\n```' + text + '```' slack = Slacker(API_TOKEN) slack.chat.post_message('#reminder', msg, as_user=True) if __name__ == "__main__": main()
Procfile
worker: python run.py cron: python cron.py
プロセスを起動する。
$ heroku ps:scale cron=1
結果はこんな感じ
VimでPythonの入力補完
jedi-vim
という python 入力補完プラグインを試してみたいと思って、インストール完了後 Vim でファイルを開くと、
Error detected while processing function jedi#init_python[11]..<SNR>72_display_exception: line 19: Error: jedi-vim failed to initialize Python: jedi-vim requires Vim with support for Python 3. (in function jedi#init_python[4]..<SNR>72_init_ python, line 4)
というエラーが出る。
ずっと使ってた Vim は macOS の built-in Vim バージョンで、-python3
になっていた。
$ vim --version | grep python +cmdline_hist -langmap +python/dyn +visual +cmdline_info +libcall -python3 +visualextra
Homebrew には、 +python3
でコンパイルされた Vim バージョンが入っているので、簡単に Homebrew で新しいバージョンの Vim をインストールする。
$ brew install vim
さっそく+python3
となっていることを確認。
$ /usr/local/bin/vim --version | grep python +comments +libcall -python +visual +conceal +linebreak +python3 +visualextra
Homebrew でインストールした Vim のパスは/usr/local/bin/vim
になっているので、
alias を設定するまたは PATH を変更すれば良い。
alias vim='/usr/local/bin/vim'
を~/.zshrc
に追加した後、source ~/.zshrc
を実行して、設定ファイルの内容を反映させる。
PATH を変更する場合は、/usr/local/bin
は/usr/bin
より優先されることが必要です。
そして、Vim で:echo has('python3')
を実行して、1 が返ることを確認。