2026,江端さんの忘備録

私の勤務している会社のセキュリティレベルが、世間の中でどの程度のレベルかは分かりませんが、少なくともシステムの不正ログインにおいては、私は、このシステムを破る手段が思いつきません。
I do not know how the security level of the company I work for compares with that of the rest of the world, but at least regarding unauthorized system logins, I cannot think of any way to break into this system.

まあ、まあ、そういう『自分では、思いもつかない』という安心感の上に胡座をかいている会社から、攻撃されることになっています。
Nevertheless, attacks often target companies that sit complacently on the belief that “we ourselves cannot even imagine how this could be broken.”

私たち(のシステム)は、一日十数時間も費やせる"ヒマ"なITエンジニア(ChatGPTにコードを作ってもらうだけの高校生)に、大企業のシステムは"負ける"運命にあるのです。
Our systems are ultimately destined to lose to “free-time” IT engineers who can spend more than ten hours a day on them (even high school students who have ChatGPT write the code).

---

SNSの炎上において、
In social-media flame wars,

- 潤沢な時間があり
having abundant time,

- やることがない
and having nothing else to do,

「ヒマな人間」が常に"最強"です。
The “person with free time” is always the strongest.

これを、"嫌味"とか"皮肉"とか"バカにしている"と受け取ることは、誤りです。これは厳然たる客観的な事実なのです。
It is a mistake to interpret this as sarcasm, irony, or ridicule. It is simply an objective fact.

逆に言えば、SNSの炎上において、本当に勝者になりたいのであれば、そのアプローチは一つしかありません。
Conversely, if you truly want to win in a social-media flame war, there is only one approach.

「ヒマな人間」になることです。または、自分を「ヒマな人間」になるための努力をすることです。
You must become a “person with nothing but time,” or you must actively work to turn yourself into one.

繰り返しますが、私は"本気"で語っています。
Let me repeat: I am speaking completely seriously.

---

「ヒマな人間」になるためには、時間に拘束される定職についていてはダメでしょう。結婚などは論外、育児などはスコープ外です。
To become a “person with free time,” you probably cannot have a regular job that binds your time. Marriage is out of the question, and raising children is outside the scope.

親からの遺産を引き継いだ不労所得のある人、あるいは潤沢な資金を得たままリタイアしたシニアを除けば、このような「ヒマな人間」の、QoL(Quality of Life)は低いのかもしれませんが ――
Unless one has inherited wealth that produces passive income, or is a senior who retired with ample funds, the QoL (Quality of Life) of such a “free-time person” might indeed be low.

SNSの炎上で勝つためには、この覚悟があれば十分です。
But this level of commitment is sufficient if your goal is to win in a social-media flame war.

繰り返しますが、「ヒマな人間」は、真の意味において最強です。このような人と戦ってはなりません。
Again, the “person with free time” is the strongest in the truest sense. One must not fight such people.

その人に正論や正義はなくとも、「ヒマな人間」には、無限に攻撃を続けることのできる「時間」という「実弾」が、無限にあるからです。
Even if they have neither sound arguments nor justice on their side, they possess an unlimited supply of “live ammunition” called time with which they can continue attacking indefinitely.

時間とコストに制約された人間が、このような「ヒマな人間」に勝てる道理はないのです。
People constrained by time and cost have no realistic way to defeat such opponents.

つまり、現代社会の多くの衝突は、「正しい側」と「間違った側」の戦いではありません。「時間のある側」と「時間のない側」の戦いなのです。
In other words, many conflicts in modern society are not battles between right and wrong. There are battles between those who have time and those who do not.

閑話休題
Anyway,

---

攻撃の報酬が「億円」のレベルで、そのために一日十数時間も費やせるITエンジニアは、最強のサイバー攻撃者になれます。
An IT engineer who can spend more than 10 hours a day hunting for attacks with rewards worth hundreds of millions of yen can become the strongest kind of cyberattacker.

なにしろ、世の中にはセキュリティが"ザル"のシステムが山ほどあり、攻撃者から見れば"宝の山"のような状態です。
After all, the world is full of systems whose security is like a sieve, and from an attacker’s perspective, they appear as mountains of treasure.

『リタイア後の生活としては、そういう戦略(サイバー攻撃者(犯罪者)となる)になる選択肢もあるが』と嫁さんに相談したら、即座に却下されました。
When I consulted my wife and suggested that becoming a cyber attacker (in other words, a criminal) might be one retirement strategy, it was immediately rejected.

理由は単純明快で『犯罪者(の家族)になるリスクに対して割が合わない』とのことでした。まあ、私もそう思います。
Her reason was simple and clear: “The risk of becoming the family of a criminal is not worth it.” I think she is right.

では、『犯罪者になる覚悟がある』『犯罪者となる家族がいない』人はどうでしょうか ―― それは一発賭ける価値があるように思えます。
But what about people who are “prepared to become criminals” and “have no family who would be affected”? For them, it might appear to be a gamble worth taking.

それはさておき。
That aside.

---

私の勤務している会社では、会社から配給されたセキュリティ基盤を『自力で差し換える』という要求が度々発生します。
At the company where I work, we are frequently required to “replace the security infrastructure ourselves.”

この命令がでるたびに、私は胃を痛めています。
Every time this instruction appears, my stomach begins to ache.

- マニュアル通りの作業で、成功したためしがない(成功するのはレアケース)
Following the manual has rarely succeeded (to be precise, it has rarely succeeded).

- コールセンタは、メールで連絡するためのエントリーを作るだけで、基本的には相談に応じてくれない
The call center merely creates an entry so that you can send an email and basically does not provide consultation.

- メールで連絡するためPCが、そのセキュリティ基盤を差し替えるPCそのもの
The PC used to send the email is the very PC whose security infrastructure must be replaced.

つまり、セキュリティ基盤の差し替えに失敗すれば、セキュリティ基盤の差し替えの指示を受けるPCそのものが止まる。
In other words, if the replacement fails, the very PC that received the instruction to perform the replacement stops working.

(社用スマホで連絡を取ることができないわけではありませんが、おそろしく面倒くさく、かつ、まともなコミュニケーションができなくなる)
(It is not impossible to contact people with a company smartphone, but it is extremely cumbersome, and meaningful communication becomes difficult.)

この恐怖は、このような差し替えをやらされた人にしか分かりません。
This fear can only be understood by those who have been forced to perform such a replacement.

差し替え作業後、再起動に失敗したら、そのPCは通信手段を失い、何もできない箱になる、ということです。
If the reboot fails after the replacement, the PC loses all communication capability and becomes nothing more than a useless box.

社用PCは業務の生命線ですが、社用PCが止まれば、報告書も、特許出願も、稟議も、何もかもできなくなります。本当に怖いです。
The company PC is the lifeline of my work. If it stops, I cannot write reports, file patents, submit approval documents, or do anything else. It is genuinely frightening.

もちろん、数日とか1週間の期間があれば、色々な手段が取れるでしょうが、仕事の大部分は「その日の内の対応」がほとんどです。
Of course, if one had several days or a week, various measures could be taken, but most work requires responses within the same day.

私、こういう『綱渡り』をやってくれる店があれば、日本中のどこへでも行って、10万円くらいなら払っても良いと思っています。
If there were a place that would perform this kind of “tightrope walk” for me, I would go anywhere in Japan and pay about 100,000 yen.

そりゃ、私が、セキュリティ部門に専属で働いているのであれば、こんな恐怖を感じる必要はないでしょう。
Of course, if I worked exclusively in the security department, there would be no reason to feel this fear.

セキュリティに関して、業務時間を使うことができる「時間」という「実弾」が潤沢にあるならいいのです。
If I had abundant “live ammunition” called time during working hours to handle security matters, it would be fine.

しかし、私たちにとって、セキュリティ基盤は、本業ではありません。副業ですらありません。しかしながら、「やらなければ、私の業務が死ぬ」という恐喝まがいの『負債』です。
However, for us, the security infrastructure is not our main job. It is not even a side job. Yet it is a coercive “liability” if we do not do it; our own work dies.

---

なんで、こんな怖いことを、自分でやらなければならないんだ ―― と、いつも泣きながら作業しています。
Why must I do something this frightening? I work on it almost in tears every time.

先程、私も、差し替えが無事完了した(ちなみに作業開始は先週の木曜日)ことを確認して、今、呆けています。
I just confirmed that the replacement was completed successfully (I started the work last Thursday), and I am currently sitting here in a daze.

こういう作業を、末端の社員にやらせる、ということは、どうなのでしょうか? こんなことさせるの、ウチの会社だけでしょうか? 私、主任クラスの研究員ですが、この作業は、コストに見当っているんでしょうか?
What does it mean to make front-line employees perform this kind of work? Is my company the only one that does this? I am a researcher at roughly the senior-staff level. Is this really worth the cost?

もしかしたら、
Perhaps,

―― セキュリティ基盤を自力で差し換えることもできない社員なんぞ、うち(の会社に)は"いらん"
“Employees who cannot even replace the security infrastructure themselves are unnecessary for our company.”

という試金石なのかもしれませんが ―― それでも、なお、エンジニアのセキュリティ対応テストのコストとして、このやり方がペイしているとは思えないですよ、私には。
It might be a kind of test but even so, I cannot believe that this method pays off as the cost of an engineer’s security capability test.

---

ともあれ ――
In any case 

私は、時間戦争における社会底辺の弱者なのに、時間戦争の強者から脅迫を受け続けている、本当に可哀想な社員なのです。
I am a socially weak person at the bottom of the “time war,” yet I continue to be threatened by those who are strong in that war. truly a pitiful employee.

私は、稼動中のサービスに手を加えるなどという恐しいことはできません ーー サービスを停止させることよりも、サービスが再起動しなくなる恐怖が、半端ではないからです。

2026,江端さんの忘備録

東日本大震災が発生した時に、著名な芸能人やアスリートを登場させて、
When the Great East Japan Earthquake occurred, well-known celebrities and athletes appeared in advertisements,

『間違った情報に惑わされないようにしよう』
"Let's not be misled by incorrect information."

というACジャパンの公共広告が、ジャンジャン流れていました。
Public service advertisements by AC Japan carrying that message were broadcast repeatedly.

