ラベル Julia の投稿を表示しています。 すべての投稿を表示
ラベル Julia の投稿を表示しています。 すべての投稿を表示

2025年9月21日日曜日

9月20日(2)

Juliaの日時処理からの続き

5年前の国勢調査の記事がないかと調べていたら,それはなくて5年前の自分の誕生日の記事が発掘された。誕生日の曜日を調べるJuliaプログラムの話だ。5年も経っているからもう一度実行してみようと,Jupyterではすぐ動いたが,コマンドラインでどうするのか迷った。

プログラムを実行形式にchmodして,頭に#!/usr/bin/env julia(あるいはjuliaの絶対パス)を書けばいいだけなのだけれど,!に気がつかず,シェルスクリプトのコメントと解釈されてエラーがでた。しかし耄碌しているのですぐに原因に気付けずChatGPT-5にたよることになる。

そんなこんなで,過去の習作コードをChatGPT-5に投げてバイブコーディングして得られたのが次の結果とコードである。プログラミングとはAIとの対話のことだった。

 koshi@mba2020 ~ % ./birth.jl

【基礎情報】

今日                : 2025年9月21日(日曜日)

誕生日              : 1953年9月20日(日曜日)


【差分と年齢】

生誕からの経過日数 : 26299日

満年齢              : 72歳


【年ごとの誕生日一覧】(*は日曜)  ※最終年:2025年

1953年9月20日(日曜日)  0歳  *

1954年9月20日(月曜日)  1歳  

1955年9月20日(火曜日)  2歳  

1956年9月20日(木曜日)  3歳  

1957年9月20日(金曜日)  4歳  

1958年9月20日(土曜日)  5歳  

1959年9月20日(日曜日)  6歳  *

1960年9月20日(火曜日)  7歳  

1961年9月20日(水曜日)  8歳  

1962年9月20日(木曜日)  9歳  

1963年9月20日(金曜日)  10歳  

1964年9月20日(日曜日)  11歳  *

1965年9月20日(月曜日)  12歳  

1966年9月20日(火曜日)  13歳  

1967年9月20日(水曜日)  14歳  

1968年9月20日(金曜日)  15歳  

1969年9月20日(土曜日)  16歳  

1970年9月20日(日曜日)  17歳  *

1971年9月20日(月曜日)  18歳  

1972年9月20日(水曜日)  19歳  

1973年9月20日(木曜日)  20歳  

1974年9月20日(金曜日)  21歳  

1975年9月20日(土曜日)  22歳  

1976年9月20日(月曜日)  23歳  

1977年9月20日(火曜日)  24歳  

1978年9月20日(水曜日)  25歳  

1979年9月20日(木曜日)  26歳  

1980年9月20日(土曜日)  27歳  

1981年9月20日(日曜日)  28歳  *

1982年9月20日(月曜日)  29歳  

1983年9月20日(火曜日)  30歳  

1984年9月20日(木曜日)  31歳  

1985年9月20日(金曜日)  32歳  

1986年9月20日(土曜日)  33歳  

1987年9月20日(日曜日)  34歳  *

1988年9月20日(火曜日)  35歳  

1989年9月20日(水曜日)  36歳  

1990年9月20日(木曜日)  37歳  

1991年9月20日(金曜日)  38歳  

1992年9月20日(日曜日)  39歳  *

1993年9月20日(月曜日)  40歳  

1994年9月20日(火曜日)  41歳  

1995年9月20日(水曜日)  42歳  

1996年9月20日(金曜日)  43歳  

1997年9月20日(土曜日)  44歳  

1998年9月20日(日曜日)  45歳  *

1999年9月20日(月曜日)  46歳  

2000年9月20日(水曜日)  47歳  

2001年9月20日(木曜日)  48歳  

2002年9月20日(金曜日)  49歳  

2003年9月20日(土曜日)  50歳  

2004年9月20日(月曜日)  51歳  

2005年9月20日(火曜日)  52歳  

2006年9月20日(水曜日)  53歳  

