ラベル プログラミング の投稿を表示しています。 すべての投稿を表示
ラベル プログラミング の投稿を表示しています。 すべての投稿を表示

2025年2月13日木曜日

索引たんご(2)


前回,5.pyというローカルの単語パズルプログラムを OpenAI o3-mini-high に作成させた。これだと,本家のパズルゲームを解くことにはならない。そこで,一日一度だけトライできる「索引たんご」の解法支援プログラムも考えることにした。

Q(koshix)
カタカナ5文字の単語当てゲームが外部サイトにあります。
そのロジックは別添のプログラムのようになっています。

単語当ての推定解をユーザが外部サイトに入れると,現時点の最小値と
最大値のいずれかが推定解に置き換わって,正解から最小値や最大値までの
距離がパーセンテージ比率で与えられます。
それをみながらユーザは次の新たな推定解を外部サイトに入力するのです。

外部サイトのゲームが持っている辞書や正解は秘匿されていますが,
自分の手元にある同様の辞書(別添ブログラム参照)と
外部サイトのゲームが返答するパーセンテージを使って
正解を推定する支援プログラムを新たに自分の手元に作りたいと思います。
この支援プログラムには,推定解の5文字単語とパーセンテージを入力します。
それを受けて新たな正解の候補を返してくれるわけです。

ここまでで,何か質問はありますか。
(別添のプログラムとして前回の5.py 105行をここにおいた)

A(OpenAI o3-staff-high)
(24秒の推論の後)
以下、現状の理解と実装にあたって確認したい点をいくつか挙げさせていただきます。
【現状の理解】
(9行 省略)
【確認・質問事項】
(26行 省略)
以上の点についてご確認いただけますでしょうか?
これらの点を明確にさせていただくことで、より正確でご希望に沿った支援プログラムの設計案をご提案できると考えております。 
 
Q(koshix)
(1) 外部サイトの辞書は約12000語を持っていますが(29795語ではありません),その詳細はこちらには不明です。
最小値「アアアアア」,最大値「ンンンンン」が初期値として設定されおり,
正解との関係を判断してこれらの境界が更新されます。

(2) 外部サイトの下限から正解までの割合p1と上限から正解までの割合p2は
p1+p2=100%になるように設定されています。

(3) 外部サイトから得られる情報は,更新された「最小値」と更新された「最大値」と
これらから正解までの距離を表すパーセンテージになります。

(4) 支援プログラム(こちらの辞書は29795語)の初期値も,最小値「アアアアア」,最大値「ンンンンン」であり,内部情報としてこれらが更新され,対話的に使用します。
つまり支援プログラムの最小値や最大値は外部サイトのそれと常に一致しています。

(5) 正解は必ず最小値と最大値の範囲に入るように,推定値を入れていきます。
条件に当てはまらない場合は推定値の再入力が促されます(外部でも支援でも)。
したがって,最小値<正解<最大値を満たすので,パーセンテージは必ず計算できます。

(6) 候補はひとつだけ返してください。

(7) 支援プログラムが使うローカル辞書と外部サイトが使う外部辞書は異なっています。
それでも両者が相似関係にあるとして推定をすることになります。

さらに,質問はありませんか。

A(OpenAI o3-staff-high)
(21秒推論の後)
以下、現状の理解と、実装に向けた最終確認として追加の質問事項をまとめさせていただきます。
【現状の理解】
(20行 省略)
【追加で確認させていただきたい点】
(28行 省略)
以上、上記の点についてご確認いただければ、より正確な支援プログラムの設計・実装の提案が可能です。
何かご不明点や追加で伝えていただくべき事項があればお知らせください。


Q(koshix)
(1) 境界はローカル辞書の中に必ず存在します。
(2) p1の式はOK。内部プログラムでは小数点以下1桁までで十分,外部プログラムは小数点以下四捨五入した1-2桁の整数で%を返してきます。

A(OpenAI o3-staff-high)
(12秒推論の後)
以下、現時点での理解内容を整理するとともに、今後の実装方針について確認させていただきます。
【整理した前提・仕様】
(26行 省略)
【今後の実装方針について】
(16行 省略)
【確認事項】
(9行 省略)
上記内容で問題なければ、この前提に沿って支援プログラムの実装案をご提案させていただきます。
また、上記以外にご不明点や追加でご要望などございましたら、お知らせください。

Q(koshix)
入力インターフェイスはそれで結構です。コマンドラインでの応答。
その他の補正は不要です。それではプログラムをつくってください。

A(OpenAI o3-staff-high)
(46秒推論の後)
以下は、上記の前提条件と仕様に沿って作成した Python プログラムの例です。
(説明10行 省略)
(プログラム120行 省略)
プログラムの使い方
(15行 省略)
このプログラムは、コマンドラインで対話的に実行可能です。
ご不明点や追加のご要望があればお知らせください。

この支援プログラムを使って「索引たんご」を解いてみたところおおむねOKだったけれど,問題点が1つ発覚した。元の「索引たんご」の辞書は,ソートの段階では濁点や半濁点を除いたものとして扱っていた。これに対して前回説明したローカルの支援プログラムの辞書は,通常のソートルールに則っている。そこで,ローカルの辞書から「索引たんご」形式の辞書に変換するプログラムもOpenAI o3-mini-highに作らせた。プログラミングがはかどるわ。

Q(koshix)
次の形式のCSVファイル5.csvがある。
1,アアアアア,
2,アイアンズ,
3,アイアース,
・・・
29793,ンドンバシ,
29794,ンバラカダ,
29795,ンンンンン,

5文字のカタカナが五十音順にソートされて番号が付けられている。

ここで,濁点や半濁点の文字をそれを取り去った文字として扱って
ソートし直して付番した新しいCSVファイル55.csvを出力する
プログラムを作ってください。

