複素関数の挙動について

概要

(森 and 杉原 2003) の 第3章の複素変数の初等関数のプロットを実際にやってみる。

複素関数のプロット

import numpy as np
import matplotlib.pyplot as plt

def plot(in_curves, func):
    f, (ax1, ax2) = plt.subplots(1, 2)
    ax1.axis('equal')
    ax2.axis('equal')
    for crv in in_curves:
        ax1.plot(crv.real, crv.imag)
        y = func(crv)
        ax2.plot(y.real, y.imag)

in_curves = []
N = 10
for i in range(N):
    crv = np.linspace(-2, 2, 100)  + 1j * (-3*np.pi + i * (3*np.pi-(-3*np.pi)) / N)
    in_curves.append(crv)
for i in range(N):
    crv = -2 + i * (2 - (-2)) / N + 1j * np.linspace(-3*np.pi, 3*np.pi, 100)
    in_curves.append(crv)
print(in_curves)
plt.cla()
plot(in_curves, np.exp)
plt.savefig(ofile)
ofile
ed9ktsxznLcGi.png

参考文献

森 正武, and 杉原 正顯. 2003. 複素関数論. 単行本. 岩波書店. https://lead.to/amazon/jp/?op=bt&la=ja&key=4000059505.

ironcladでハッシュ関数MD5を使ってみる

概要

sharplispers/ironclad: A cryptographic toolkit written in Common Lisp でハッシュ関数MD5を使ってみた。

コード

quicklispironclad をインストールする。

(ql:quickload "ironclad")

文字列をバイト配列に変換する関数とバイト配列を16進数表記にする関数を定義する。

(defun plain2bytes (message)
  (sb-ext:string-to-octets message  :external-format :utf-8))

(defun bytes2hexstr (bytes)
  (reduce (lambda (x y) (format nil "~x~x" x y)) bytes :initial-value ""))

ironclad の doc/ironclad.html を参考ハッシュ関数MD5を呼び出す。

(let* ((message "hello!"))
  (bytes2hexstr (ironclad:digest-sequence :md5 (plain2bytes message))))
5A8DD3AD756A93DED72B823B19DD877

検証

RFC 1321 – The MD5 Message-Digest Algorithm の”A.5 Test suite”を参照して、正しく呼び出せているか確認する。

MD5 (“message digest”) = f96b697d7cb7938d525a2f31aaf161d0

(let* ((message "message digest"))
  (bytes2hexstr (ironclad:digest-sequence :md5 (plain2bytes message))))
F96B697D7CB7938D525A2F31AAF161D0

参考文献

ミュンヒハウゼン数

概要

(数学セミナー編集部 2024) でミュンヒハウゼン数を知ったので計算してみた。

コード

import itertools
import functools

@lru_cache(maxsize=1000)
def _calc(i, ind):
    return i * (10**ind)

def Munchhausen(n_digits=4):
    ret = []

    values = [(i, i**i) for i in range(1, 10)]
    for x in itertools.product(*[values]*n_digits):
        a = 0
        b = 0
        for ind, (i, iv) in enumerate(x):
            a += _calc(i, ind)
            b += iv
        if a == b:
            ret.append(a)
    return ret

for i in range(1, 7):
    print(i, Munchhausen(i))
1 [1]
2 []
3 []
4 [3435]
5 []
6 []

参考文献

数学セミナー編集部, ed. 2024. 数学セミナー2024年4月号 通巻750号【特集】数学とのつきあい方2024. Kindle版. 日本評論社. https://lead.to/amazon/jp/?op=bt&la=ja&key=B0CWDVX4KN.

GMP_ECMによる素因数分解

概要

(岩波書店『科学』編集部 2024) にあった”RSAと素因数分解”に GMP-ECM が紹介されていたので、実際に動かしてみた。

GMP-ECMの準備

ZIMMERMANN Paul / ecm · GitLab を利用した。

git clone https://gitlab.inria.fr/zimmerma/ecm.git

INSTALL.dev にある記載のように実行して、コンパイルした。

cd ecm
autoreconf -i
./configure
make -j4
make check

コンパイル後に実行ファイル ecm がフォルダに生成されている。

素数の生成

2つの素数を生成して、素因数分解のための合成数を用意する。

import sympy

N = 100
p1 = sympy.randprime(2**(N+1), 2**(N+2))
p2 = sympy.randprime(2**(N+1), 2**(N+2))