2007年9月20日(木曜日)  54歳  

2008年9月20日(土曜日)  55歳  

2009年9月20日(日曜日)  56歳  *

2010年9月20日(月曜日)  57歳  

2011年9月20日(火曜日)  58歳  

2012年9月20日(木曜日)  59歳  

2013年9月20日(金曜日)  60歳  

2014年9月20日(土曜日)  61歳  

2015年9月20日(日曜日)  62歳  *

2016年9月20日(火曜日)  63歳  

2017年9月20日(水曜日)  64歳  

2018年9月20日(木曜日)  65歳  

2019年9月20日(金曜日)  66歳  

2020年9月20日(日曜日)  67歳  *

2021年9月20日(月曜日)  68歳  

2022年9月20日(火曜日)  69歳  

2023年9月20日(水曜日)  70歳  

2024年9月20日(金曜日)  71歳  

2025年9月20日(土曜日)  72歳    

ChatGPT-5 Thinking が提案してきたコードは以下のとおりである。かなり修正してもらった。

#!/usr/bin/env julia

# birth.jl


using Dates


# --- ヘルパ ---

const WDAY_SHORT = ["月","火","水","木","金","土","日"]

const WDAY_LONG  = ["月曜日","火曜日","水曜日","木曜日","金曜日","土曜日","日曜日"]


fmt_date_ja(d::Date; long_wday::Bool=true) = string(

    Dates.format(d, dateformat"yyyy年m月d日"), "(",

    (long_wday ? WDAY_LONG : WDAY_SHORT)[dayofweek(d)], ")"

)


# 引数: [YYYY MM DD [ENDYEAR]]

function parse_args()

    if length(ARGS) == 0

        return Date(1953, 9, 20), year(Dates.today())

    elseif length(ARGS) == 3 || length(ARGS) == 4

        b = Date(parse(Int, ARGS[1]), parse(Int, ARGS[2]), parse(Int, ARGS[3]))

        lasty = (length(ARGS) == 4) ? parse(Int, ARGS[4]) : year(Dates.today())

        return b, lasty

    else

        error("使い方: ./birth.jl  または  ./birth.jl YYYY M D  [ENDYEAR]")

    end

end


function list_birthdays(birth::Date, last_year::Int)

    last_year = max(last_year, year(birth))  # 下限は誕生年

    for y in year(birth):last_year

        d = Date(y, month(birth), day(birth))

        age = y - year(birth)

        star = (dayofweek(d) == 7) ? "*" : ""   # 7 = 日曜

        println(fmt_date_ja(d; long_wday=true), "  ", age, "歳  ", star)

    end

end


# --- 本体 ---

birthday, list_until = parse_args()

today = Dates.today()


println("【基礎情報】")

println("今日                : ", fmt_date_ja(today; long_wday=true))

println("誕生日              : ", fmt_date_ja(birthday; long_wday=true))


println("\n【差分と年齢】")

days_elapsed = Dates.value(today - birthday)

age_years = year(today) - year(birthday) - (Date(year(today), month(birthday), day(birthday)) > today ? 1 : 0)

println("生誕からの経過日数 : ", days_elapsed, "日")

println("満年齢              : ", age_years, "歳")


println("\n【年ごとの誕生日一覧】(*は日曜)  ※最終年:", list_until, "年")

list_birthdays(birthday, list_until)

2024年10月31日木曜日

衆院選(2024)の結果

参院選(2022)の結果からの続き