例えば,「ガンシップ」は比較の際に「カンシッフ」としてソートして
付番するが,出力CSVファイル上は,「ガンシップ」のままである。

A(OpenAI o3-mini-high)
(8秒推論の後)
【解説・手順】
(14行 省略)
【コード例】

import csv
import unicodedata

def remove_diacritics(text):
    """
    文字列 text を Unicode 正規化 (NFD) し、
    濁点・半濁点(結合文字)を取り除いた文字列を返す。
    """
    # NFD正規化により合成文字を分解する
    normalized = unicodedata.normalize('NFD', text)
    # 結合文字(濁点や半濁点など)を除去して再結合する
    return ''.join(ch for ch in normalized if not unicodedata.combining(ch))

def main():
    input_filename = "5.csv"
    output_filename = "55.csv"

    # CSVの各行は [番号, カタカナ文字列, (その他)] と仮定
    rows = []
    with open(input_filename, newline="", encoding="utf-8") as csvfile:
        reader = csv.reader(csvfile)
        for row in reader:
            if len(row) < 2:
                continue  # 少なくとも番号と文字列がある行のみ対象
            # row[1] がカタカナの5文字であると仮定
            # ソートキーは、row[1] に対して濁点・半濁点を除去したもの
            key = remove_diacritics(row[1])
            rows.append((row, key))

    # ソート実行:変換後の文字列で五十音順に並べ替える
    # 注意:日本語の五十音順に近いソートとなるが、完全な辞書順ではない場合もあるので
    # 必要に応じて locale.strxfrm() などの利用も検討してください。
    sorted_rows = sorted(rows, key=lambda x: x[1])

    # 出力時に新しい番号を付ける
    with open(output_filename, mode="w", newline="", encoding="utf-8") as csvfile:
        writer = csv.writer(csvfile)
        for new_index, (orig_row, _) in enumerate(sorted_rows, start=1):
            # もとの行の1列目(番号)を新しい番号に置き換える
            # もとの行の他のフィールドはそのまま残す
            new_row = [str(new_index)] + orig_row[1:]
            writer.writerow(new_row)

if __name__ == "__main__":
    main()

【ポイント解説】
(14行 省略)

2025年2月12日水曜日

索引たんご(1)

単語あてと推論AIからの続き

一日に一回トライできるパズルゲームの索引たんご。12回のトライで正解を挟み込む必要があるのだけれど,これが中々に難しい。単語辞書中の正解の位置が左にパーセンテージで表示されているので,辞書があればより確実に推定できるはずだけれど,それは公開されていない。

しかたがないので,代替の辞書がないものかと探してみたところ,言語資源開発センター『現代日本語書き言葉均衡コーパス』短単位語彙表(Version 1.0)にたどり着いた。なんとなくそれらしい雰囲気が漂っている。Creative Commonsライセンス(CC BY-NC-ND)なので私達には使いやすい。

8MBのgzipファイルを解凍してできたTSVファイルをmacOSのNumbersに読み込ませて,品詞属性や統計データを取り除いて,正規表現で5文字だけのデータを選び出すと36785語が得られた。さらに,sortしてuniqをかけて同音異義語を排除すると29795語になる。ただし両端に「アアアアア」と「ンンンンン」を加えている(実際は,Excelがユニコードに弱いので慣れないNumbersでの作業を強いられたりして,結構面倒なクレンジングになった)。

これをOpenAI o3-mini-highにプログラムに依頼するのだけれど,そのロジックを考えるのに難儀する。こういうことは丁寧な段取りが重要なのである。一発で動くコードができたので,あとは2-3回微修正を入れるとスイスイとできあがった。ロジックの中身はブラックボックスでも構わないが,各動作の機能仕様やデータ構造は押さえる必要があって,今後のプログラミング教育の方向性は若干かなり変わってくるかもしれない。

Q(koshix):
1から29795まで五十音順に番号のついた5文字のカタカナを格納したファイル5.csvがあります。このプログラムがランダムに発生する1-29795の整数m0に対応するファイル内の 5文字のカタカナc0を当てるゲームです。正解のans=(m0,c0)とします。 最小の推定値min=(m1,c1)の初期値は(1,アアアアア)です。 最大の推定値max=(m2,c2)の初期値は(29795,ンンンンン)です。 ユーザはコマンドラインから5文字のカタカナ5文字cを入力します。 このようなコマンドラインで動作するプログラムを作ってください。

A(OpenAI o3-mini-high):
(28秒の推論の後)

以下は、Python を用いてコマンドライン上で動作するプログラムの例です。

このプログラムでは、まず CSV ファイル("5.csv")から 1~36787 番目の「5文字のカタカナ」を読み込み、

・辞書(word → 番号)およびリスト(番号 → word)の両方で管理します。
・ゲーム開始時に、ランダムに 1~29795 の整数 m₀ を選び、その番号に対応するカタカナ c₀ を正解とします。
・ゲームでは、初期の推定範囲を下限 (1, "アアアアア")、上限 (29795, "ンンンンン") とし、
ユーザが入力する 5文字のカタカナ(必ず CSV 内に存在するものとする)をもとに、
入力された単語の CSV 上の番号と正解の m₀ を比較します。
・もし入力の番号が m₀ より小さい場合は下限を更新し、
大きい場合は上限を更新します。
・ユーザの入力が正解に一致するまで繰り返します。

以下、コード例です。

import csv
import random

