Lahの部屋

移植予定です→https://lah-blog.pages.dev/

ChatGPTコーディングのTIPS

 ChatGPTはすこぶる優秀なプログラマーだ。私は、これまでに色々な開発を彼にお願いしてきた。今回は、彼に開発を依頼するときの作法をまとめようと思う。備忘録のようなものなので、体系的にはならなかった。

 

GPT-4に英語で命令せよ

 まずは基本的なところ。2023年5月現在のGPTの最新バージョンであるGPT-4を使用しておきたい。その上で、他の言語よりも英語で命令するのが望ましい。当然ながら、英語の文章はGPTの学習に際して最も多く投入されている。正確なレスポンスが欲しいのであれば英語で命令しない選択肢はない。

 英語が苦手な人は、それこそGPTに翻訳させるのがいいと思う。彼は言語処理のプロなので、文脈もある程度汲み取って柔軟に翻訳してくれる。

 

単体開発をさせるべし

 これはChatGPTだからというよりも、システム開発の基本である。

 例えば、「Webサイトから抽出してきた文字列をSNSに投稿する」というシステムを作成するとする。

 このとき、このシステム全体をGPTに作らせてはいけない。うまく動作しなかった際に、原因の切り分けがしにくくなるからだ。

 以下の1、2というユニットに分けて、それぞれをGPTに作成させるのがよい。

  1. Webサイトから文字列を抽出してコンソールに出力する機能
  2. 与えられた文字列をSNSに投稿する機能

 それぞれ上手く動作したことを確認したら、自分で結合して完成だ。

 

箇条書き形式で命令せよ

 相手は人工知能なのだから、人間に伝えるような自然な文章として体裁を整える必要はないのだ。そのため、箇条書き形式で命令をしたほうが、手間もかからないし、要件を伝達しやすいと思う。

 "Please generate a script. The requirements are below."などのように伝え、要件を羅列していくのが最も書きやすかった。

 

情報の不足を考慮せよ

 この命令ができるところがChatGPTの真価であると言ってよい(言い過ぎ)。命令文の最後に「情報に不足があれば質問してください」という旨を記載する。"If you need more information, please ask me."みたいな感じでいい。

 僕らは人間なので、文脈や前提条件などを要件に盛り込み忘れるときがある。そうした場合、普段ならGPTは勝手に文脈を想定してとりあえずの回答をくれるのだ。でも、この文言を追加すれば、回答に必要なピースを向こうから求めてきてくれるのだ。

 すべての命令文の末尾には、とりあえずこれをつけておくのが無難だ。

 

出力が途切れたときは

 GPTに文章を生成させていると、ときどき文章の途中で回答が途切れることがある。その場合は、"Go on""Continue" などと命令すれば、途切れたところから出力を続けてくれる。日本語で命令したときには「つづき」などと言えばよい。

 

まとめ

 AIにプログラミングを任せられるようになったのは非常に嬉しい。もっと性能が向上すれば、個人が気軽に大規模システムを生成できる時代が来るかもしれない。

RSA暗号の原理(+証明)

概要

 RSA暗号は現在普及している公開鍵暗号の基礎となる暗号技術である。説明しているサイトは色々あるが、他人が書いたものなので読みにくかった。私にとって分かりやすいように書く。Wikipediaの同項目を参考にした。

 証明中で用いたオイラーのφ関数とフェルマーの小定理については後半で解説した。

 

暗号化と復号

 0以上n未満の整数の集合を\mathbb{Z}_nとおく。

 異なる2つの素数 p, q をとり、 n:=p,q を定義する。

  (p-1)(q-1)と互いに素であるような e(公開鍵)を任意に設定する。

 このとき、秘密鍵 d \in \mathbb{Z}_n

\begin{align} d \equiv e^{-1} \pmod n \end{align}

、すなわち eのモジュラ逆数となるように作成される。 eのみから導くのは困難だが、p, qが既知であれば、ユークリッドの互除法によって導出できる。

 平文 a \in \mathbb{Z}_n-\{0\}について、暗号文 bは、

\begin{align} b = \{ z \in \mathbb{Z}_n | z \equiv a^e \pmod n \} \end{align}

と算出できる。これを復号したものを a'とすると、

\begin{align} a' = \{ z \in \mathbb{Z}_n | z \equiv b^d \pmod n \} \end{align}

とかけ、 a' = aである。

 