三春充希の第50回衆院選情報部によれば,今回の第50回衆議院議員総選挙の比例区では次のような結果が得られている。前49回との絶対得票数を比較したものである。
保守  なし  → 114万  +114万
参政  なし  → 187万  +187万
自民  1991万 → 1458万       -533万
国民  259万    → 616万    +357万
公明  711万  → 596万    -115万
維新  805万  → 509万      -296万
立憲  1149万 → 1155万       +6万
れいわ 221万    → 380万    +159万
共産  416万    → 336万      -80万
社民  101万   → 93万     -8万
みんな 80万   → 2万    -78万
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
合計 5733万 → 5446万  -287万
一方,Perplexityに参院選(2022)の結果のデータを与えて,これと同様のものを生成するよう指示したところ,次の結果(左総議席,右比例区)が得られた。まあまあである。
投票率 55.93%→53.85% (-2.08%)
議席数 総数 465→465 (unchanged) 過半数 233
保守  0 → 3 (+3)    保守  データなし
参政  1 → 3 (+2)    参政  データなし
自民 247 →191 (-56)  自民  26.73% (1,458万票 = 59議席)
公明  32 → 24 (-8)   公明  10.93% (   596万票 = 20議席)
維新  44 → 38 (-6)   維新  9.36%   (   511万票 = 15議席)
国民  7 → 28 (+21)   国民  11.32% (   617万票 = 17議席)
立憲  98 →148 (+50)  立憲  21.20%  (1,156万票 = 44議席)
れいわ 3 → 9 (+6)   れいわ   6.98%  (   381万票 =  9議席)
共産  10 → 8 (-2)     共産    6.16%  (   336万票 =  7議席)
社民  1 → 1 (±0)    社民  データなし
無所属 22 → 12 (-10)

注目すべき点:
1.自民党と公明党の連立与党は過半数を失い、215議席にとどまった。
2.立憲民主党が大幅に議席を増やし、148議席を獲得した。
3.国民民主党も大きく躍進し、7議席から28議席に増加した。
4.れいわ新選組が議席を3倍に増やし、9議席を獲得した。
5.自民党の得票率は前回より低下し、小選挙区での絶対得票率は20.1%と、1996年以降で最低を記録した。
6.女性当選者数は過去最多の73人となり、衆議院議員の16%を占めた。
7.投票率は53.85%で、前回より約2ポイント低下し、戦後3番目に低い水準となった。
絶対得票数では,自公維の減少分-944万票と公共の減少分-195万票から,全体の減少分を除いた-852万票分を,国民が+357万票,れいわが+159万票の計+516万票,保守と参政の計で+301万票と分けあった計算になっている。立憲はわずか6万票増なので,有権者の票が立憲に向かったわけではなく,自民と維新の減少のあおりで小選挙区の当選数が増えたにすぎない


比例区の世代別の投票率を朝日新聞の出口調査のデータから Julia で図示してみた。ヒストグラムの各世代別の領域の高さは人口に比例させている。グレイゾーンは政党に投票しなかったものであり,左右均等に振り分けたが,あわせてほぼ " 1 - 投票率 "となる。

図:衆議院選挙比例区年代・政党別投票率

また,小選挙区における絶対得票数は次のようなものである(Wikipediaから)。
保守           →   16万( 0.3%) + 16万
参政           →  136万( 2.5%) +136万
自民  2763万(48.1%)  → 2087万(38.5%)  - 676万
公明    87万( 1.5$)   →   73万( 1.4%)   - 14万
維新   480万( 8.4%) →  605万(11.2%) +125万
国民   125万( 2.2%) →  235万( 4.3%) +110万
立憲  1722万(30.0%)  → 1574万(29.0%)  -148万
れいわ   25万( 0.4%) →   43万(  0.8%)  + 18万
共産   263万( 4.6%) →  370万( 6.8%) +107万
社民    31万( 0.6%) →   28万( 0.5%)   -  3万
もちろん,擁立候補者数に依存するのだけれど,立憲はここでも票を減らしている。

[1]第49回衆議院議員総選挙全国意識調査(公益財団法人 明るい選挙推進協会)


using CairoMakie

# データの準備
# populations = [93, 457, 608, 874, 1102, 1038, 1168, 595]
populations = [219, 1277, 1317, 1637, 1828, 1484, 1608, 1290]
total_population = sum(populations)