def load_katakana(file_path):
    """
    CSVファイル(例: "5.csv")から、番号と5文字カタカナの組を読み込み、
    番号→カタカナのリスト(インデックス番号は番号-1)と、
    カタカナ→番号の辞書を返す。
    CSVは各行が「番号,カタカナ」という形式である前提です。
    """
    total = 29795  # ファイルの行数
    words = [None] * total      # 番号 1~29795 に対応(リストのインデックスは 0~29794)
    katakana_to_index = {}
    
    with open(file_path, 'r', encoding='utf-8') as csvfile:
        reader = csv.reader(csvfile)
        for row in reader:
            if not row or len(row) < 2:
                continue
            try:
                idx = int(row[0])
            except ValueError:
                continue  # 番号が数値でない行はスキップ
            word = row[1].strip()
            if 1 <= idx <= total:
                words[idx - 1] = word
                katakana_to_index[word] = idx

    if any(w is None for w in words):
        raise ValueError("CSVファイルの読み込みに失敗しました。行数やデータ形式を確認してください。")
    
    return words, katakana_to_index

def print_range(lower_index, lower_word, upper_index, upper_word, target_index):
    """
    現在の推定範囲と、正解の位置を表すパーセンテージ (p1, p2) を表示する。
    p1: 下限から正解までの割合, p2: 正解から上限までの割合 (p1+p2=100)
    """
    range_span = upper_index - lower_index
    if range_span > 0:
        p1 = (target_index - lower_index) / range_span * 100
        p2 = 100 - p1
    else:
        p1, p2 = 0, 0
    print(f"現在の推定範囲: {lower_index}:{lower_word}  ~  {upper_index}:{upper_word}  (p1:{p1:.1f}%, p2:{p2:.1f}%)\n")

def main():
    file_path = "5.csv"
    
    try:
        words, katakana_to_index = load_katakana(file_path)
    except Exception as e:
        print(f"エラー: {e}")
        return

    # 初期の推定範囲の設定
    lower_index, lower_word = 1, "アアアアア"    # 最小の推定値
    upper_index, upper_word = 29795, "ンンンンン"  # 最大の推定値

    # 正解は、1~29795 の中からランダムに選ぶ(番号 m0 に対応する c0)
    target_index = random.randint(1, 29795)
    target_word = words[target_index - 1]
    
    attempts = 0
    print("=== カタカナ当てゲーム ===")
    print("CSVファイル内の5文字カタカナのうち、\n  番号 1~29795 に対応する単語を当ててください。")
    print_range(lower_index, lower_word, upper_index, upper_word, target_index)

    while True:
        # 試行回数を表示(例:1回目、2回目…)
        print(f"{attempts + 1}回目")
        guess = input("5文字のカタカナを入力してください: ").strip()
        attempts += 1
        
        if len(guess) != 5:
            print("入力エラー:5文字のカタカナを入力してください。\n")
            continue
        
        if guess not in katakana_to_index:
            print("入力されたカタカナはリストに存在しません。\n")
            continue
        
        guess_index = katakana_to_index[guess]
        
        if not (lower_index < guess_index < upper_index):
            print(f"入力された単語は現在の範囲外です。(現在の範囲:{lower_index}~{upper_index})\n")
            continue
        
        if guess_index == target_index:
            print(f"\n正解です!")
            print(f"{attempts}回目の試行で正解 (番号: {target_index}, カタカナ: {target_word}) に辿り着きました。")
            break
        elif guess_index < target_index:
            if guess_index > lower_index:
                lower_index, lower_word = guess_index, guess
            print("ヒント: 入力は小さすぎます。")
        else:  # guess_index > target_index
            if guess_index < upper_index:
                upper_index, upper_word = guess_index, guess
            print("ヒント: 入力は大きすぎます。")
        
        print_range(lower_index, lower_word, upper_index, upper_word, target_index)

if __name__ == "__main__":
    main()

プログラムのポイントと流れ

1.CSV 読み込み
・load_katakana 関数で CSV ファイルから全 29795 行を読み込み、
番号順にリスト words と、単語から番号を引く辞書 katakana_to_index を作成します。
2.正解の設定
・ランダムに 1~29795 の整数 target_index を選び、対応する単語 target_word を正解とします。
3.推定範囲の初期値
・下限は (1, "アアアアア")、上限は (29795, "ンンンンン") としておき、
ユーザの入力によって更新していきます。
4.ユーザ入力と比較
・ユーザはコマンドラインから 5文字のカタカナを入力します。
・入力された単語が CSV 内に存在し、かつ現在の推定範囲内であるかチェックします。
・単語に対応する番号を取り出し、正解の番号と比較します。
  *一致すればゲーム終了。
  *小さい場合は「ヒント:小さすぎる」と表示し、下限を更新。
  *大きい場合は「ヒント:大きすぎる」と表示し、上限を更新。
・現在の推定範囲を画面に表示し、正解するまで繰り返します。

この例は、インタラクティブな当てゲームとして動作します。実際にご利用になる際は、CSV ファイルの配置場所や文字コード(ここでは UTF-8 を想定)などをご確認ください。




図:本家の索引たんごの成功例(チートを利用した場合)
上記の辞書だけを用い,自力で位置を計算してやるとようやく正解にたどり着けた。

P. S.  上述の「『現代日本語書き言葉均衡コーパス』短単位語彙表」だけど,NumbersからExcelにファイル変換すればよかった。5文字以外も含めてすべてで18万5千語ある。このうち,36%が固有名詞,27%が漢語由来,21%が和語,12%が外来語だ。固有名詞の多くが外来語的なものになる。


2024年11月22日金曜日

置換可能素数

レピュニットからの続き

プログラミングの例題として次のようなことを考えた。「ある素数があって,その各桁を入れ換えてできる全ての数が素数になるものを考える。その最大の素数はどんなものだろうか。」

で,実際に自分でプログラムを書くかというと,それは面倒なので,いつものようにAIに頼ることにする。軟弱だ。脳細胞のフレイルだ。OpenAI o1とかPerlexity Proに頼ってみたが,正しい答えがでない。PythonをJuliaに書き換えても,さらにドツボにはまるだけ。

一度リセットしてから,再度挑戦した。