原理の証明

  e \phi(n)=(p-1)(q-1)オイラーのφ関数)は互いに素だから、任意の正の整数 dについて、

\begin{align} de -x\phi(n)= 1\end{align}

となるような正の整数 xが存在する。

定義より

\begin{align} a' \equiv a^{de} \pmod n = a^{x\phi(n)+1} = (a^\phi(n))^x \cdot a \tag{1} \end{align}

である。したがって、

\begin{align} a' \equiv (a^\phi(n))^x \cdot a \pmod p \tag{2} \\\ a' \equiv (a^\phi(n))^x \cdot a \pmod q \tag{3} \end{align}

が成立する。

  a n未満であることから、以下の( i )( ii )( iii )によって、すべての場合を扱える。以下では式(1),(2),(3)を用いる。

( i )  a p,qのそれぞれと互いに素であるとき

 フェルマーの小定理より

\begin{align} a^{\phi(n)} \equiv 1 \pmod p \tag{3} \\\ a^\phi(n) \equiv 1 \pmod q \tag{4} \end{align}

が成立する。式(3),(4)より

\begin{align} a^{\phi(n)} \equiv 1 \pmod n \end{align}

であるから、

\begin{align} a' = (a^{\phi(n)})^x \cdot a \equiv a \pmod n \end{align}

が成立する。

( ii )  a pの倍数であるとき

\begin{align} a \equiv 0 \pmod p \end{align}

であるから、

\begin{align} a' = a^\phi(n) \cdot a \equiv a \pmod p \tag{5} \end{align}

である。

  a qと互いに素だから、フェルマーの小定理より、

\begin{align} a^{q-1} \equiv 1 \pmod q \end{align}
が成立する。よって、

\begin{align} a^\phi(n) = a^{(p-1)(q-1)} \equiv 1 \pmod q \end{align}

であることから、

\begin{align} a' = (a^\phi(n))^x \cdot a \equiv a \pmod q \tag{6} \end{align}

となる。式(5),(6)より、

\begin{align} a' \equiv a \pmod n \end{align}

が成立する。

( iii )  a qの倍数であるとき

 ( ii )と同様にして

\begin{align} (a^\phi(n))^x \cdot a \equiv a \pmod n \end{align}

が示される。

 ( i )( ii )( iii )より、すべての場合について

\begin{align} a' \equiv a \pmod n \end{align}

が成立する。

  a,a' \in \mathbb{Z}_n-\{0\}であるから、 a'=aである。

 

オイラーのφ関数

 正の整数 nに対して、 nと互いに素である 1以上 n以下の整数の個数 \phi(n)のことをこう呼ぶ。

 したがって、 n素数ならば \phi(n)=n-1である。

 異なる2つの素数 p, q について  n:=pq を定義したとき、

\begin{align} \phi(n) = (p-1)(q-1) \end{align}

が成り立つ。

 

フェルマーの小定理

 任意の素数  p と、 pの倍数でない任意の整数  a について、

\begin{align} a^p \equiv a \pmod p \end{align}

が成立する。

証明

 任意の素数  p と任意の正の整数  m をおく。二項定理より、

\begin{align} (m+1)^p=m^p+{}_p\mathrm{C}_1 m^{p-1} + {}_p\mathrm{C}_2 m^{p-2} + \cdots + {}_p\mathrm{C}_{p-1} m + 1 \tag{★} \end{align}

が成り立つ。

  p素数であるため、 p未満の正の整数 kについて {}_p\mathrm{C}_k pの倍数となることから、

\begin{align} (m+1)^p \equiv m^p+1 \pmod p \end{align}

が成立する。

( i ) 式(★)に  m=1 を代入すると、

\begin{align} 2^p \equiv 2 \pmod p \end{align}

となり、 a=2 において命題は成り立つ。

( ii ) 命題が  a=k (k = 2,3,\cdots) において成立すると仮定する。

 このとき、式(★)から、

\begin{align} (k+1)^p \equiv k^p+1 \pmod p \end{align}

であり、仮定より、

\begin{align} k^p \equiv k \pmod p \end{align}

であることを踏まえると、

\begin{align} (k+1)^p \equiv k+1 \pmod p \end{align}

となり、 a = (k+1) においても命題が成立する。

 ( i )と( ii )より、題意は示された。

 

まとめ

 楽しかった。せっかく原理を学んだので、RSAで平文を暗号化するツールを作りたい。