wp.kobore.net/2011/04/09/post-24554/

でもその後の私の調査結果では、政府の発表の中でさえ「間違った情報」はありました。
However, according to my later investigation, even the government's announcements contained "incorrect information."

それらの情報は、(1)その時点においては本当に分からなかった、(2)その時点で分かってはいたが、国民のパニックを抑えるために、政治的な恣意が含まれていた[1][2][3]、という2種類があることが分かっています。
It turns out that such information can be divided into two types: (1) things that were genuinely unknown at that time, and (2) things that were already known but were presented with political discretion in order to suppress public panic [1][2][3].

[1]メルトダウン発表の遅れ
[1] Delay in announcing the meltdown

―― この東京五輪のイベントに関して『だけ』は、予測が外れる

[2]SPEEDI情報の公開遅れ
[2] Delay in releasing SPEEDI information

[3]放射線拡散予測の非共有
[3] Non-sharing of radiation dispersion forecasts

SPEEDIのシミュレーション結果、非公開

つまるところ、『間違った情報』というのは、その時点から十分な時間が経過した後に、多くの人間によって、多角的な観点からレビューされた時に判明するものになってしまう、ということです。
In short, what is called "incorrect information" often becomes identifiable only after sufficient time has passed and many people have reviewed it from multiple perspectives.

私が、最近、色々な政治団体について、五月雨的に調べているのは、分からないことが多いからです。
The reason I have recently been examining various political organizations piecemeal is simply that there are many things I do not understand.

参政党は外国人の何を問題としているのですか?

---

で、最近、私、開き直ってきました。
Recently, however, I have begun to take a rather resigned attitude about this.

つまるところ、『リアルタイムの世界においては"間違った情報"もないし、また"正しい情報もない』ということです。
In other words, in a real-time world, there is neither "incorrect information" nor "correct information."

要するに、私たちは、
In other words, we are

―― 絶対的正義も絶対的不正義もない、いつまでもユラユラと動き続ける不確実な情報という波の上で、
"standing on a constantly shifting wave of uncertain information where there is neither absolute justice nor absolute injustice,"

―― 一人乗りのサーフボードに乗って、
"riding a one-person surfboard,"

―― 一人で自分の進む方向を決めるしかない
"and have no choice but to decide the direction we will go by ourselves."

ということです。
That is the situation we are in.

---

そういう状況においては、事前に「何を信じるのか」を決めておくことが必要になると思います。
In such a situation, I think it becomes necessary to decide in advance what one will believe.

SNSを信じてもいいし、NHKを信じてもいい。
You may trust social media or NHK.

けど最終的には、自分で決める、という覚悟が必要です。
But in the end, you must be prepared to decide for yourself.

これは、「助かるため」というより、むしろ「ほぼ、助からないという状況で『まあ、自分で決めたことだ。仕方ない』と自分に納得させるため」です。
This is not so much for the purpose of "surviving" as to allow oneself to say, in a situation where survival is unlikely, "Well, I decided this myself, so it can't be helped."

"間違った情報"も"正しい情報"もない中で、正確な判断をする ―― そんなことは、神さまだって無理です。
To make an accurate judgment in a situation where there is neither "incorrect information" nor "correct information,"  even God would find that impossible.

無理なら無理なりに、自分のできることを自分で決定して、自分で自分の生死を自分で選びとる、というくらいの気概が必要だと思うのです。
If it is impossible, then at the very least we need the resolve to decide what we can do ourselves and to choose our own life or death by our own decisions.

---

これは、『間違った情報に惑わされないようにしよう』という「間違ったメッセージ」に対する、たぶん正解を選び取ることができない私の、人生の方針です。
This is my life policy in response to the "incorrect message" that says, "Let's not be misled by incorrect information," even though I may never be able to choose the correct answer.

ただし、もし今、あのACジャパンのCMを作り直すことができるなら、私は、こういうメッセージに変えると思います。
However, if I could remake that AC Japan commercial today, I think I would change the message to this:

―― 『どの情報に惑わされるかくらい、自分で決めよう』
"At least decide for yourself which information you will be misled by."

少なくとも、それくらいの覚悟がなければ、この情報社会を生きることはできないと思っています。
At the very least, without that level of resolve, I do not think one can live in this information society.

そして、どのような決定をするにあたっても、
And even when making such decisions,

―― 『自分で決めているつもりで、実は誰かに決めさせられている』
"Even when you think you are deciding for yourself, someone else may actually be deciding for you."

という可能性を、頭の片隅に置いておくことです。
One should keep that possibility somewhere in the back of one's mind.

そして、私も、どこかの政治家や政党に踊らされて、後になって「自分は誤った判断をしていた」と思い知ることになると思うのですが ―― まあ、それでいいというか、それしか方法がない、と思うんです。
And I myself will probably end up being made to dance by some politician or political party somewhere, but well, perhaps that is fine, or rather, perhaps there is simply no other way.

私たちは皆、これまでも、これからも、間違え続けるしかないのです。
We all have no choice but to keep making mistakes, both in the past and in the future.

訓練されたエンジニア/研究員は、自分が「間違っていること」を前提に行動します。

2011

小学3年生になる娘が、『間違った情報に惑わされないようにしよう』と言っている震災CMを見ていて尋ねました。

娘:「パパ、間違った情報って、どういう情報?」
私:「パパにも、全然分からん」

-----

間違っていない情報とは、

(1)政府発表の情報
(2)テレビのコメンテータをしている学者のコメント
(3)あるいは、ニュース番組の司会者の所感
(4)ネット上に流れる各種の噂

のどれなのでしょうか。

私には分からんけど、きっとこのCMに出ている人には分かるのでしょう。
今後は、このCMに出ている本人に尋ねることにしましょう。

その芸能人の本人(か、またはその事務所)は、こんな「勇気のある発言」をするCMに出演を決めたものだと、心の底から敬意を称します。

私なら間違っても、こんな「テロップ」を喋らされるCMには、絶対出演しない。

-----

福島原発の事故のニュースが流れた時から、私の基準は1つでした。

○原発正門前の放射線量が300ミリシーベルトを越えるという政府発表が
○連続して
○3回出た場合、
○家を捨てて逃げる

私のシナリオにおける『原子炉破壊の要件成立』としていたからです。

-----

私は、一番最初の「300ミリシーベルト」の政府発表時に、

○嫁さんに荷物を纏めるように指示をして、
○自動車にミネラルウォータ24リットルを搭載して
○ガソリンを満タン状態にして

待機していました。

----------

『逃げるべきなのか、どうなのか、なんど江端さんに電話しようかと思ったかしれませんよ』と言う後僚に、上記の私の基準を一応教えておきました。

しかし、この私の判断基準は、政府発表の内容から鑑みれば、完全に、

『間違った情報』

になります。

原子炉から放出されている放射線(×放射能物質)が何万ミリシーベルトであろうが、放射線が届かない範囲では、絶対的な意味において『安全』だからです。

-----

何を信じるかは、各個人の自由ですが、少なくとも私は、自分の家族が安全なところに辿りつく前には、『逃げるべきなのか、どうなのか』について、一切コメントしません。

私が二十の頃から、個人の趣味で読み倒してきた、原発クライシスのドキュメント、文献、映画、マンガに記載された「原子力」に関する知識は、先ず自分と家族の為に使わせて頂きます。

ネットに流したりするような「親切な行為」は一切致しませんので、御安心下さい。

未分類

ベースはこちらです。

matsim_crs_verified_exporter.html 解説書(統合版)

以下が、matsim_live_map.go の修正版全文です。
主な修正点は次の3つです。

  • eventx,y があれば それを優先使用

  • x,y がなければ link から座標を求める

  • 先頭の event 属性を画面に表示して デバッグ可能 にした

起動方法は、
(1)go run matsim_live_map.go (I:\home\ebata\hakata\video3)
(2)ブラウザからlocalhost:8080
(3)起動したブラウザで
output_network.xml.gz と output_events.xml.gz をドラッグ&ドロップ(同時に行うところがポイント)
(4)対象イベントtypeは空欄にする

[matsim_live_map.go]

package main

import (
	"flag"
	"html/template"
	"log"
	"net/http"
)

var addr = flag.String("addr", "0.0.0.0:8080", "http service address")