Q(koshix):
n桁の素数が与えられたとき,その各桁の数字を入れ替えてできるすべての数が再び素数になるようなもので,できるだけ大きな数を探したい。桁数nを与えたときに,この条件を満たす最大の数を求めるプログラムを作成してください。

A(Perplexity Pro):
この問題を解決するために、以下のようなプログラムを作成しました。このプログラムは、与えられた桁数nに対して、条件を満たす最大の素数を見つけます。

import itertools
from sympy import isprime

def find_large_permutational_prime(n):
    lower_bound = 10**(n-1)
    upper_bound = 10**n
    primes = [p for p in range(lower_bound, upper_bound) if isprime(p)]
    largest_permutational_prime = -1
    for prime in primes:
        permutations = set(int(''.join(p)) for p in itertools.permutations(str(prime)))
        if all(isprime(p) for p in permutations):
            largest_permutational_prime = max(largest_permutational_prime, prime)
    return largest_permutational_prime

そして,ChatGPT-4oでJuliaに変換したものが次の通りである。
using Primes
using Combinatorics

function find_large_permutational_prime(n)
    lower_bound = 10^(n - 1)
    upper_bound = 10^n - 1
    largest_permutational_prime = -1
    
    for prime in primes(lower_bound, upper_bound)
        perms = Set(parse(Int, join(p)) for p in Combinatorics.permutations(string(prime)))
        if all(isprime(p) for p in perms)
            largest_permutational_prime = max(largest_permutational_prime, prime)
        end
    end
    
    return largest_permutational_prime
end
これを実行すると,n=3で991が得られる。n=4,5,6では結果なしということになった。これから,nが4以上では条件を満たす素数が存在しないと証明できないかと考えたけれど,そんな簡単な話ではなかった。もちろん,現在の生成AIでは証明できない。

調べてみるとこのような素数は,置換可能素数として既によく知られているものだった。911の上にある素数はレピュニット素数だった。これは無限個あると推定されているがまだよくわかっていないのだ。もちろん,3,7,9 の1種類だけが並んだ数は素数にはならない。

2024年10月26日土曜日

政党マップ

YouTubeで,衆議院選挙向けの政治教養的な話をしていた。天理教の人だったので,思わずみてしまったが,意外にまともな説明だった。政党(および自民党総裁選の三候補)を政治的な対立軸と経済的な対立軸で2次元のマップ上に書いていた。前者は,国家主義・全体主義と個人主義・自由主義,後者は,資本主義・新自由主義と社会主義・共産主義というものだ。

なかなか面白かったのだけれど,最後に本人の支持政党が日本維新の会ということでがっくり。まあそんなものだ。

同じようなことをしてみようと,AIチャットボットに次の質問をいれてみる。

Q(koshix):
日本の政党の主張を分析して,経済軸と政治軸の2軸で あらわされる平面上の矩形領域で表わしたいと思います。 経済軸(X軸)は,-1から1の値をとり,左(-1)が社会主義や共産主義的な 考え方,右(+1)が資本主義や新自由主義的な考え方に対応します。 政治軸(y軸)は,-1から1の値をとり,下(-1)が自由主義や個人主義的な 考え方,上(+1)が国家主義は全体主義的な考え方に対応します。 例えば,アメリカ合衆国は (1,-1)の近傍に位置し,中華人民共和国は (-1,1)の近傍に位置します。 日本の政党としては,以下の10党を選びます。 自由民主党,公明党,日本維新の会,参政党,日本保守党, 国民民主党,立憲民主党,れいわ新撰組,社民党,日本共産党 結果は,政党名:(-0.9〜-0.6,0.4〜0,8) のように表現してください。

ChatGPT-4o,Gemini 1.5 flash,Perplexity Pro ,Claude 3.5 Sonnet で試してみるとそれらしい結果が出てきたが,まちまちだったので,適当に平均した後に主観的に修正してみる。これを図示するプログラムもPerplexity ProやChatGPT-4oに任せてみたところ,なんとか結果が得られた(日本語フォントコードはこちらで修正追加,USAとChinaも追加している)。あとは,適当なアルゴリズムで尺度さえ決まればよいということになるが,そのあたりを具体的に表現するのは難しい。ちなみに,OpenAI o1 では期待するようなそれらしい結果がでなかった。

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.font_manager as fm
from matplotlib import rcParams

# フォントの設定
font_path = '/System/Library/Fonts/ヒラギノ丸ゴ ProN W4.ttc'  # 適切な日本語フォントファイルを指定してください
font_prop = fm.FontProperties(fname=font_path)
rcParams['font.family'] = font_prop.get_name()

# 政党名とそれぞれの位置(Avarage?)
parties = {
    '自由民主党': ((0.1, 0.9), (0.1, 0.9)),
    '公明党': ((-0.1, 0.3), (0.0, 0.5)),
    '日本維新の会': ((0.6, 1.0), (0.2, 0.8)),
    '参政党': ((-0.2, 0.2), (0.7, 0.9)),
    '日本保守党': ((0.6, 0.9), (0.8, 1.0)),
    '国民民主党': ((0.0, 0.4), (-0.1, 0.2)),
    '立憲民主党': ((-0.6, 0.2), (-0.5, 0.1)),
    'れいわ新撰組': ((-0.7, -0.3), (-0.7, -0.3)),
    '社民党': ((-0.9, -0.5), (-0.6, -0.3)),
    '日本共産党': ((-0.9, -0.7), (-0.3, -0.1)),
    'U.S.A.': ((0.75, 1.0), (-1.0, -0.20)),
    'China': ((-1.0, -0.25), (0.75, 1.0)),
    # 修正された位置
}

# グラフの設定
plt.figure(figsize=(8, 6))

# 軸の設定
plt.axhline(0, color='black', linewidth=0.5, ls='--')  # x=0
plt.axvline(0, color='black', linewidth=0.5, ls='--')  # y=0
plt.grid(color='gray', linestyle='--', linewidth=0.5)