思考の切実さ

自分は頭が悪くなったなあと思う。

大学生のときは物事をもっと精緻に捉えていたし、起きているときは常に考え事をする習慣があった。社会人になってそれは明らかに失われてきている。内省力ないし思考力は僕が矜持をもっているところでもあったから、少し悲しい。

対処すべき問題が少なくなった、というのが原因としてあると思う。もちろん仕事や家庭で考えなくてはいけないことはあるが、そこでは自分の内に向かう思考が求められる機会は少ない。

学生時代には、精神的に自立が求められながら家庭に従属するという宙吊り状態にある。親族内で流通する倫理にも対処しなくてはいけない。

また、家庭や義務教育などのように自らの意思によらない所属をもつことになる。存在の仕方がすでに不安定だったのだ。

日常が、トピックに恵まれていた。

やはり結局、個人が熱心に回す内省的思考というのは、各人の抱える切実な問題に対する直接的あるいは間接的な対処の域を出ないのではないか。それ自身で完結する純粋な思考などなく、思考は常に何らかに対する応答なのかもしれない。

倫理学に励む気持ちなどが典型的だ。現実世界で自分が直面あるいは見聞きした倫理的理不尽への抵抗が、そのエネルギー源になったりする。

僕の思考は、そんな捻じ曲がった切実さを駆動力としていたんだと、最近になって気付かされた。

しかしながら、思考を回さなくても幸せに生きられるようになったというのは、考え事に暮れて必死に境遇を受容していた頃の自分がまさに求めていたものだ。ただ、なんというか、満足な豚になることをよしとしている自分がいて、旧友に見放されたような寂しさがある。次に不幸が訪れるまでの間、この形容し難い感覚と付き合っていくんだろう。

Nobles Obligeと偶然性

 Nobles Obligeというのは素敵な倫理だなあと思う。これは端的にいえば、強者が弱者に施すべきだという原理だ。メリトクラシーの荒波に揉まれる今日この頃、このNobles Obligeの倫理をより原理から考えていきたい。


 僕がNobles Obligeを支持するのは、偶然性を日常的に感じているからだ。


 成功者が成功者であることができたのには、幾分かの偶然性が必ず寄与している。具体的には、生まれた家がもつ富や文化資本、特定の才能、そして、その能力が認められる社会にたまたま誕生できたこと、などだ。この世に成功というものがあるとして、そこに偶然性が多大に絡むことには疑いの余地がない。


 そして、それら偶然的なもの(coincidence)によって金銭などの意味で利益を得ることができるのには、十分な「試行回数」が必要である。
ここでいう「試行回数」とは、この世界に沢山の人間が生まれているということである。すなわち、ある人が利益を上げているとき、その人は利益を得ていない全ての人間に対して借りがある状態だといえる。


 その借りを返すべし、という倫理こそがNoblesse Obligeだと僕は思う。


 もちろん政府が貧富や特性などの偶然性による格差を埋めるべく活動しているけれど、それだけに頼ることはできない。なぜなら、支援される者と支援されないものとの境界で常に問題が発生するからだ。政府のような厳密な存在はあらゆる種の不遇さを漏れなく想定することはできないし、必要な施しを精密に見定めることもできない。


 では、Noblesse Obligeの倫理に従えば、恵まれない者への施しが正確に行われるのだろうか?


 それもおそらくNoだろう。重要なのは、その点を曖昧にしてしまうことだ。
 富める者が他者の様々な恵まれなさを感じ取り、そこに都度施しを行うことで、引き換えに道徳的利益を得る。この営みが社会に根付くことにより、「恵まれること」と「恵まれないこと」とを貫く基軸が多元化し、利益の概念が交錯する。こうして全てを有耶無耶にすることが、絶対的な不遇さの解消につながる気がしている。


 すごく曖昧な展望だが、これからの世の中はこういう徳によって良くなってほしいなあと思う。

ファイル暗号化ソフトを自作してみた

 ファイルをパスワードで暗号化するデスクトップアプリを作ってみた。画面はこんな感じ。


 ソフト本体(JARファイル)は以下で公開したよ。Macで作成したけど、JARなのでOSに依存せず動作するはず。
github.com

暗号化アルゴリズム

  1. 入力したパスワードをSHA-1でハッシュ化する
  2. ファイルをbyte列として読み込む
  3. パスワードハッシュとファイル内容を1 byteずつ突合し、XOR演算をする
  4. 新しいファイルとして出力する