print(f'{p1=}')
print(f'{p2=}')
print(f'{p1*p2=}')
p1=2869069113607969685726929882709
p2=3871722605512470843352197003061 
p1*p2=11108239743933603608670116823884965417896735085597594043972249

2つの素数p1, p2から合成数: 11108239743933603608670116823884965417896735085597594043972249 を得た。

実行

GMP-ECM の使い方 の USAGE を参考にコマンドのオプションの引数を決定した。未知の素因数の桁数が30桁なので表から以下の値を採用した。

  • -c(反復): 500
  • B1: 250000

結果の最後部をみると、合成数が元の2つの素数に分解されていることが分る。

echo 11108239743933603608670116823884965417896735085597594043972249 | ./ecm -c 500 250000

GMP-ECM 7.0.6-dev [configured with GMP 6.3.0, --enable-asm-redc, --enable-assert] [ECM]
Input number is 11108239743933603608670116823884965417896735085597594043972249 (62 digits)
Using B1=250000, B2=128992510, polynomial Dickson(3), sigma=1:4010257250
Step 1 took 77ms
Step 2 took 70ms
Run 2 out of 500:
Using B1=250000, B2=128992510, polynomial Dickson(3), sigma=1:2451171661
Step 1 took 70ms
Step 2 took 69ms
Run 3 out of 500:
Using B1=250000, B2=128992510, polynomial Dickson(3), sigma=1:2557109986
Step 1 took 70ms
Step 2 took 69ms

...

Run 18 out of 500:
Using B1=250000, B2=128992510, polynomial Dickson(3), sigma=1:504643144
Step 1 took 70ms
Step 2 took 70ms
Run 19 out of 500:
Using B1=250000, B2=128992510, polynomial Dickson(3), sigma=1:2975852467
Step 1 took 70ms
Step 2 took 71ms
********** Factor found in step 2: 2869069113607969685726929882709
Found prime factor of 31 digits: 2869069113607969685726929882709
Prime cofactor 3871722605512470843352197003061 has 31 digits

参考文献

岩波書店『科学』編集部, ed. 2024. 科学2024年3月号[雑誌]. Kindle版. 岩波書店. https://lead.to/amazon/jp/?op=bt&la=ja&key=B0CW1DZS1W.

ラゲール法による多項式の求解

概要

(長田 2024) の第4章代数方程式:1ラゲール法をもとに実装する。以下のサイトも参考にした。

  1. ラゲール法による求根アルゴリズム~Pythonコード付き~ – LASCODE

方針

根 1, 2, 3, 4, 5 を持つ5次の多項式を解くことにする.

import numpy as np

p5deg = np.polynomial.polynomial.Polynomial.fromroots([1, 2, 3, 4, 5])
print(p5deg)
-120.0 + 274.0·x - 225.0·x² + 85.0·x³ - 15.0·x⁴ + 1.0·x⁵

実装

def laguerre(f, x0, delta=0.01, max_iter=100):
    ans = []

    f = f.copy()
    while 1 < f.degree():
        n = f.degree()
        df = f.deriv()
        ddf = df.deriv()

        x = x0
        iter = 0
        while not (np.abs(f(x)) < delta):
            numerator = n * f(x)
            denom_plus = df(x) + np.sqrt((n-1)**2 * df(x)**2 - n*(n-1)*f(x)*ddf(x))
            denom_minus = df(x) - np.sqrt((n-1)**2 * df(x)**2 - n*(n-1)*f(x)*ddf(x))

            denominator = denom_plus if np.abs(denom_minus) < np.abs(denom_plus) else denom_minus
            x -= numerator / denominator

            if np.abs(f(x)) < delta:
                break
            if max_iter < iter:
                break
            iter += 1

        ans.append(x)
        f = f // np.polynomial.polynomial.Polynomial.fromroots([x])

    if f.degree() == 1:
        ans.append(-f.coef[0])
    return ans

検証

5つの根の近似値が得られた。

roots = laguerre(p5deg, x0=0, delta=0.00001)
print(roots)
[1.0000000000000002, 1.9999999999999907, 3.0000000000000107, 4.000000000000017, 4.999999999999981]

参考文献

長田 直樹. 2024. 数値解析 非線形方程式と数値積分. 単行本. 現代数学社. https://lead.to/amazon/jp/?op=bt&la=ja&key=4768706266.