func main() {
	flag.Parse()
	log.SetFlags(0)

	http.HandleFunc("/", home)
	log.Printf("listen on http://%s", *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

func home(w http.ResponseWriter, r *http.Request) {
	if err := pageTemplate.Execute(w, nil); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

var pageTemplate = template.Must(template.New("page").Parse(`
<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8" />
  <title>MATSim Live Map Viewer</title>
  <meta name="viewport" content="width=device-width, initial-scale=1" />

  <link
    rel="stylesheet"
    href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
    integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
    crossorigin=""
  />
  <script
    src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
    integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
    crossorigin=""
  ></script>

  <style>
    html, body {
      margin: 0;
      padding: 0;
      height: 100%;
      font-family: system-ui, sans-serif;
    }
    body {
      display: grid;
      grid-template-columns: 420px 1fr;
      overflow: hidden;
    }
    #side {
      border-right: 1px solid #ccc;
      padding: 12px;
      overflow: auto;
      background: #fafafa;
    }
    #map {
      width: 100%;
      height: 100%;
    }
    #drop {
      border: 2px dashed #777;
      border-radius: 12px;
      padding: 16px;
      background: white;
    }
    #drop.drag {
      background: #f0f0f0;
    }
    .row {
      margin-top: 10px;
    }
    label {
      display: block;
      font-size: 12px;
      color: #444;
      margin-bottom: 4px;
    }
    input, select, button {
      width: 100%;
      box-sizing: border-box;
      padding: 8px;
      font-size: 14px;
    }
    .btns {
      display: grid;
      grid-template-columns: 1fr 1fr 1fr;
      gap: 8px;
    }
    pre {
      white-space: pre-wrap;
      word-break: break-word;
      border: 1px solid #ddd;
      background: white;
      padding: 10px;
      height: 240px;
      overflow: auto;
      font-size: 12px;
    }
    .legend {
      position: absolute;
      top: 12px;
      right: 12px;
      z-index: 1000;
      background: rgba(255,255,255,0.95);
      border-radius: 8px;
      padding: 10px 12px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.25);
      font-size: 13px;
      line-height: 1.5;
    }
    .dot {
      display: inline-block;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      margin-right: 6px;
      vertical-align: middle;
      border: 1px solid rgba(0,0,0,0.25);
    }
    .small {
      font-size: 12px;
      color: #666;
    }
    .stat {
      margin-top: 8px;
      padding: 8px;
      background: white;
      border: 1px solid #ddd;
      border-radius: 6px;
      font-size: 12px;
      white-space: pre-wrap;
    }
  </style>
</head>
<body>
  <div id="side">
    <h2>MATSim Live Map Viewer</h2>

    <div id="drop">
      <b>output_network.xml.gz</b> と <b>output_events.xml.gz</b> をここにドラッグ&ドロップ
      <div class="small" style="margin-top:8px">
        読み込み後、そのまま地図上でエージェントを再生する。<br>
        途中CSVは作らない。
      </div>
    </div>

    <div class="row">
      <label>対象イベント type(空なら広めに採用)</label>
      <input id="types" value="entered link,left link,departure,arrival,vehicle enters traffic,actstart,actend" />
    </div>

    <div class="row">
      <label>地域ヒント(経度,緯度)</label>
      <input id="hintLonLat" value="139.50,35.60" />
    </div>

    <div class="row">
      <label>ゾーン(自動選択後に手動変更可)</label>
      <select id="zoneSelect" disabled></select>
    </div>

    <div class="row">
      <label>再生速度(倍速)</label>
      <input id="speed" type="number" step="0.1" min="0.1" value="20" />
    </div>

    <div class="row">
      <div class="btns">
        <button id="prepare" disabled>解析</button>
        <button id="play" disabled>再生</button>
        <button id="pause" disabled>停止</button>
      </div>
    </div>

    <div class="row">
      <button id="reset" disabled>先頭に戻す</button>
    </div>

    <div class="stat" id="status">未読み込み</div>

    <div class="row">
      <label>プレビュー</label>
      <pre id="preview"></pre>
    </div>
  </div>

  <div style="position:relative">
    <div id="map"></div>
    <div class="legend">
      <div><span class="dot" style="background:#e74c3c"></span>Type1</div>
      <div><span class="dot" style="background:#3498db"></span>Type2</div>
      <div><span class="dot" style="background:#2ecc71"></span>Type3</div>
      <div><span class="dot" style="background:#f1c40f"></span>Type4</div>
      <hr>
      <div>time: <span id="timeText">(none)</span></div>
      <div>agents: <span id="agentCount">0</span></div>
    </div>
  </div>

<script>
let networkFile = null;
let eventsFile = null;

let parsedNetwork = null;
let preparedFrames = [];
let personMarkers = new Map();
let personState = new Map();

let timer = null;
let currentFrameIndex = 0;

const drop = document.getElementById("drop");
const prepareBtn = document.getElementById("prepare");
const playBtn = document.getElementById("play");
const pauseBtn = document.getElementById("pause");
const resetBtn = document.getElementById("reset");
const zoneSelect = document.getElementById("zoneSelect");
const statusEl = document.getElementById("status");
const previewEl = document.getElementById("preview");
const timeText = document.getElementById("timeText");
const agentCount = document.getElementById("agentCount");

const map = L.map("map", {
  attributionControl: false
}).setView([35.60, 139.50], 14);

L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
  maxZoom: 19
}).addTo(map);

function setStatus(s) {
  statusEl.textContent = s;
}

function setPreview(s) {
  previewEl.textContent = s;
}

drop.addEventListener("dragover", (e) => {
  e.preventDefault();
  drop.classList.add("drag");
});
drop.addEventListener("dragleave", () => {
  drop.classList.remove("drag");
});
drop.addEventListener("drop", (e) => {
  e.preventDefault();
  drop.classList.remove("drag");

  networkFile = null;
  eventsFile = null;

  for (const f of e.dataTransfer.files) {
    const n = f.name.toLowerCase();
    if (n.includes("network") && n.endsWith(".gz")) networkFile = f;
    if (n.includes("events") && n.endsWith(".gz")) eventsFile = f;
  }

  preparedFrames = [];
  clearMarkers();
  currentFrameIndex = 0;
  parsedNetwork = null;
  zoneSelect.innerHTML = "";
  zoneSelect.disabled = true;

  playBtn.disabled = true;
  pauseBtn.disabled = true;
  resetBtn.disabled = true;

  if (networkFile && eventsFile) {
    prepareBtn.disabled = false;
    setStatus("network/events 読み込み準備完了\n「解析」を押してください");
  } else {
    prepareBtn.disabled = true;
    setStatus("output_network.xml.gz と output_events.xml.gz の両方が必要");
  }
});

function clearMarkers() {
  for (const marker of personMarkers.values()) {
    map.removeLayer(marker);
  }
  personMarkers.clear();
  personState.clear();
  agentCount.textContent = "0";
  timeText.textContent = "(none)";
}

async function* ungzipTextChunks(file) {
  const ds = new DecompressionStream("gzip");
  const textStream = file.stream().pipeThrough(ds).pipeThrough(new TextDecoderStream("utf-8"));
  const reader = textStream.getReader();
  try {
    while (true) {
      const {value, done} = await reader.read();
      if (done) break;
      if (value) yield value;
    }
  } finally {
    reader.releaseLock();
  }
}

function parseAttrs(tagText) {
  const attrs = {};
  const re = /(\w+)\s*=\s*(?:"([^"]*)"|'([^']*)')/g;
  let m;
  while ((m = re.exec(tagText)) !== null) {
    attrs[m[1]] = (m[2] !== undefined) ? m[2] : m[3];
  }
  return attrs;
}

function normalizeTypeName(s) {
  return String(s || "").trim().toLowerCase();
}

const JPRCS = {
  1:  {lat0:33, lon0:129},
  2:  {lat0:33, lon0:131},
  3:  {lat0:36, lon0:132.1666666667},
  4:  {lat0:33, lon0:133.5},
  5:  {lat0:36, lon0:134.3333333333},
  6:  {lat0:36, lon0:136},
  7:  {lat0:36, lon0:137.1666666667},
  8:  {lat0:36, lon0:138.5},
  9:  {lat0:36, lon0:139.8333333333},
  10: {lat0:40, lon0:140.8333333333},
  11: {lat0:44, lon0:140.25},
  12: {lat0:44, lon0:142.25},
  13: {lat0:44, lon0:144.25},
  14: {lat0:26, lon0:142},
  15: {lat0:26, lon0:127.5},
  16: {lat0:26, lon0:124},
  17: {lat0:26, lon0:131},
  18: {lat0:20, lon0:136},
  19: {lat0:26, lon0:154},
};

function tmParamsFromZone(zone) {
  const z = JPRCS[zone];
  return {
    epsg: 6669 + zone,
    zone,
    a: 6378137.0,
    f: 1.0 / 298.257222101,
    k0: 0.9999,
    lat0: z.lat0 * Math.PI / 180,
    lon0: z.lon0 * Math.PI / 180,
    fe: 0.0,
    fn: 0.0,
    lat0deg: z.lat0,
    lon0deg: z.lon0,
  };
}

function invTM(x, y, p) {
  const a = p.a, f = p.f, k0 = p.k0;
  const e2 = 2*f - f*f;
  const ep2 = e2 / (1 - e2);

  const x1 = (x - p.fe) / k0;
  const y1 = (y - p.fn) / k0;

  const A0 = 1 - e2/4 - 3*e2*e2/64 - 5*e2*e2*e2/256;
  const A2 = 3*e2/8 + 3*e2*e2/32 + 45*e2*e2*e2/1024;
  const A4 = 15*e2*e2/256 + 45*e2*e2*e2/1024;
  const A6 = 35*e2*e2*e2/3072;

  function meridional(phi) {
    return a*(A0*phi - A2*Math.sin(2*phi) + A4*Math.sin(4*phi) - A6*Math.sin(6*phi));
  }

  const M0 = meridional(p.lat0);
  const M  = M0 + y1;
  const mu = M / (a*A0);

  const e1 = (1 - Math.sqrt(1-e2)) / (1 + Math.sqrt(1-e2));
  const J1 = 3*e1/2 - 27*Math.pow(e1,3)/32;
  const J2 = 21*e1*e1/16 - 55*Math.pow(e1,4)/32;
  const J3 = 151*Math.pow(e1,3)/96;
  const J4 = 1097*Math.pow(e1,4)/512;

  const fp = mu + J1*Math.sin(2*mu) + J2*Math.sin(4*mu) + J3*Math.sin(6*mu) + J4*Math.sin(8*mu);

  const sinfp = Math.sin(fp), cosfp = Math.cos(fp), tanfp = Math.tan(fp);

  const N1 = a / Math.sqrt(1 - e2*sinfp*sinfp);
  const R1 = a*(1-e2) / Math.pow(1 - e2*sinfp*sinfp, 1.5);
  const T1 = tanfp*tanfp;
  const C1 = ep2*cosfp*cosfp;
  const D  = x1 / N1;

  const Q1 = N1*tanfp / R1;
  const Q2 = (D*D)/2;
  const Q3 = (5 + 3*T1 + 10*C1 - 4*C1*C1 - 9*ep2) * Math.pow(D,4)/24;
  const Q4 = (61 + 90*T1 + 298*C1 + 45*T1*T1 - 252*ep2 - 3*C1*C1) * Math.pow(D,6)/720;

  const lat = fp - Q1*(Q2 - Q3 + Q4);

  const Q5 = D;
  const Q6 = (1 + 2*T1 + C1) * Math.pow(D,3)/6;
  const Q7 = (5 - 2*C1 + 28*T1 - 3*C1*C1 + 8*ep2 + 24*T1*T1) * Math.pow(D,5)/120;

  const lon = p.lon0 + (Q5 - Q6 + Q7) / cosfp;

  return {lat, lon};
}

function median(arr) {
  if (!arr.length) return NaN;
  const a = [...arr].sort((x, y) => x - y);
  const mid = Math.floor(a.length / 2);
  return (a.length % 2) ? a[mid] : (a[mid-1] + a[mid]) / 2;
}

function parseHint() {
  const s = (document.getElementById("hintLonLat").value || "").trim();
  const m = s.split(",").map(x => x.trim()).filter(Boolean);
  if (m.length !== 2) return {hintLon: 139.5, hintLat: 35.6};
  const hintLon = parseFloat(m[0]);
  const hintLat = parseFloat(m[1]);
  if (!Number.isFinite(hintLon) || !Number.isFinite(hintLat)) {
    return {hintLon: 139.5, hintLat: 35.6};
  }
  return {hintLon, hintLat};
}

function inferZonesByHint(sampleXY, hintLon, hintLat) {
  const results = [];
  for (let zone = 1; zone <= 19; zone++) {
    const p = tmParamsFromZone(zone);
    const lons = [];
    const lats = [];

    for (const [x, y] of sampleXY) {
      const ll = invTM(x, y, p);
      const lon = ll.lon * 180 / Math.PI;
      const lat = ll.lat * 180 / Math.PI;
      if (Number.isFinite(lon) && Number.isFinite(lat)) {
        lons.push(lon);
        lats.push(lat);
      }
    }

    const mlon = median(lons);
    const mlat = median(lats);
    const dLon = (mlon - hintLon) * Math.cos(hintLat * Math.PI / 180);
    const dLat = mlat - hintLat;
    const dist = Math.sqrt(dLon*dLon + dLat*dLat);
    const inJapan = (mlon >= 120 && mlon <= 155 && mlat >= 20 && mlat <= 47);
    const score = dist + (inJapan ? 0 : 999);

    results.push({zone, epsg: p.epsg, mlon, mlat, dist, score});
  }
  results.sort((a, b) => a.score - b.score);
  return results;
}

async function parseNetworkStream(file) {
  const nodes = new Map();
  const links = new Map();
  const sampleXY = [];
  let crsText = "";
  let buf = "";
  let nodeCount = 0;
  let linkCount = 0;
  let headerChecked = false;

  for await (const chunk of ungzipTextChunks(file)) {
    if (!headerChecked) {
      const head = (buf + chunk).slice(0, 200000);
      const m = /coordinateReferenceSystem[^>]*>([^<]+)</i.exec(head);
      if (m) crsText = m[1].trim();
      headerChecked = true;
    }

    buf += chunk;

    while (true) {
      const iNode = buf.indexOf("<node ");
      const iLink = buf.indexOf("<link ");
      let i = -1, kind = "";

      if (iNode === -1 && iLink === -1) break;
      if (iNode !== -1 && (iLink === -1 || iNode < iLink)) {
        i = iNode;
        kind = "node";
      } else {
        i = iLink;
        kind = "link";
      }

      const j = buf.indexOf(">", i);
      if (j === -1) break;

      const tagText = buf.slice(i, j + 1);
      buf = buf.slice(j + 1);

      const a = parseAttrs(tagText);

      if (kind === "node") {
        const id = a.id;
        const x = parseFloat(a.x);
        const y = parseFloat(a.y);
        if (id && Number.isFinite(x) && Number.isFinite(y)) {
          nodes.set(id, [x, y]);
          nodeCount++;
          if (sampleXY.length < 300) sampleXY.push([x, y]);
        }
      } else {
        const id = a.id;
        const from = a.from;
        const to = a.to;
        if (id && from && to) {
          links.set(id, [from, to]);
          linkCount++;
        }
      }
    }

    if ((nodeCount + linkCount) % 20000 === 0 && (nodeCount + linkCount) > 0) {
      setStatus("network解析中… nodes=" + nodeCount + " links=" + linkCount);
      await new Promise(r => setTimeout(r, 0));
    }
  }

  return {nodes, links, crsText, sampleXY};
}

function colorByType(t) {
  if (t === 1) return "#e74c3c";
  if (t === 2) return "#3498db";
  if (t === 3) return "#2ecc71";
  if (t === 4) return "#f1c40f";
  return "#666";
}

function typeFromPersonId(pid) {
  let h = 0;
  for (let i = 0; i < pid.length; i++) {
    h = (h * 31 + pid.charCodeAt(i)) >>> 0;
  }
  return (h % 4) + 1;
}

async function parseEventsToFrames(file, network, zone, wantTypes) {
  const tmParams = tmParamsFromZone(zone);
  const veh2person = new Map();
  const framesMap = new Map();
  const preview = [];
  const firstEvents = [];
  const seenTypes = new Map();

  let carry = "";
  let matched = 0;
  let skippedType = 0;
  let skippedNoLink = 0;
  let skippedNoXY = 0;
  let skippedNoPerson = 0;

  for await (const chunk of ungzipTextChunks(file)) {
    let text = carry + chunk;
    carry = "";

    while (true) {
      const i = text.indexOf("<event ");
      if (i === -1) {
        carry = text;
        break;
      }

      const j = text.indexOf("/>", i);
      if (j === -1) {
        carry = text.slice(i);
        break;
      }

      const tagText = text.slice(i, j + 2);
      text = text.slice(j + 2);

      const a = parseAttrs(tagText);

      if (firstEvents.length < 20) {
        firstEvents.push(JSON.stringify(a));
      }

      const rawType = a.type || "";
      const type = normalizeTypeName(rawType);
      seenTypes.set(rawType, (seenTypes.get(rawType) || 0) + 1);

      if (wantTypes.size && !wantTypes.has(type)) {
        skippedType++;
        continue;
      }

      if (type === "personentersvehicle") {
        const p = a.person;
        const v = a.vehicle;
        if (p && v) veh2person.set(v, p);
        continue;
      }

      let pid = a.person || "";
      if (!pid) {
        const v = a.vehicle || "";
        if (v && veh2person.has(v)) pid = veh2person.get(v);
      }
      if (!pid) {
        skippedNoPerson++;
        continue;
      }

      let x = null;
      let y = null;

      if (a.x !== undefined && a.y !== undefined) {
        x = parseFloat(a.x);
        y = parseFloat(a.y);
        if (!Number.isFinite(x) || !Number.isFinite(y)) {
          skippedNoXY++;
          continue;
        }
      } else if (a.link) {
        const lt = network.links.get(a.link);
        if (!lt) {
          skippedNoXY++;
          continue;
        }

        const p1 = network.nodes.get(lt[0]);
        const p2 = network.nodes.get(lt[1]);
        if (!p1 || !p2) {
          skippedNoXY++;
          continue;
        }

        x = (p1[0] + p2[0]) / 2.0;
        y = (p1[1] + p2[1]) / 2.0;
      } else {
        skippedNoLink++;
        continue;
      }

      const ll = invTM(x, y, tmParams);
      const lon = ll.lon * 180 / Math.PI;
      const lat = ll.lat * 180 / Math.PI;

      if (!Number.isFinite(lon) || !Number.isFinite(lat)) {
        skippedNoXY++;
        continue;
      }

      const sec = Math.floor(parseFloat(a.time || "0"));
      const typeId = typeFromPersonId(pid);

      if (!framesMap.has(sec)) framesMap.set(sec, []);
      framesMap.get(sec).push({
        time: sec,
        person: pid,
        lat: lat,
        lng: lon,
        eventType: rawType,
        typeId: typeId,
      });

      if (preview.length < 80) {
        preview.push(
          sec + "\t" + pid + "\t" + lat.toFixed(6) + "\t" + lon.toFixed(6) + "\t" + rawType + "\tType=" + typeId
        );
      }

      matched++;
      if (matched % 10000 === 0) {
        setStatus(
          "events解析中… matched=" + matched +
          "\nzone=" + zone + " EPSG:" + (6669 + zone) +
          "\nskip type=" + skippedType +
          " noLink=" + skippedNoLink +
          " noXY=" + skippedNoXY +
          " noPerson=" + skippedNoPerson
        );
        setPreview(preview.join("\n"));
        await new Promise(r => setTimeout(r, 0));
      }
    }
  }

  const times = Array.from(framesMap.keys()).sort((a, b) => a - b);
  const frames = times.map(sec => ({
    time: sec,
    items: framesMap.get(sec),
  }));

  return {
    frames,
    matched,
    skippedType,
    skippedNoLink,
    skippedNoXY,
    skippedNoPerson,
    previewText: preview.join("\n"),
    firstEvents: firstEvents,
    seenTypes: Array.from(seenTypes.entries()).sort((a, b) => b[1] - a[1])
  };
}

function secToHHMMSS(sec) {
  const h = Math.floor(sec / 3600);
  const m = Math.floor((sec % 3600) / 60);
  const s = sec % 60;
  return String(h).padStart(2, "0") + ":" +
         String(m).padStart(2, "0") + ":" +
         String(s).padStart(2, "0");
}

function applyFrame(frame) {
  for (const item of frame.items) {
    let marker = personMarkers.get(item.person);
    if (!marker) {
      marker = L.circleMarker([item.lat, item.lng], {
        radius: 5,
        color: colorByType(item.typeId),
        fillColor: colorByType(item.typeId),
        fillOpacity: 0.9,
        weight: 1
      }).addTo(map);
      marker.bindTooltip(item.person, {direction:"top"});
      personMarkers.set(item.person, marker);
    } else {
      marker.setLatLng([item.lat, item.lng]);
      marker.setStyle({
        color: colorByType(item.typeId),
        fillColor: colorByType(item.typeId)
      });
    }

    personState.set(item.person, {
      lat: item.lat,
      lng: item.lng,
      typeId: item.typeId,
      eventType: item.eventType
    });
  }

  timeText.textContent = secToHHMMSS(frame.time);
  agentCount.textContent = String(personMarkers.size);
}

function fitToCurrentMarkers() {
  if (personMarkers.size === 0) return;
  const arr = [];
  for (const m of personMarkers.values()) {
    arr.push(m.getLatLng());
  }
  const bounds = L.latLngBounds(arr);
  map.fitBounds(bounds.pad(0.2));
}

function stopPlayback() {
  if (timer) {
    clearInterval(timer);
    timer = null;
  }
}

prepareBtn.onclick = async function() {
  try {
    prepareBtn.disabled = true;
    playBtn.disabled = true;
    pauseBtn.disabled = true;
    resetBtn.disabled = true;
    stopPlayback();
    clearMarkers();
    currentFrameIndex = 0;
    preparedFrames = [];

    setStatus("network解析開始…");
    parsedNetwork = await parseNetworkStream(networkFile);

    const hint = parseHint();
    const inferred = inferZonesByHint(parsedNetwork.sampleXY, hint.hintLon, hint.hintLat);
    const defaultZone = inferred[0]?.zone || 9;

    zoneSelect.innerHTML = "";
    for (let z = 1; z <= 19; z++) {
      const opt = document.createElement("option");
      opt.value = String(z);
      opt.textContent = "第" + z + "系 (EPSG:" + (6669 + z) + ")";
      if (z === defaultZone) opt.selected = true;
      zoneSelect.appendChild(opt);
    }
    zoneSelect.disabled = false;

    const zoneMsg = inferred.slice(0, 5).map(r =>
      "zone" + r.zone + " median=(" + r.mlon.toFixed(5) + "," + r.mlat.toFixed(5) + ") dist=" + r.dist.toFixed(3)
    ).join("\n");

    setStatus(
      "network解析完了\n" +
      "nodes=" + parsedNetwork.nodes.size + " links=" + parsedNetwork.links.size + "\n" +
      "network記載CRS=" + (parsedNetwork.crsText || "(unknown)") + "\n\n" +
      "候補:\n" + zoneMsg + "\n\n" +
      "events解析を開始してください"
    );

    const typeStr = document.getElementById("types").value.trim();
    const wantTypes = new Set(
      typeStr
        ? typeStr.split(",").map(s => normalizeTypeName(s)).filter(Boolean)
        : []
    );

    const zone = parseInt(zoneSelect.value, 10) || defaultZone;

    setStatus("events解析中… zone=" + zone + " EPSG:" + (6669 + zone));
    const result = await parseEventsToFrames(eventsFile, parsedNetwork, zone, wantTypes);

    preparedFrames = result.frames;
    setPreview(result.previewText);

    const seenTypeText = result.seenTypes
      .map(([name, cnt]) => name + "=" + cnt)
      .join("\n");

    setStatus(
      "解析完了\n" +
      "frames=" + preparedFrames.length + "\n" +
      "matched=" + result.matched + "\n" +
      "skip type=" + result.skippedType +
      " noLink=" + result.skippedNoLink +
      " noXY=" + result.skippedNoXY +
      " noPerson=" + result.skippedNoPerson + "\n\n" +
      "events内のtype一覧:\n" + seenTypeText + "\n\n" +
      "先頭event例:\n" + result.firstEvents.join("\n")
    );

    if (preparedFrames.length > 0) {
      applyFrame(preparedFrames[0]);
      fitToCurrentMarkers();
    }

    playBtn.disabled = false;
    pauseBtn.disabled = false;
    resetBtn.disabled = false;
  } catch (e) {
    setStatus("エラー: " + (e?.message || e));
  } finally {
    prepareBtn.disabled = false;
  }
};

playBtn.onclick = function() {
  if (!preparedFrames.length) return;
  stopPlayback();

  const speed = parseFloat(document.getElementById("speed").value || "20");
  const intervalMs = 100;

  timer = setInterval(() => {
    if (currentFrameIndex >= preparedFrames.length) {
      stopPlayback();
      return;
    }

    const frame = preparedFrames[currentFrameIndex];
    applyFrame(frame);

    const nextIndex = currentFrameIndex + 1;
    if (nextIndex < preparedFrames.length) {
      const dt = preparedFrames[nextIndex].time - frame.time;
      const step = Math.max(1, Math.floor(Math.max(1, dt) / Math.max(0.1, speed)));
      currentFrameIndex += Math.max(1, step);
    } else {
      currentFrameIndex++;
    }
  }, intervalMs);
};

pauseBtn.onclick = function() {
  stopPlayback();
};

resetBtn.onclick = function() {
  stopPlayback();
  clearMarkers();
  currentFrameIndex = 0;
  if (preparedFrames.length > 0) {
    applyFrame(preparedFrames[0]);
    fitToCurrentMarkers();
  }
};
</script>
</body>
</html>
`))

2026,江端さんの忘備録

本日は、朝からグッタリしています。昨夜の悪夢で、相当に疲れてしまったからです。
I have been completely exhausted since this morning, because last night’s nightmare drained me considerably.

私が、早朝覚醒型の不眠症であることは、これまでも何度かお話してきました。そして、この対応策として「自律神経訓練法」と、精神安定剤(セルシン)の投与で対応しているというお話もしました。
I have mentioned several times before that I suffer from early-morning awakening insomnia. As a countermeasure, I have been using “autogenic training” and taking a tranquilizer (Cercine).

セルシンの投与が、悪夢を誘発しやすくなることは、こちらにも書きました。
I have also written that taking Cercine tends to make nightmares more likely.

『「薬剤(特にベンゾジアゼピン系)の副作用として「生々しい夢」や「悪夢の頻発」が報告されており、医学的にも無矛盾」となる理由を教えて下さい』とChatGPTに聞いてみた件

ただ、今回の悪夢は、セルシンの摂取前に見たもので、しかも、寝入り端の2時間ほどで発生しました。
However, this particular nightmare occurred before I had taken Cercine, and it appeared about two hours after I had fallen asleep.

---

昨夜、就寝前に、現在の連載の初回をレビューしていたのですが、どうやら、それが、悪夢を誘引させてしまったようです。
Last night, before going to bed, I was reviewing the first installment of my current series, and apparently that ended up triggering the nightmare.

(↑Jump to the column)

『"動かないシステム"というのは、本当に怖い』
“A system that doesn’t run is truly frightening.”

そして、
And,

『今まで動いていたシステムに、ちょっとした変更を加えたら、動かなくなることは死ぬほど恐しい』
“If you make a small change to a system that has been working and it suddenly stops running, that is terrifying beyond words.”

さらに、
Furthermore,

『元の状態に戻しても、再起動しないシステムというのは、PTSD(心的外傷後ストレス障害)になるレベルの恐怖である』
“A system that refuses to reboot even after reverting to the original state is frightening enough to cause PTSD (Post-Traumatic Stress Disorder).”

という、システムエンジニアの三大悪夢がフラッシュバック(再体験)するのは ―― 心底疲れるものです。
Having the three great nightmares of system engineers flash back (re-experienced) like that is utterly exhausting.

---

いや、PTSDという言葉を、こんなことで使うのは適切ではないのかもしれません。
Well, perhaps it is not appropriate to use the word PTSD in a situation like this.

PTSD(心的外傷後ストレス障害)とは、交通事故や災害、戦争、暴力など、強い恐怖や衝撃を伴う出来事を体験した後、その影響が長く続き、日常生活に支障をきたす精神的な状態のことです。
PTSD (Post-Traumatic Stress Disorder) refers to a psychological condition in which the effects of a traumatic event, such as a traffic accident, disaster, war, or violence, persist for a long time and interfere with daily life.

システムの停止程度のことで、この言葉を使うのは、やはり大袈裟でしょう。
Using that term for something like a system outage is, admittedly, an exaggeration.

それでも、システム開発、特にバックエンドを担当するエンジニアにとって、システム停止の恐怖は、かなり本気で胃に来るものなのです。
Even so, for engineers involved in system development?especially those responsible for the backend, the fear of a system failure genuinely hits the stomach.

たった1人で作っているようなシステム(またはサブシステム)の開発の場合は、特にそうです。
This is especially true when developing a system (or subsystem) built by a single person.

―― 他人のせいにできないシステム開発は、悪夢
"System development where you cannot blame anyone else is a nightmare."

です。
That is the reality.

こういうのは、たいていの場合、ファームウェアやミドルウェア、またはOSが悪さをしていることがあり、そのエンジニア一人が責を負わされるものではないはずなのですが、そういう理屈が"通じない"のが、この世界のしきたりです。
In many cases, the real culprit may be firmware, middleware, or the OS, and it should not be that engineer's sole responsibility. However, such reasoning rarely works in this world.

「他人が作ったモノに問題があれば、それを見付け出す。見つけ出した上で、その問題を回避する手段を考える」までが、『ワンオペエンジニア』の宿命です。
If there is a problem in something created by someone else, you must find it, and once you find it, you must devise a way to work around it. That is the fate of a “one-operator engineer.”

---

私、今期の仕事(今期の仕事も)、しんどかったです。
My work this term (and this term as well) has been quite exhausting.

- 当初の私の仮説を裏切る、膨大な計算結果の数々
- A vast number of computational results that betrayed my initial hypothesis

- 商用クラウドを使ったファイアウォールシステムの実装(×提案)
- Implementing (not merely proposing) a firewall system using a commercial cloud

『実装は研究員の仕事ではない』というのは、私の信条に反します。私の中にいる、エンジニアの私が私にこう言うからです。
The idea that “implementation is not a researcher’s job” runs counter to my beliefs. The engineer inside me says this to me.

―― お前、実際に動かすこともできないシステムを"提案"だけして、逃亡するのか?
"Are you going to propose a system you cannot actually run, and then run away?"

と。
That is what it says.

いや、「私の中のエンジニア」が言わなくても、研究依頼元や上司がそう言いますけどね。
Even if the engineer inside me did not say it, the client or my supervisor probably would.

だから、まあ、プロトタイプシステムでの稼動が確認できた時は ――― もう、そりゃあ『ヨッシャーーーー!』と、一人部屋の中で、拳を振り上げて、雄叫びを上げることになります。
So when the prototype system finally confirms operation, I end up shouting “YESSSSS!” alone in my room, raising my fist and roaring in triumph.

比して、稼動が確認できない日々の連続は、陰鬱とした時間が続くことになります。
In contrast, days when the system refuses to run are filled with gloom.

---

それに比べて、特許明細書や報告書やプレゼンテーションや論文は、(教授や上司や関連部門からツッコミは受けますが)、いつかは終了します、が、
Compared to that, patent specifications, reports, presentations, and papers, although they receive comments from professors, supervisors, and related departments, are not. will eventually come to an end.

―― 動かないシステムは、動かすまでは終わらない
"A system that does not run will not end until it runs."

これが、ドキュメンテーションとシステム開発の決定的な違いです。
That is the decisive difference between documentation work and system development.

どちらが良いとか悪いではないのですが、私の立場としては、『動かないシステム』に心を削られる日々に比べれば、100ページのドラフト作成などは、恐怖の対象ではありません ―― だって、睡眠時間を削れば、いつかは終わるのですから。
Neither is inherently better or worse, but from my perspective, compared to the days when a non-working system gnaws at my nerves, writing a 100-page draft is not something to fear because if I sacrifice enough sleep, it will eventually be finished.

---

いや、こういう言い方は、卑怯(我田引水)だな。
Well, putting it that way is a bit unfair (self-serving).

同じように「終わるまで終われない」仕事は、世の中にいくらでもあります。
Many jobs in this world do not end until they are finished.

- 営業はノルマが果たされなければ終わらない
- Salespeople cannot finish until their quotas are met.

- 作家は原稿を書き上げなければ終わらない
- Writers cannot finish until the manuscript is written.

- 裁判官は判決を書かなければ終わらない
- Judges cannot finish until the verdict is written.

- 農家は収穫しなければ終わらない
- Farmers cannot finish until the harvest is completed.

- 職人は品物を完成させなければ終わらない
- Craftsmen cannot finish until the product is completed.

これらの仕事も、「途中までがんばった」ということには、ほとんど価値が認められない種類の業務でしょう。
These jobs, too, are the kind where “I tried hard halfway through” is rarely considered valuable.

そういえば、
Come to think of it,

- 育児は子どもが自力でメシを食えるようになるまでは終わらない
- Parenting does not end until the child can feed themselves.

というのも、ここに加えてよいかもしれません。
That may also deserve to be added to the list.

---

動かないシステムは、動くまで終わらない。
A system that does not run does not end until it runs.

そして、動かない理由は、たいてい、すぐには分からない。
And the reason it does not run is usually not immediately clear.

しかも、その理由が自分のコードなのか、OSなのか、ミドルウェアなのか、ファームウェアなのか、あるいは宇宙線なのか―― それを切り分けるのも、『ワンオペエンジニア』です。
Moreover, determining whether the cause lies in your code, the OS, the middleware, the firmware, or perhaps even cosmic rays also falls to the “one-operator engineer.”

そういう仕事をしている人間が、夜中にシステムの停止を夢に見るというのは、まあ、自然なことなのでしょう。
So it may be only natural that someone doing such work dreams about system failures in the middle of the night.

……とはいえ、昨夜の夢は、ちょっと酷かった。
…Even so, last night’s dream was rather brutal.

悪夢で一度目が覚めて、『ああ、夢だった。よかった』と思って再び眠りに落ちたのですが、昨夜の悪夢は、悪夢のまま無矛盾のまま継続したのです。
I woke up once from the nightmare and thought, “Ah, it was just a dream. Thank goodness,” and fell asleep again, but the nightmare resumed seamlessly, without contradiction.

―― 悪夢のインターミッション(休憩)
"An intermission in a nightmare."

いや、本当に、悪夢にそんなサービスはいらないです。
Honestly, nightmares do not need such a service.

今朝の悪夢は、もうメチャクチャです。

未分類

参政党は外国人の何を問題としているのですか?

参政党が「外国人問題」として主張している内容は、いくつかの論点に分かれている。大きく整理すると、次の4つである。


1. 外国人労働者の増加

参政党が最も強く問題視しているのは、外国人労働者の受け入れ拡大である。

主張の骨子は次の通り。

  • 日本人の賃金が下がる可能性
  • 日本人の雇用機会が減る可能性
  • 技能実習・特定技能制度が「実質的移民政策」になっている
  • 低賃金労働力として利用されている

つまり
「日本は移民国家ではないのに、事実上の移民政策になっている」
という批判である。


2. 外国人による土地・不動産取得

参政党は、外国人(特に海外資本)が日本の土地を取得する問題も指摘している。

例として挙げられるのは

  • 水源地周辺の土地取得
  • 自衛隊基地周辺
  • 農地の取得

主張は

「安全保障の観点から規制が必要」

というもの。

これは参政党だけでなく、
自民党の一部や安全保障関係者も問題提起している論点である。


3. 社会保障・生活保護

外国人に対する社会保障の扱いについても問題提起している。

主張の例:

  • 外国人への生活保護は見直すべき
  • 社会保障は日本国民を優先すべき

ただし実際には

  • 外国人生活保護受給率は日本人より低い
  • 法的には「生活保護法対象ではなく行政措置」

という複雑な制度になっている。


4. 文化・社会秩序の問題

もう一つの主張は

文化的摩擦

である。

例として挙げられるのは

  • 地域コミュニティとの摩擦
  • ゴミ出し・生活習慣の違い
  • 学校教育の問題

この点について参政党は

「日本文化を守るべき」

という主張をしている。


まとめ(参政党の問題設定)

参政党の主張を整理すると、外国人そのものではなく

①移民政策化
②安全保障
③社会保障
④文化摩擦

の4点を問題にしている構図である。

ただし重要なのは、
これらの論点の中には

  • 事実として存在する政策問題
  • 誇張されている可能性のある問題

が混在していることである。


もし希望があれば、さらに踏み込んで

  • 参政党の外国人政策の実際の内容(公約)
  • なぜこのテーマが支持を集めているのか
  • データで見る外国人問題の実態

なども解説できる。
実は、このテーマは日本政治の構造をかなりよく表している。

参政党の主張に対して、ファクトチェックをお願いします。

参政党が主張している「外国人問題」について、主要な論点ごとに事実関係(統計・制度)と主張との関係を整理する。ここでは評価ではなく、公開データに基づくファクトチェックとして説明する。


1 外国人労働者増加は「移民政策」なのか

参政党の主張

「日本は移民政策を取っていないと言いながら、実質的には移民政策になっている」

事実

日本の外国人労働者数は確かに増えている。

  • 外国人労働者数
    • 2013年:約72万人
    • 2023年:約204万人

つまり約10年で約3倍になっている。

これは主に

  • 技能実習制度
  • 特定技能制度(2019開始)

によるものである。

ファクト評価

この点については

主張は概ね事実に基づいている

理由
政府自身が

「移民政策ではない」

と説明している一方で、

  • 長期滞在
  • 家族帯同(特定技能2号)

が可能になっており、

多くの研究者が

「実質的には移民政策に近い」

と指摘している。

ただし

外国人比率は

  • 日本:2.7%
  • ドイツ:約18%
  • フランス:約13%

であり、国際比較ではまだ低い


2 外国人による土地取得問題

参政党の主張

外国人が日本の土地(水源地・基地周辺)を買っている。

事実

外国人による土地購入は可能である。
日本には国籍による土地購入規制がほぼ存在しない。

ただし統計を見ると

外国人所有土地の割合は

全国で1%未満と推定されている。

また問題視されることの多い

「水源地買収」

については

具体的な被害事例はほとんど確認されていない。

ただし安全保障上の懸念から

2021年に

重要土地等調査法

が成立し

  • 自衛隊基地
  • 原発
  • 国境離島

周辺の土地利用を調査できるようになった。

ファクト評価

  • 外国人が土地を購入できる → 事実
  • それが広範な問題になっている → 証拠は限定的

つまり

制度的問題はあるが、被害規模は不明確

という状態である。


3 外国人の生活保護

参政党の主張

外国人が生活保護を受けている。

事実

外国人の生活保護受給は

法律上の権利ではない。

しかし

1954年の厚生省通知により

「人道的措置」

として支給されている。

受給者数

  • 外国人世帯:約4.7万世帯
  • 全体:約164万世帯

割合

約2〜3%

である。

外国人の人口割合は

約2.7%なので

人口比とほぼ同程度

である。

ファクト評価

  • 外国人が生活保護を受けている → 事実
  • 外国人が多く受給している → 統計上は確認できない

4 外国人犯罪増加

参政党支持層の議論でよく出る論点。

事実

外国人刑法犯検挙数

  • 2005年:約43,000件
  • 2022年:約10,000件

つまり

大幅に減少している。

また人口比でも

外国人犯罪率は

日本人と大差ない

とされている。

ファクト評価

「外国人犯罪が急増している」

という主張は

統計とは一致しない。


まとめ

参政党の外国人問題について、データとの関係を整理すると次のようになる。

論点 ファクト評価
外国人労働者増加 事実
日本が実質移民化 議論あり
外国人土地取得 制度的には可能
水源地買収問題 根拠は限定的
外国人生活保護 事実だが割合は人口比程度
外国人犯罪増加 データと一致しない

最後に重要なのは、この問題は単純な

「正しい/間違い」

ではなく

①人口減少
②労働力不足
③安全保障
④社会統合

という政策設計の問題であるという点である。


もし興味があれば、かなり面白い分析として

「なぜこのテーマで参政党が支持を伸ばしているのか」

を政治学・社会心理の観点から説明できる。
これは日本の政治構造のかなり核心に触れる話になる。

「なぜこのテーマで参政党が支持を伸ばしているのか」をお願いします

参政党が「外国人問題」を強く掲げることで支持を伸ばしている理由は、単純に政策の中身というより、社会心理・政治構造・メディア環境の3つの要因が重なっていると分析されている。主なポイントを整理する。


1 「不安の受け皿」としてのテーマ

日本社会では近年、次のような構造的な不安が広がっている。

  • 人口減少
  • 地域の衰退
  • 賃金停滞
  • 社会保障への不安
  • 治安や文化の変化への漠然とした不安

こうした不安は本来、

  • 経済政策
  • 産業構造
  • 社会制度

などの複雑な問題に関係している。

しかし人間の心理として、複雑な問題よりも「分かりやすい原因」を求める傾向がある。

そのため政治的には

「外国人問題」

というテーマが

不安の象徴的な対象

として機能しやすい。


2 既存政党が触れにくいテーマ

日本の主要政党(自民・立憲など)は、外国人問題を

  • 人権問題
  • 国際関係
  • 経済政策

とのバランスで扱う必要があるため、比較的慎重な表現を取る。

結果として

問題を率直に語る政治勢力が少ない

という状況が生まれる。

そこに

  • 参政党
  • 一部の保守系政治団体

が入り込み、

「他の政党が言わないことを言う」

という構図が成立する。

これは政治学で

ニッチ政党戦略

と呼ばれる典型的なパターンである。


3 SNS政治との相性

外国人問題は

  • 感情的
  • 分かりやすい
  • 短い言葉で伝えられる

という特徴があり、SNSと非常に相性が良い。

例えば

  • 「日本人が損をしている」
  • 「日本を守れ」

といったメッセージは、短い動画や投稿で拡散しやすい。

近年の日本政治では

SNSを通じて支持を広げる新興政党が増えており、参政党もその典型例とされている。


4 国際的に見ても典型的な現象

この現象は日本だけではない。

欧米でも似たパターンがある。

例:

  • フランス
    • マリーヌ・ル・ペン
  • ドイツ
    • ドイツのための選択肢
  • イタリア
    • ジョルジャ・メローニ

いずれも

  • 移民問題
  • 文化アイデンティティ
  • 国家主権

をテーマに支持を拡大している。

つまり参政党の現象は

世界的な政治トレンドの日本版

とも言える。


まとめ

参政党がこのテーマで支持を伸ばしている理由は主に次の4点である。

  1. 社会的不安を象徴するテーマになりやすい
  2. 既存政党が扱いにくい領域である
  3. SNS政治と相性が良い
  4. 世界的なポピュリズム政治の流れと一致している

もし興味があれば、もう一歩踏み込んだ分析として

「参政党支持層の特徴(年齢・職業・心理)」

も説明できる。
実はここに、日本社会のかなり深い構造が見える。

「参政党支持層の特徴(年齢・職業・心理)」もお願いします。

参政党の支持層については、まだ大規模な学術研究が十分に蓄積されているわけではない。ただし、選挙分析・世論調査・政治学者の観察などから、ある程度共通した傾向が指摘されている。主に「年齢」「職業・社会階層」「心理」の3つの側面で整理できる。


1 年齢層の特徴

既存政党と比べて、参政党支持層はやや特徴的な分布を持つと指摘されている。

比較的多い層:

  • 30〜50代
  • 子育て世代

これは、参政党が初期に

  • 食品安全
  • ワクチン
  • 教育
  • 子どもの健康

といったテーマを強く扱ったことと関係していると考えられる。

一方で

  • 70歳以上の高齢層

は自民党支持が依然として多い傾向がある。


2 職業・社会階層

研究者の分析では、参政党支持者には次のような傾向が見られるとされる。

比較的多いとされる層:

  • 自営業
  • 中小企業経営者
  • フリーランス
  • 会社員(中間管理職層)

共通する特徴は

「既存の大組織に強く依存していない」

ことである。

例えば

  • 公務員
  • 大企業正社員

などは既存政党支持が比較的多い。


3 都市 vs 地方

地域的には

  • 地方都市
  • 郊外地域

で一定の支持が見られる。

これは

  • 地域衰退
  • 人口減少
  • 外国人労働者増加

などを体感しやすい地域であることが関係していると考えられている。


4 心理的特徴(政治心理学)

政治心理学の観点からは、次の特徴が指摘されることが多い。

(1) 既存制度への不信

例:

  • 政府
  • 大企業
  • メディア
  • 医療制度

への不信が比較的強い傾向。

これは

  • コロナ政策
  • ワクチン政策

などをめぐる議論と関係していると分析されている。


(2) 自己決定志向

参政党の支持者は

  • 自分で調べる
  • 自分で判断する

という価値観を重視する傾向があると言われる。

そのため

  • オルタナティブメディア
  • YouTube
  • SNS

から情報を得る割合が高い。


(3) ナショナル・アイデンティティ

政治心理学では

「文化的アイデンティティの保護」

という動機も指摘される。

これは

  • 日本文化
  • 伝統
  • 食の安全
  • 家族観

などを重視する価値観と結びついている。


5 まとめ

現在の研究から見える参政党支持層の典型像をまとめると次のようになる。

項目 傾向
年齢 30〜50代
家族構成 子育て世代
職業 自営業・フリーランス・会社員
地域 地方都市・郊外
心理 既存制度への不信・自己決定志向

ただし重要なのは、

支持層は決して単一ではない

という点である。

  • 保守層
  • オルタナティブ志向層
  • 政治不信層

など、複数のグループが重なっている。


もし希望があれば、もう一歩踏み込んで

「参政党支持者と“陰謀論”の関係(かなり誤解が多い)」

についても、研究結果を踏まえて説明できる。これはかなり興味深いテーマである。

未分類

先程、昼食を作るためにリビングに降りていったところ、嫁さんが、NetFlixでワールドベースボールクラッシック(WBC)を見ていました。

ベネゼエラ v.s. 日本 で、ちょうど、ベネゼエラの選手が逆転ホームランを打ったところでした。

『ベネゼエラって、あのベネゼエラだよなぁ?』

―― と、思わず口に出してしまいました。

---

そういえば、ついこの前までニュースでは、ベネゼエラの大統領が米国の作戦で拘束されたとか、国外に連行されたとかいう話を見ていた気がします。国際政治のニュースでは、かなり物騒な話題として扱われていたはずです。

しかし、テレビの中では、そのベネゼエラの選手が、見事な逆転ホームランを打ってベースを回っています。スタンドは大歓声で、実況も興奮気味です。

政治ニュースで見る国名と、テレビの中でホームランを打っている国名が、頭の中でうまく結びつかないのです。

―― 政教分離ならぬ、政球分離(せいきゅうぶんり)といってもいいのかな

世界情勢とスポーツ中継というのは、どうやら、同じ国名でも、まったく別の世界の話として進行するものらしい、と思いました。

また、WBCとオリンピックやパラリンピックとも、随分な違いがあるのだなぁ、とも。

---

オリンピックでは、例えばロシアのように、国家として問題があると判断された場合、国旗や国名での参加が認められなかったり、場合によっては出場自体が制限されたりします。

ところが、WBCでは、国家元首が国外に連行されるような事件があっても、その国の代表チームは普通に試合をしています。

つまり、

オリンピックは政治問題で選手の扱いが変わることがあるが、WBCでは政治問題があっても試合は普通に続く

という違いがあるようですが ―― なんか違和感がありまして。

とはいえ、政球分離(せいきゅうぶんり)は、正しいと思います。

オリンピックにあっても、政競分離(せいきょうぶんり)(政治(政)と競技(競)の分離)であることが、そもそもオリンピックの精神だったと思いますが、政治とオリンピックがベタベタなのは、今に始まったことではありません。

---

この「政治とオリンピックがベタベタ」という構図は、実は今に始まった話ではありません。

例えば、日本でも経験があります。1980年のモスクワ五輪では、ソ連のアフガニスタン侵攻に対する抗議として、米国主導のボイコットに日本も参加し、結局、日本選手団は出場できませんでした。

その4年後の1984年ロサンゼルス五輪では、今度はソ連や東欧諸国が「安全上の理由」を名目に大会をボイコットしました。

さらに遡れば、1936年のベルリン五輪はナチス政権の国威発揚の舞台として利用されましたし、1968年メキシコ五輪では、アメリカの黒人選手が表彰台で人種差別への抗議を示すブラックパワー・サリュートを行いました。

1972年ミュンヘン五輪では、イスラエル選手団がパレスチナ武装組織に襲撃されるという事件まで起きています。

最近でも、ロシアの国家ドーピング問題で国旗の使用が制限されたり、ロシアのウクライナ侵攻後には参加資格そのものが議論されたりと、政治問題はオリンピックと常に絡み続けています。

つまり、

「政治とスポーツは分けるべきだ」という理念は昔から存在するものの、現実のオリンピックは、ほぼ常に政治と一緒に動いてきた

と言ってしまっても、あまり間違いではない気がします。

―― そう考えると、

WBCで普通に試合が続いている光景の方が、むしろ「本来の政競分離」に近いのかもしれない、などと、ちょっとだけ思いました。

2026,江端さんの忘備録

先日、「下を向いて歩こう」というコラムを掲載しました。
The other day, I published a column titled "Walk Looking Down."

もう一つの『下を向いて歩こう』の意義は、「高い目標を目指して歩む人生は、大体『コケる』」という、私の経験則です。

その話を家族でしていたところ、「『下を向いて歩こう』という生き方は、実際には成立しにくいのではないか」という指摘を受けました。
When I was talking about it with my family, someone pointed out that "a way of life based on ‘walking while looking down’ might actually be difficult to sustain in reality."

嫁さんが独身時代に働いていた銀行のような組織では、同期のライバルと競争しながら昇進を目指すことが当然であり、多くの人が常に"上"を目指して働いている、という経験談です。
She spoke from her experience working at a bank before we married, where it was natural to compete with peers for promotion, and where many people constantly worked aiming "upward."

このような環境では、「上を目指さない」という選択肢が実質的に存在しません。
In such an environment, the option of "not aiming upward" practically does not exist.

上昇を目指さなければ、そのまま留まるのではなく、むしろ"下に落ちる"という感覚になる。つまり、「自分の望む位置で安定して停止する」という状態が成立しないのです。
If one stops aiming upward, it does not mean remaining in place; rather, it feels like "falling downward." In other words, a state in which one can stabilize at a desired position does not exist.

その話を聞いて、
Hearing that,

―― なんという不安定な系だ
"I thought what an unstable system."

と思いました。
That was my reaction.

しかし、他の仕事では、この不安定な系がデフォルトのようです。特に、営業職では、この傾向が顕著で、世界観そのものが「上か下か。勝つか負けるか」によって構成されている場合が多いようです。
However, in many other professions, this unstable system seems to be the default. In particular, in sales jobs, this tendency is pronounced, where the worldview itself is often structured around "up or down, win or lose."

競争を続けること自体が仕事の一部であり、常に順位や成果が比較される環境では、「戦い続けること」が前提となります。
In environments where competition itself is part of the job, rankings and performance are constantly compared, and continuing to fight is taken as a given.

(なお、ここで述べられているのは、いわゆる「昭和的」な企業文化の経験談であり、現在の社会をそのまま正確に反映しているかは不明です)
(Incidentally, this refers to experiences from what might be called "Showa-era corporate culture," and it is unclear whether it accurately reflects present-day society.)

嫁さんは、私の働き方を見て驚いた、と言っていました。
My wife said she was surprised by the way I work.

- 仕事でありながら、「人と戦う」という視点がない
Even though it is work, there is no sense of "fighting other people."

- 仕事が競争の体をなしていない。
The job does not take the form of competition.

- 自分の内で完結しているように見える
It appears to be self-contained within oneself.

ということが、とても奇妙に感じた、とのことでした。
She said these things felt very strange to her.

それを聞いた私は、『それでは、まるで研究職が"ラク"な職業のように見える』と思いました。
Hearing that, I thought, "That would make research jobs look like an easy profession."

本当に、"ラク"なら本当に良いのですが、(他の人は知りませんが)私には全然"ラク"ではありませんでした。
If it really were easy, that would be wonderful, but (I cannot speak for others) it has never been easy for me.

研究職が他の職業より、「下を向いて歩こう」ができるのは否定できませんが、「下を向いて歩こう」は、"ラク"を意味している訳ではないのです。
It may be true that research jobs allow one to "walk looking down" more than other professions, but that does not mean the work is easy.

---

まず、"人"とは戦わないかもしれないが、"未知の課題"とは、四六時中バトルを繰り返しています。
First of all, we may not fight "people," but we battle "unknown problems" around the clock.

つまり、私たちは、『まだ、この世の中に出てきていない問題を掲げて、その解決手段を見つけ出さなければならない』というミッションを与えられています。
In other words, we are given the mission of identifying problems that do not yet exist and finding ways to solve them.

もちろん、この解決手段には、色々(お金とか政治力とか戦争とか)ありますが、私たちは、この手段が『技術しばり』になっています。
Of course, there are many possible ways to solve problems, such as money, political power, or even war, but for us, the constraint is that the solution must be technological.

『技術』というのは、基本的には「誰でもその恩恵を受けられる」という普遍的で公平な手段です。
Technology, in principle, is a universal and fair means that can potentially benefit anyone.

実際は資金力とか特許権とか技術力などがあり、完全に公平に恩恵を受けられる訳ではありませんが ―― 気合と、十分に長い時間を投資すれば、おおむね"公平"を担保できる手段です。
In reality, of course, funding, patents, and technological capability matter, so it is not perfectly fair, but with enough determination and enough time invested, it can roughly maintain fairness.

ところが、この"未知の課題"を解決する手段というのが ―― 恐ろしく難しい。難しいから"未知の課題"になっているのです。
However, finding a way to solve these "unknown problems" is extraordinarily difficult indeed; that difficulty is precisely why they are unknown problems.

それを『なにがなんでも解決しろ。1年以内に』てなこと、普通に言われるのが研究職です ―― 胃が痛いですよ、特に今のような年度末で、研究報告書を書かなければならない時期は、最悪です。
And yet researchers are often told things like, "Solve it no matter what within a year." It is enough to give you stomach pain, especially around the end of the fiscal year when research reports must be written.

ニュースで、桜の開花報告など聞くころになると ―― 気分は『断崖絶壁で、刑事に追いつめられた殺人犯』のような気分です。
By the time the news begins reporting the cherry blossoms blooming, I feel like a criminal cornered by a detective at the edge of a cliff.

---

ただ、"未知の課題"の"技術による解決方法"が『ない』、なんてことは普通にあります。当然です。
However, it is quite common for there to be no technological solution to an unknown problem. That is only natural.

そんなものがあれば、世界中の課題は全部『技術だけ』で解決できるはずです。
If such solutions always existed, every problem in the world could be solved purely by technology.

例えば、どこぞの米国大統領が、一方的に反米の国に軍事力をぶつけたりすることなんて必要ないのです ―― 石油以外のエネルギー生成手段が、世界中のどこでも無尽蔵に産み出せる技術があれば、ホルムズ海峡なんぞ、封鎖どころか、埋め立てして貰っても、全然OKです。
For example, would a U.S. president not need to use military force against anti-American countries unilaterally? If we had technology capable of generating limitless energy everywhere without relying on oil, the Strait of Hormuz being blocked would hardly matter.

そんな時(『ない』時)、研究員はどうすればいいのか?
So what should researchers do when such solutions do not exist?

―― あは。ダメでした。テヘペロ
"Ah, well. It didn’t work. Oops."

なんてことが許されるなら、研究員は世界でもっとも"ラク"な職業となっているはずです。
If that were acceptable, researchers would surely have the easiest job in the world.

違うんですよ。もっと過酷な命題を突き付けられるのです。
But that is not how it works. We are given a far harsher task.

『なぜ、これが技術的に解決できないのかを、論理的に説明せよ』という任務に置き換わるのです。
Instead, the task becomes: "Explain logically why this cannot be solved technologically."

これはどういうことか。
What does that mean?

―― 世界中で、あと1000人の研究員と、1兆円の資金と、100年の期間を投入しても"解決不能である"ことを証明せよ
"Prove that even if 1,000 more researchers, one trillion yen in funding, and a hundred years were invested worldwide, the problem would remain unsolved."

てなことを言われるのです。この、絶望的な証明をするのも、また研究員の仕事なのです。
In effect, that is what we are asked to do. Producing such a hopeless proof is also part of a researcher’s job.

これ、技術的に解決する手段を見つけるよりも難しい。しかし、それができない研究員は、研究員として食っていけないのです。
Sometimes this is even harder than finding a technical solution. Yet a researcher who cannot do this cannot survive.

ね? いやでしょ? こんな仕事。
See? Not a pleasant job, is it?

でも、そうして私たち研究員は日々を凌いで、飯を喰っているわけですよ。
And yet, in this way, we researchers manage to get through our days and earn our living.

で、そのような「絶望的な証明」が結果としては『ウソ』だった、ということも結構あったりするんですよ。
And sometimes those "hopeless proofs" later turn out to be wrong.

私が知る限り2つあります。
I know at least two examples.

太陽光発電(の商用利用)と、(一般人が普通に使えるAI)です。
Commercial solar power generation and AI that ordinary people can use in everyday life.

30年前には、この2つ、絶対に無理だと言われていたのです。
Thirty years ago, both were said to be impossible.

---

当時の議論を思い出すと、太陽光発電については「コストが高すぎる」「発電効率が低すぎる」「面積が必要すぎる」といった理由が並べられていました。
Looking back at the discussions at the time, arguments about solar power often listed reasons such as "the cost is far too high," "the power generation efficiency is too low," and "it requires too much area."

電力を支える基幹エネルギーになるなどという話は、ほとんど誰も真面目には受け取っていなかったと思います。研究としては面白いが、社会の主役になる技術ではない、という位置づけでした。
The idea that it could become a core energy source supporting the electric power system was not taken seriously by almost anyone. It was considered interesting as research, but not a technology that would become a main player in society.

AIも同じです。音声認識も画像認識も、研究室の中では動くけれど、一般の人が日常的に使うものにはならない、と言われていました。
AI was the same. Speech recognition and image recognition worked inside laboratories, but people said they would never become something ordinary people could use in everyday life.

計算量が大きすぎる、データが足りない、精度が実用レベルに届かない、など、もっともらしい理由が山ほど並んでいたのを覚えています。
I remember many plausible reasons being listed: the computational requirements were too large, the data was insufficient, and the accuracy did not reach practical levels.

ところが、結果としてどうなったか。
And yet, what happened in the end?

---

太陽光発電は、いつの間にか電力システムの一角を担う技術になりました。効率は上がり、コストは下がり、世界中の屋根や空き地にパネルが並ぶ時代になりました。
Solar power quietly became one of the technologies supporting the power system. Efficiency improved, costs fell, and we entered an era in which panels appear on rooftops and on open land across the world.

AIについては、もはや説明するまでもありません。いまや一般の人が、日常的にAIを使って文章を書いたり、翻訳したり、画像を作ったりする時代です。
As for AI, it hardly needs explanation anymore. We now live in a time when ordinary people routinely use AI to write text, translate languages, and create images.

つまり、「技術的に無理だ」と言われていたことが、ある日突然、当たり前の技術になってしまうことがあるのです。
In other words, things once deemed "technically impossible" can suddenly become ordinary technologies.

もちろん、研究員が「これは無理です」と説明したとき、それが間違いだったというわけではありません。その時点での技術水準、コスト、社会条件を前提にすれば、確かに『無理』だったのです。
Of course, when researchers said "this is impossible," it does not mean they were wrong. Given the technological level, costs, and social conditions at that time, it truly was impossible.

しかし技術というのは、ある時点までは全く動かないように見えて、ある閾値を越えると急に状況が変わることがあります。
However, technology sometimes appears stagnant for a long time, and then suddenly the situation changes once a certain threshold is crossed.

材料、製造技術、計算能力、社会制度など、いくつもの条件が重なった瞬間に、突然「できる技術」になってしまうのです。
At the moment when various conditions, materials, manufacturing technology, computational power, and social systems come together, something can suddenly become a "possible technology."

---

ですから、研究員が「技術的に解決できない」と説明するとき、それは「未来永劫不可能」という意味ではありません。「少なくとも、現在の技術体系の延長線上では、解決の見通しが立たない」という意味なのです。
Therefore, when researchers say that something "cannot be solved technologically," it does not mean it is impossible forever. It means that, at least within the current technological framework, there is no foreseeable solution.

そして、研究員にとって厄介なのは、その「延長線上では無理」という判断が、いつ覆るか分からないということです。
And what makes this troublesome for researchers is that no one knows when that judgment, "impossible along the current trajectory," will be made. may be overturned.

つまり私たちは、『解決できるかもしれない問題』と『本当に解決できない問題』の境界線の上で、ずっと作業を続けているようなものです。
In other words, we continue working along the boundary between "problems that might be solvable" and "problems that truly cannot be solved."

この境界線は、霧の中にあります。どこまでが可能で、どこからが不可能なのか、はっきり見えません。
This boundary lies in the fog. It is impossible to see where the possible ends and the impossible begins clearly.

だから研究員は、今日もまた「未知の課題」に向かって、下を向いて歩き続けるしかないのです。
That is why researchers can only keep walking forward, looking down, toward unknown problems.

---

まあ、今日のコラムで、あなたにご理解頂きたいことは、
What I would like you to understand from today’s column is this:

―― 「下を向いて歩こう」は、"ラク"を意味していることではないよ
"Walking while looking down" does not mean the work is easy.

ということなのです。
That is all I wanted to say.

つまり、私は「全力疾走」をしていて「普通」なのです。