ソース

 アプリの核となる暗号化処理をする部分のソースはこんな感じ。BufferedStreamを使った方が処理は速くなると思うけど、ロジックがややこしくなるので使わなかった。

// 本処理
byte[] pw_hash = sha1(pw);
int index = 0;
FileInputStream fis = new FileInputStream(inputFile);
FileOutputStream fos = new FileOutputStream(outputFile);
byte[] data = new byte[1];
// ファイルから1バイトずつ読み込み、ハッシュとXOR演算する。
while ((fis.read(data, 0, 1)) != -1) {
	data[0] = (byte) (data[0] ^ pw_hash[index]);
	fos.write(data, 0, 1);
	// 演算用バイト列の中身は一回ごとにクリアする。
	data[0] = 0;
	// ハッシュ値の項目番号は周期20でリセットする。
	switch (index) {
	case 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18:
		index++;
		break;
	case 19:
		index = 0;
		break;
	}
}
fis.close();
fos.close();

// パスワードからSHA-1のハッシュ値を算出するメソッド
private static byte[] sha1(String pw) throws NoSuchAlgorithmException {
	MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
	byte[] result = sha1.digest(pw.getBytes());
	return result;
}

セキュリティについて

 暗号化セキュリティはそこまで強固ではないと思う。
 共通鍵暗号だと見たとき、セキュリティに関してはハッシュ化アルゴリズムSHA-1の強度に依存していることになる。その点はSHA-256などにすれば多少マシにはなるだろう。
 弱点になりそうなのは暗号の周期性だ。パスワードから生成された20 byteのハッシュをそのままXOR演算に使用しているため、ファイル内容に対して20 byteおきに同じ演算がなされることになる。それがファイル内容の周期性と噛み合ってしまったら、暗号として意味をなさなくなるかもしれない。
 色々な暗号化システムで採用されている通り、やはり並び替え処理は必要なのだと思う。

まとめ

 楽しかった。byteレベルでファイルを操作できることがわかったので、今後はそういう処理を使って便利な機能を有する何かを作りたい。面倒臭くない範囲で。

自由意志って何?

 自由意志とは、人間が自発的に行為できる能力のことだ。自由意志が存在するのか否かという問題は、人に行為責任を負わせるときの合理性を支えるからというのもあって、重要な哲学的問題のひとつとされている。

 この掴みどころのない概念について、今のところの自分の意見をまとめた。

両立論

 自由意志をどんなものだと考えるかについての立場は、まず大きく二分される。両立論(決定論と両立するという立場)と非両立論(決定論と両立しないという立場)だ。

 これらは、すでに意味内容が確立された自由意志なるものがどんな属性をもつか、についてではなく、自由意志というのをどう定義するのかという決定についての二つの立場だ。したがって、説Aと説Bとの間の排他的論争とは異なり、自由意志についての異なる言明として互いに両立しうる。

 もっとも、僕はかなり経験論に傾倒しているので決定論をナンセンスだと思っている。なので厳密には両立論者ではないのだが、一般には両立論に含まれる立場をとっていると思う。

 

自己制御感

 非両立論の立場をとるひとは、要するに「物理世界の将来は決まっているんだから、我々は自由でない!」みたいなことを言っているわけだ。言いたいことはわかる。因果の鎖が物理世界の中だけで閉じているのだとしたら、我々の心とか精神が「自由による因果性」を追加するすべはない。

 しかし、仮に決定論を受け入れたとしても、「そうは言っても我々は自由に行動できるじゃん?」という主張ができる。

 そういう、自己制御感とでもいうべき感覚を頼りに、両立論を推したい。未来が決まっていても自由な選択ができる、という感じ。

 感覚だけで主張をするのも格好悪いので、もうすこし理論的に整理したい。

 

経験論的なアプローチ

 記事の冒頭で、自由意志は人に行為責任を負わせる合理性に関係するので大事だと言った。ということは、後者から前者の輪郭を掴もうというアプローチができる。

 わかりにくいので言い換える。「自由意志」という言葉はちょっと抽象的で捉えどころが難しい。そこで、「自由意志があると、自分が行為責任を負わされることが理不尽なことではなくなる」という、自由意志がもたらす実際的な効果に着目する。「行為責任を負わされることが理不尽ではなくなる」という性質が、自由意志が何たるかを規定している、と捉えられる。したがって、「行為責任を負わされることが理不尽ではなくなる」条件が、そのまま自由意志の成立条件として挙げられる。

 それは以下のふたつではないかと思う。

  1. 自分の身体が行為Aをしたという記憶
  2. 自分が行為Aを意図したという記憶

 これらふたつを照合することによって反省的に確証されるのが「自由意志」なのだと思っている。

 