support_rates = [
    [0.294, 0.008, 0.017, 0.110, 0.025, 0.034, 0.080, 0.072, 0.038, 0.021, 0.008, 0.294],  # 10代
    [0.321, 0.007, 0.018, 0.072, 0.021, 0.036, 0.093, 0.054, 0.036, 0.018, 0.004, 0.321],  # 20代
    [0.272, 0.009, 0.023, 0.097, 0.028, 0.055, 0.097, 0.069, 0.051, 0.023, 0.005, 0.272],  # 30代
    [0.236, 0.011, 0.027, 0.128, 0.037, 0.064, 0.075, 0.096, 0.064, 0.021, 0.005, 0.236],  # 40代
    [0.205, 0.018, 0.030, 0.151, 0.042, 0.060, 0.060, 0.133, 0.060, 0.030, 0.006, 0.205],  # 50代
    [0.161, 0.014, 0.028, 0.182, 0.070, 0.056, 0.049, 0.182, 0.042, 0.042, 0.014, 0.161],  # 60代
    [0.148, 0.015, 0.015, 0.218, 0.065, 0.051, 0.036, 0.211, 0.022, 0.058, 0.015, 0.148],  # 70代
    [0.285, 0.005, 0.005, 0.171, 0.042, 0.028, 0.014, 0.115, 0.005, 0.037, 0.009, 0.285]   # 80代
]

colors = ["#AAAAAA", "#777777", "#FC9F05", "#FF0000", "#F9EA09", "#00FF00", "#2242D2", "#06EAFC", "#FA69FF", "#DD0000",  "#777777", "#AAAAAA"]
age_labels = ["18-", "20-", "30-", "40-", "50-", "60-", "70-", "80-"]

# プロットの作成
fig = Figure(size=(800, 600))
ax = Axis(fig[1, 1], ylabel="人口割合", xlabel="投票率")

current_y = 0.0
for (i, pop) in enumerate(populations)
    segment_height = pop / total_population
    rates = support_rates[i]
    
    current_x = 0.0
    for (j, rate) in enumerate(rates)
        poly!(ax, [current_x, current_x+rate, current_x+rate, current_x], 
              [current_y, current_y, current_y+segment_height, current_y+segment_height], 
              color=colors[j])
        current_x += rate
    end
    
    # 年代ラベルを追加
    text!(ax, -0.05, current_y + segment_height/2, text=age_labels[i], 
          align=(:right, :center), fontsize=14)
    
    current_y += segment_height
end

# 軸の設定
ax.yticks = (collect(0:0.2:1), ["0%", "20%", "40%", "60%", "80%", "100%"])
ax.xticks = (collect(0:0.2:1), ["0%", "20%", "40%", "60%", "80%", "100%"])

# 凡例の追加
elements = [PolyElement(polycolor = c) for c in colors]
Legend(fig[1, 2], elements, ["--", "保守", "参政", "自民", "公明", "維新", "国民","立憲", "れいわ", "共産", "社民", "--"], 
       "政党", tellheight=false, tellwidth=true)

# タイトルの追加
Label(fig[0, :], "年代別投票率", fontsize = 24)

# グラフの表示
display(fig)


2024年9月22日日曜日

OpenAI o1(4)

OpenAI o1(3)からの続き

引き続き,o1 のプログラミング能力について検証してみよう。数学体感教室2024の問題3を一般化したものについて,o1-mini に Julia プログラムを作成させた。1回目は関数定義が重複するエラーが出ていたので,そのエラーコードを読ませると,2回目には正しい答えが得られた。
Q(koshix):
$a_i \ (i=1 ... n) $が正の整数であり,$n$個の$a_i$の組 $(a_1, a_2,  ..., a_n)$ 最大公約数は1であるとする。
$a_1/a_2=k_1,\  2*a_2/a_3=k_2, \ 3*a_3/a_4=k_3,\  ... (n-1)*a_{n-1}/a_n \\ =k_{n-1},  n*a_n/a_1 = k_n $において,$(k_1, k_2, ... k_n)$ は整数となって,$k_1*k_2*k_3*...*k_{n-1}*k_n = n!$ が成り立つ。
$n$を与えたときに,上記の条件を満足する$(k_1, k_2, ..., k_n)$ の組の数 $b_n$ を求める Julia プログラムを作成せよ。
このプログラムを用いて,$ b_1, b_2, ..., b_8$ の値を計算せよ。