# 政党ごとに異なる色を設定
colors = {
    '自由民主党': 'blue',
    '公明党': 'yellow',
    '日本維新の会': 'green',
    '参政党': 'orange',
    '日本保守党': 'purple',
    '国民民主党': 'brown',
    '立憲民主党': 'cyan',
    'れいわ新撰組': 'magenta',
    '社民党': 'pink',
    '日本共産党': 'red',
    'U.S.A.': 'gray',
    'China': 'gray',
}

# データプロット
for party, ((x_min, x_max), (y_min, y_max)) in parties.items():
    rect = patches.Rectangle(
        (x_min, y_min),
        x_max - x_min,
        y_max - y_min,
        alpha=0.3,
        color=colors[party],  # 色を設定
        label=party
    )
    plt.gca().add_patch(rect)
    plt.text((x_min + x_max) / 2, (y_min + y_max) / 2, party,
             fontsize=9, ha='center', va='center')

# 軸の範囲
plt.xlim(-1.1, 1.1)
plt.ylim(-1.1, 1.1)

# タイトル
plt.title('日本の政党の経済軸と政治軸')
plt.xlabel('経済軸 (左: 社会主義, 右: 資本主義)')
plt.ylabel('政治軸 (下: 自由主義, 上: 国家主義)')
plt.grid()

plt.show()


図:日本の政党マップ(ひとつのイメージ)

[2]政治的スペクトル(Wikipedia)

2024年7月20日土曜日

プログラミング的思考(3)

プログラミング的思考(2)コンピューテーショナル・シンキング(4)からの続き

Q(koshix):
プログラミング的思考とComputational Thinkingの共通点と相違点をおしえてください。情報教育において,どちらが有用でしょうか。

A(Perplexity PRO):
プロサーチ
・プログラミング的思考の定義を検索する
・Computational Thinkingの定義を検索する
・プログラミング的思考とComputational Thinkingの共通点と相違点を比較する
・情報教育においてプログラミング的思考とComputational Thinkingのどちらが有用かを評価する

回答
プログラミング的思考とComputational Thinkingの共通点と相違点 
共通点
・目的達成のための論理的思考: 両者とも、目的を達成するために論理的に手順や方法を考える力を重視しています。これは問題解決において非常に重要です。
・抽象化と分解: どちらも、問題を小さな部分に分解し、それぞれを抽象化して理解する能力を必要とします。このプロセスにより、複雑な問題を扱いやすくします。
・シミュレーションと試行錯誤: どちらも、シミュレーションや試行錯誤を通じて、最適な解決策を見つけることを重視します。 
相違点
プログラミング的思考
効率の重視: 目的達成のために最適な方法を見つけることに重点を置きます。例えば、洗濯の手順を効率的に行う方法を考える。
具体的な手順の構築: 具体的な手順やアルゴリズムを作成し、それを実行することに焦点を当てます。
プログラミング教育の一環: 主にプログラミング教育の中で教えられ、具体的なプログラムの作成を通じて学びます。
Computational Thinking
広範な適用: 計算論的思考は、プログラミングだけでなく、日常生活や他の学問分野にも適用されます。例えば、データの整理やパターン認識。
概念的な理解: 計算論的思考は、問題を解決するための概念的な枠組みを提供し、広範な問題に適用可能です。
コンピュータ科学の基盤: コンピュータ科学全般の基盤として教えられ、プログラミング以外の多くの分野にも応用されます。

情報教育における有用性
どちらが情報教育において有用かは、教育の目的や対象によって異なります。
・プログラミング的思考は、具体的なプログラムを作成する能力を養うために非常に有用です。プログラミングの基礎を学ぶことで、論理的思考力や問題解決能力が向上します。
・Computational Thinkingは、より広範な問題解決能力を養うために有用です。これは、プログラミングだけでなく、データ分析やシステム設計など、他の多くの分野にも応用可能です。

したがって、初等教育やプログラミングの基礎を教える場合には「プログラミング的思考」が有用です。一方で、より広範な問題解決能力やコンピュータ科学全般の理解を深めるためには「Computational Thinking」が有用です。

両者をバランスよく取り入れることで、学生は具体的な技術スキルと広範な問題解決能力の両方を身につけることができます。

Sources:
1: https://kids.athuman.com/cecoe/articles/000178/
2: https://www.koov.io/column/210
3: https://products.sint.co.jp/topsic/blog/computational-thinking
4: https://www.cs.cmu.edu/afs/cs/usr/wing/www/ct-japanese.pdf

 


図:Computational Thinking のイメージ(Nord Anglia から引用)


2024年7月11日木曜日

サイコロの雌雄

70年以上生きていても知らないことがたくさんある。サイコロには雌雄がある(普通のサイコロは雌)というのだ。まあ,いわれてみれば,サイコロの鏡像は自分自身とは重ならない。学部の4回生のセミナーでLeeとYangのパリティ非保存の論文をさんざん読み込まされた割には,自らの不明を恥じるしかない。

さっそく,図示するため生成AIに頼るのだが,これが一筋縄では行かない。最近,巷で流行る石○構文のようなもので,空気が読み切れない部分的に論理的な無駄なやりとりの行き違いが山のように蓄積していく。ときどき一発で正解がでることもあるが,ちょっと相手の機嫌を損ねると,三途の河原送りで石を積み上げ続ける羽目に陥る。
Q(koshix):
立方体のサイコロの展開図を描画するpythonプログラムを下さい。
展開図は6つの正方形の面が接して並ぶものです。
各面には1〜6の数が適当は大きさの小丸(ドット)で表現されます。
展開図を組み立てたときに向かいあう面の数の和が7になるように配置されます。
1の面の小丸は少し大きい赤色ですが,他の面の2-6の小丸は黒です。 
A(Claude 3.5 Sonnet):
→SVGファイルが生成されて保存されるはずが失敗
→画面にも表示されないので,pngファイルに変更
→画面に出力された(OK)ので,雌雄を判定させる。