締め

 おわり。形而上学的な概念を解体するのは結構好きだ。またもう一歩考えが進んだら記事として残そうと思う。

 

決定論とその周辺の話

 決定論まわりの議論についての今の見解をメモとして残したいなと思って書いた。自分の中でだいたい結論は出ているものの、他者に説明するときに上手く言葉が出てこなくて困ることが多いからだ。

決定論の主張

 まず、決定論の主張内容を確認したい。日本大百科全書の「決定論」の項目には、下のように書いてあった。

世の中でどういうことが起こるかは、未来永劫にわたってすべてあらかじめ決定されている、と主張する立場。

 要するに、決定論の意義と真偽を考えるためには、この「あらかじめ決定されている」が何を意味するのかを考えればいいわけだ。

 

実証学的プラグマティズム

 決定論の主張を分析するにあたって、実証学的プラグマティズムの理論を基軸にして考えたい。これは、一言でいえば「観念の意味は、それがもたらす具体的な結果にある」という主張だ。僕がいつも無意識に使ってしまってる主張のひとつなので、ここで明示しておく。

 これを採用することによって、議論が過度に抽象化されるのを防ぐことができる。思惟経済にかなう、なんて言い方もする。

 次の節では、これに基づいて決定論の主張を見てみる。

 

出来事の記録

 結論から言ってしまえば、「あらかじめ決定されている」という主張は無内容だ。

 一般に、何かの出来事の発生が「あらかじめ決定されている」というのは、未来にその出来事が発生することが、現在どこかの装置に記録されているということを指す。「装置」というのは、例えば、メモ帳、電子媒体、記憶などのことだ。

 ここでさらにプラグマティックに分析する。「記録されている」ということは、それが読み取り可能であるということでもある。解読できない文章をメモとは呼ばないし、絶対に思い出せないものを記憶とは呼ばない。再生可能性は記録の定義だ。

 しかしながら、未来の出来事は読み取り可能な形で記録されてはいない。すなわち、決定論の言う「決定されている」は、プラグマティックな「決定されている」の条件を満たさない。したがって、決定論は誤りないしナンセンスだということができる。

 

もっとそもそも

 そもそも、決定論問題の是非をプラグマティックに問いたいのなら、「決定論が成り立った場合と成り立たなかった場合とで、我々の実際的な経験はどのように異なるか」を考えればいい。

 もちろん、両者では何も異ならない。決定論と非決定論のどっちを採用したとしても、我々の経験を説明できるからだ。そう考えると、決定論の真偽を問うこと自体が、経験論的にナンセンスだといえる。

 

量子力学決定論

 決定論を否定する意見として、量子力学を援用するものがある。粒子Aが地点Xに見出される出来事は確率的にしか議論できないものだから、決定論は誤りだ、という主張だ。

 これはなかなかもっともらしいが、形而上学的な主張の前にはちょっと弱いと思う。

 決定論者の立場からすれば、「粒子Aが地点Xに見出されることを我々は確率的にしか予測できないものの、粒子Aが地点Xに見出されることは別途あらかじめ決定されていた」と反論すればいいだけだからだ。サイコロの出目と同様に、「発生確率についてしか議論ができないのは我々の無知ゆえである」という反論をされてしまうと、言い返しにくい。

 そもそもシュレディンガー方程式は経験則だし、コペンハーゲン解釈は現実の解釈のひとつにすぎない。そのままの形では、こういう形而上学的な主張の打破には向かないと思う。

 

運命論と決定論

 決定論と近しい主張として運命論がある。運命論は、未来が「あらかじめ決定されている」のだという主張において決定論と似ているが、決定論ほど強固な言明ではない(「運命は変えられる」とされる場合がある)。また、単なる決定論的方向性に加えてロマンティシズムのような側面を持つ(「まさに運命」などのニュアンスによく表れている)。

 そう考えると、「運命はあるか?」という議論は全く違った形をとりそうだ。これの説明には現象学あたりが役立つのではないだろうか。面白そう。