A(OpenAI o1-mini 思考時間29秒):
以下に、与えられた条件を満たす $(k_1, k_2, ..., k_n)$の組の数 $b_n$ を計算する Julia プログラムを示します。このプログラムは、各 $n$に対して $n!$ の素因数分解を行い、各素数の指数を用いて組み合わせの数を計算します。

アプローチ
1.素数の列挙:各 $n$ に対して,$n!$ の素因数分解に必要な素数を列挙します。
2.指数の計算:各素数 $p$ について,$n!$ 中の $p$ の指数 $e_p$ を計算します。
これは,$\lfloor \dfrac{n}{p} \rfloor + \lfloor \dfrac{n}{p^2} \rfloor +\lfloor \dfrac{n}{p^3} \rfloor +\cdots $で求められます・
3.組み合わせの計算:各素数 $p$ に対して,$\binom{n+e_p-1}{e_p}$を計算します。これにいおり,$e_p$ 個の指数を $k_i$ に分配する方法の数が求まります。
4.総数の計算:すべての素数についての組み合わせの数を掛け合わせることで、総数 $b_n$ を得ます。

Julia プログラム(修正版)
以下のJuliaプログラムは、上記のアプローチに基づいて $b_n$ を計算します。

# Primes.jl パッケージのインポート
import Primes: primes

# n! における素数 p の指数を計算する関数
function exponent_in_factorial(n::Int, p::Int)::Int
    exponent = 0
    power = p
    while power <= n
        exponent += div(n, power)
        power *= p
    end
    return exponent
end

# b_n を計算する関数
function calculate_b(n::Int)::Int
    if n == 0
        return 1
    end
    
    # n >= 2 の場合のみ素数を取得
    if n >= 2
        primes_list = Primes.primes(2, n)
    else
        primes_list = Int[]  # 空の配列
    end
    
    total = 1
    for p in primes_list
        e_p = exponent_in_factorial(n, p)
        # 組み合わせの計算
        total *= binomial(n + e_p - 1, e_p)
    end
    return total
end

# b_1 から b_8 までを計算して表示
for n in 1:8
    bn = calculate_b(n)
    println("b_$n = $bn")
end

プログラムの説明
・exponent_in_factorial 関数:与えられた $n$ と素数 $p$ に対して,$n!$ の中の $p$ の指数を計算します。
・calculate_b 関数:与えられた $n$ に対して,$b_n$ を計算します。まず,$n$ 以下の素数を取得し、各素数についての組み合わせの数を計算して掛け合わせます。
・メインループ: $n=1$ から $n=8$ までの $b_n$ を計算し,結果を表示します。

実行結果
上記のプログラムを実行すると、以下のように $b_1$ から $b_8$ までの値が得られます。
$b_1 = 1$
$b_2 = 2$
$b_3 = 9$
$b_4 = 80$
$b_5 = 875$
$b_6 = 15876$
$b_7 = 288120$
$b_8 = 7907328$
これにより、各 $n$ に対して条件を満たす $(k_1, k_2, ..., k_n)$ の組の数 $b_n$ を効率的に計算することができます。
このプログラムは,Mathematicaだとより簡潔に表現できる。これは,もとの関数を2つ与えた上で,ChatGPT-4oに作ってもらった。エラーが出ていたが,3回目の修正で正しい答えに到達した。
f[n_] := FactorInteger[n!]
g[n_, k_] := Binomial[n + k - 1, k]
h[n_] := 
 Module[{factors = f[n], product = 1}, 
  Do[product *= g[n, pair[[2]]], {pair, factors}];
  product]
a = Table[h[k], {k, 1, 8}]
ListPlot[Log[a]] 
{1, 2, 9, 80, 875, 15876, 288120, 7907328} 

図:n=1-8 に対するh[n] の対数のListPlot(Mathematica)