Q(koshix):
以下の定義からこのサイコロの展開図の雌雄を判定してください。

サイコロの目の割り振りは、ほぼ「天一地六東五西二南三北四」(雌サイコロ)と決まっている。これは、日本だけの特徴ではなく、世界的な共通点である。ただし、「南三」でなく「北三」になっているサイコロもあり、「南三」を雌サイコロ、「北三」を雄サイコロと呼ぶこともある(異性)。サイコロの雌雄の見分け方は、1・2・3の面が集まる頂点を正面に置き、1→2→3の順に見たときに時計回りになるのが雄サイコロ、反時計回りになるのが雌サイコロである。

→ただしく雌であると判定できた,エライ
→雌雄の両方の展開図を上下に出力させる
→日本語ラベルが文字化けしているので修正させる
→なかなか修正できず何度も失敗する
→こちらから日本語設定のサンプルコードを与える
→できた!日本語ラベルと展開図が重なっているので右シフトを依頼
→Claude 3.5 Sonnetの無料制限オーバーで停止,PROへの移行を勧められる

ChatGPT-4oに移動
Q(koshix):
サイコロの雌雄の展開図を表示する以下のpythonプログラムで,日本語ラベルと展開図が少し重なっているので,展開図を少し右に動かしてください。
Q(koshix):
今度は,展開図の右側が画面からはみ出して欠けてしまいました。なんとかなりませんか。
Q(koshix):
雄サイコロの方は向かいあう目の数の和が7になっていないところがあります。
4の目と5の目を入れ替えてください。
Q(koshix):
いや、そうではなくて,1,2,3の配置は元のままでよかったのです。それらと6はそのままで,4の目と5の目だけを入れ替えてという意味です。
Q(koshix):
雄のサイコロの2の目と3の目も入れ替わりましたよ。そこは元のままなのです。
Q(koshix):
雄サイコロはまだ直っていませんよ
この辺で,人間のほうが力尽きてしまったので,自分で修正したのが以下のコードである。賢いような・・・そうでもないような・・・プログラミングにおいて人間に最も必要とされるのは忍耐強い対話力だ。いわゆるプログラミング的思考力や論理的思考力ではない。

from PIL import Image, ImageDraw, ImageFont
from IPython.display import display
from matplotlib import rcParams
import matplotlib.font_manager as fm

def create_dice_face(draw, x, y, size, number):
    # 正方形を描画
    draw.rectangle([x, y, x + size, y + size], outline="black", width=2, fill="white")
    
    dot_positions = {
        1: [(0.5, 0.5)],
        2: [(0.25, 0.75), (0.75, 0.25)],
        3: [(0.25, 0.75), (0.5, 0.5), (0.75, 0.25)],
        4: [(0.25, 0.25), (0.25, 0.75), (0.75, 0.25), (0.75, 0.75)],
        5: [(0.25, 0.25), (0.25, 0.75), (0.5, 0.5), (0.75, 0.25), (0.75, 0.75)],
        6: [(0.25, 0.25), (0.25, 0.5), (0.25, 0.75), (0.75, 0.25), (0.75, 0.5), (0.75, 0.75)]
    }    
    dot_size = int(size * 0.15) if number == 1 else int(size * 0.1)
    dot_color = "red" if number == 1 else "black"
    
    for px, py in dot_positions[number]:
        cx, cy = int(x + px * size), int(y + py * size)
        draw.ellipse([cx - dot_size//2, cy - dot_size//2, cx + dot_size//2, cy + dot_size//2], fill=dot_color)

def create_dice_net(draw, start_x, start_y, size, is_female):
    # 展開図の配置: 
    #     [4]
    # [2][1][5][6]
    #     [3]
    faces_female = [
        (4, size, 0),        # 上面
        (2, 0, size),        # 左面
        (1, size, size),     # 前面
        (5, size * 2, size), # 右面
        (6, size * 3, size), # 後面
        (3, size, size * 2)  # 下面
    ]    
    faces_male = [
        (5, size, 0),        # 上面(4と5を入れ替え)
        (3, 0, size), # 左面
        (1, size, size),     # 前面
        (4, size * 2, size), # 右面(4と5を入れ替え)
        (6, size * 3, size), # 後面
        (2, size, size * 2)  # 下面
    ]    
    faces = faces_female if is_female else faces_male
    
    for number, x, y in faces:
        create_dice_face(draw, start_x + x, start_y + y, size, number)

def create_both_dice_nets(size=100):
    # フォントの設定
    font_path = '/System/Library/Fonts/ヒラギノ丸ゴ ProN W4.ttc'  # 適切な日本語フォントファイルを指定してください
    font_prop = fm.FontProperties(fname=font_path)
    rcParams['font.family'] = font_prop.get_name()
    
    # PILで使用するフォントを設定
    pil_font = ImageFont.truetype(font_path, 20)

    # 展開図の全体サイズを設定
    img_width, img_height = size * 5, size * 7 # 高さを2倍+余白に
    img = Image.new('RGB', (img_width, img_height), color='white')
    draw = ImageDraw.Draw(img)
    
    # 雌サイコロの展開図を描画
    create_dice_net(draw, 50, 20, size, True)
    draw.text((10, 10), "雌サイコロ", fill="black", font=pil_font)
    
    # 雄サイコロの展開図を描画
    create_dice_net(draw, 50, 20 + size * 3 + 50, size, False)  # 50ピクセルの余白を追加
    draw.text((10, size * 3 + 60), "雄サイコロ", fill="black", font=pil_font)
    
    return img

# 画像を生成し、Jupyter Notebookに表示
both_dice_nets = create_both_dice_nets(size=100)
display(both_dice_nets)

# PNG画像として保存(オプション)
both_dice_nets.save("both_dice_nets.png")
print("both_dice_nets.png が生成されました。")


図:サイコロの雌雄(雌 1→2→3 反時計回り,雄 1→2→3 時計回り)

P. S. 上記の配置はサイコロキャラメルで確認した。実際には,2の目,3の目,6の目の配置がそれぞれ2パターンあるので,1つの性別に対して8パターンの配置がある。普通のサイコロは,1-2-3の頂点から見て,2や3の目が頂点から放射状に配列するのではなく,頂点のまわりを取り囲むように配列していることになる。

2024年7月9日火曜日

東京都知事選挙(5)

東京都知事選挙(4)からの続き

NHKが,出口調査で得た年代別の主要候補者支持率(それほど正確ではない)を出している。これを用いて,各年代区分のサイズを投票率×年代人口で正規化した人口比例面積表示のグラフを描いた。

生成AIを使ってプログラムを作ろうとしたが,(1) Pythonでは違うロジックの図がでてきたので,修正をあきらめた。(2) ChatGPTで,設定を簡単にした説明を入れ,Juliaプログラミングをすると,概ねできたが途中でつまづいた。(3) Claude 3.5 Sonnetで日本語の修正を含めて,最終版まで行き着いた。やはり,プログラミング経験なしでスイスイとはいかないのだ。

最初のChatGPTへの入力と,最終のJuliaコードを示す。
Q(koshix):
次のように正方形の領域を分割して色を塗るJuliaプログラムをつくってください。
 (1) 6個の世代人口データがあります。この値に比例して正方形を水平線で分割します。 つまり,上から,20代,30代,40代,50代,60代,70代の人口に比例した長方形 ができます。
 (2) 各長方形を,n人の候補者(A,B,C,...)の支持率をつかって垂線で分割します。 つまり,各年代ごとに,Aの支持率,Bの支持率,Cの支持率,その他の支持率と なります。これらの支持率の和は100%であり,それが正方形の横軸の長さになります。
 (3) 各年代のAの支持率のブロックは色#28A745,Bの支持率のブロックは色#F39C12, Cの支持率のブロックは色#3498DB,その他の支持率のブロックは色#CA8888 とします。
 (4) 各世代の人口数は,{48,68,100,133,132,117}とします。
 (5) 20代の支持率は,{0.27,0.44,0.11,0.18}とします。 
      30代の支持率は,{0.30,0.31,0.18,0.21}とします。 
      40代の支持率は,{0.36,0.32,0.15,0.16}とします。
      50代の支持率は,{0.42,0.25,0.16,0.16}とします。 
      60代の支持率は,{0.41,0.22,0.26,0.11}とします。 
      70代の支持率は,{0.51,0.11,0.31,0.08}とします。

A(Claude 3.5 Sonnet):
using CairoMakie

# データの準備
populations = [48, 68, 100, 133, 132, 117]
total_population = sum(populations)

support_rates = [
    [0.27, 0.44, 0.11, 0.18],  # 20代
    [0.30, 0.31, 0.18, 0.21],  # 30代
    [0.36, 0.32, 0.15, 0.17],  # 40代
    [0.42, 0.25, 0.16, 0.17],  # 50代
    [0.41, 0.22, 0.26, 0.11],  # 60代
    [0.51, 0.11, 0.31, 0.07]   # 70代
]

colors = ["#28A745", "#F39C12", "#3498DB", "#CA8888"]
age_labels = ["18-", "30-", "40-", "50-", "60-", "70-"]

# プロットの作成
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)

図:東京都知事選年代別支持率(人口比例面積表示)

50代以上が6割,40代以下が4割なので,若年層の石丸効果はそれほど効かず,中年層が実数を担っている。蓮舫は60代以上でしっかりと受け止められているが,若年中年層にはまったく浸透していない。

P. S. NHKの年代別支持率のグラフをキャプチャーして,画像ファイルを生成AIに読み込ませて,各候補の支持率%の数値を読み取ろうとした。それらしい数字は出てくるのだけれど,残念ながら,正しい答えは得られなかった。仕方がないので,プリントアウトして,15cmのプラスチック物差しで測って,エクセルに入力して最終的なデータを導いた。

P. P. S. 奥村先生のところに関連するグラフなどがあった。

[1]2024年の東京都知事選挙(奥村晴彦)

2024年6月14日金曜日

合計特殊出生率(3)

合計特殊出生率(2)からの続き

物理学者の白石さんが,合計特殊出生率の件で強い主張をしていたので確認してみる。ようは,合計特殊出生率を決めているのは有配偶率であって,人口学的に確かめられている。それ以外の恣意的な原因を持ち出しているのはみんな出鱈目だ。ということだった。

仮にそうであっても,有配偶率自身を規定する原因は考えられるのだから,そこまで強く主張する必要はまったくないのに,と思う。まあいつものことだ。

都道府県別の合計特殊出生率と直近の国勢調査で得られた都道府県別の15-49歳女性の有配偶率を散布図にしてみる。1953年から2022年までの両者の関係も出せないかと調べるのだけれど,必要な情報はデータの海に埋もれてしまっていて,簡単には見つかりそうになかった。どの生成AIも簡単には探しだしてはくれない。

とりあえず,Juliaでコーディングしてみる。
using Gadfly
using Compose
using DataFrames
X = [46.6,47.4,49.4,47.7,49.9,51.2,50.0,50.4,50.9,49.9,49.3,49.0,44.2,49.0,50.4,52.0,50.4,52.3,49.5,51.7,51.4,51.6,51.9,51.4,52.2,44.4,43.5,48.8,47.0,48.5,49.2,51.9,48.9,50.7,49.9,49.3,51.1,49.6,46.1,46.2,48.5,48.6,48.5,49.5,49.4,49.2,45.8]
Y = [1.06,1.23,1.16,1.07,1.10,1.22,1.21,1.22,1.19,1.25,1.14,1.14,0.99,1.13,1.23,1.35,1.34,1.46,1.32,1.34,1.31,1.25,1.29,1.29,1.38,1.11,1.19,1.29,1.21,1.33,1.44,1.46,1.32,1.33,1.40,1.36,1.40,1.31,1.30,1.26,1.46,1.49,1.47,1.39,1.49,1.48,1.60]
Labels = ["北海道","青森","岩手","宮城","秋田","山形","福島","茨城","栃木","群馬","埼玉","千葉","東京","神奈川","新潟","富山","石川","福井","山梨","長野","岐阜","静岡","愛知","三重","滋賀","京都","大阪","兵庫","奈良","和歌山","鳥取","島根","岡山","広島","山口","徳島","香川","愛媛","高知","福岡","佐賀","長崎","熊本","大分","宮崎","鹿児島","沖縄"]

plot(x=X, y=Y, label=Labels, Geom.point, Geom.label, Theme(major_label_font="CMU Serif",minor_label_font="CMU Serif",major_label_font_size=12pt,minor_label_font_size=12pt), Guide.xlabel("女性有配偶率"),Guide.ylabel("合計特殊出生率"))

沖縄は特異点になっているが,合計特殊出生率とこれに対応する年代の女性の有配偶率には正の相関がみられる。

図:女性の有配偶率と合計特殊出生率の関係

P. S.  x軸とy軸の日本語ラベルのつけ方は,ChatGPT-4oに教えてもらった。

2024年6月7日金曜日

合計特殊出生率(2)

合計特殊出生率(1)からの続き

去年の今ごろ,奥村晴彦さんの合計特殊出生率Rプログラムを写経していた。すっかり忘れていたけれど,2023年の新しい合計特殊出生率が過去最低の1.20人を記録したいうニュースで思い出すことになった。

都道府県別でみれば,東京都の0.99や北海道の1.06をはじめ,大都市部や東北地方で低く,沖縄の1.60を筆頭に,九州,山陰,北陸などで高い。

今年は,コマンドラインで実行すべく,Rプログラムを修正した。あいかわらず日本語表示でつまづいたので,ChatGPT-4oに聞きながら試行錯誤した結果,何とか解決できた。なお,スタートを自分が生まれた1953年にしてみた。

修正したRプログラムのtfr.rの内容(注:remove \ before copy and paste)は

library(showtext)

font_add("Hiragino", "/System/Library/Fonts/ヒラギノ角ゴシック W3.ttc")

showtext_auto()


X <- read.table('/Users/koshi/Desktop/birth.txt', header = TRUE)

par(family = "Hiragino")  # 利用可能なフォントを指定

plot(X\$年, X\$合計特殊出生率, type="o", pch=16, xlab="", ylab="")

t = c(range(X\$合計特殊出生率), X\$合計特殊出生率[length(X[,1])])

axis(4, t, t)

title("合計特殊出生率", line=0.5)

#title("Total Fertility Rate", line=0.5)


# x軸とy軸のメモリの位置を手動で指定

x_ticks <- pretty(X$年, 15)  # 15個のメモリを生成

y_ticks <- pretty(X$合計特殊出生率, 16)  # 16個のメモリを生成


# x軸に対してグリッド線を描画

for (i in x_ticks) {

  abline(v = i, col = "lightgray", lty = "dotted")

}


# y軸に対してグリッド線を描画

for (i in y_ticks) {

  abline(h = i, col = "lightgray", lty = "dotted")

}

これで,Rscript tfr.r とすると,pdfの出力,Rplots.pdf が得られる。なお,showtextというライブラリは,コマンドラインで R として立ち上げたプロンプトに,install.packages("extrafont") としてインストールしておく。また,データは birth.txt にあらかじめ入れてある。

年    出生数 合計特殊出生率

1953 1868040           2.70

1954 1769580           2.48

1955 1730692           2.37

1956 1665278           2.22

1957 1566713           2.04

1958 1653469           2.11

1959 1626088           2.04

1960 1606041           2.00

1961 1589372           1.96

1962 1618616           1.98

1963 1659521           2.01

1964 1716761           2.05

1965 1823697           2.14

1966 1360974           1.58

1967 1935647           2.23

1968 1871839           2.13

1969 1889815           2.13

1970 1934239           2.13

1971 2000973           2.16

1972 2038682           2.14

1973 2091983           2.14

1974 2029989           2.05

1975 1901440           1.91

1976 1832617           1.85

1977 1755100           1.80

1978 1708643           1.79

1979 1642580           1.77

1980 1576889           1.75

1981 1529455           1.74

1982 1515392           1.77

1983 1508687           1.80

1984 1489780           1.81

1985 1431577           1.76

1986 1382946           1.72

1987 1346658           1.69

1988 1314006           1.66

1989 1246802           1.57

1990 1221585           1.54

1991 1223245           1.53

1992 1208989           1.50

1993 1188282           1.46

1994 1238328           1.50

1995 1187064           1.42

1996 1206555           1.43

1997 1191665           1.39

1998 1203147           1.38

1999 1177669           1.34

2000 1190547           1.36

2001 1170662           1.33

2002 1153855           1.32

2003 1123610           1.29

2004 1110721           1.29

2005 1062530           1.26

2006 1092674           1.32

2007 1089818           1.34

2008 1091156           1.37

2009 1070036           1.37

2010 1071305           1.39

2011 1050807           1.39

2012 1037232           1.41

2013 1029817           1.43

2014 1003609           1.42

2015 1005721           1.45

2016  977242           1.44

2017  946146           1.43

2018  918400           1.42

2019  865239           1.36

2020  840835           1.33

2021  811622           1.30

2022  770759           1.26

2023  727277           1.20

図:1953-2023  の合計特殊出生率の推移