菊池律です。

三日坊主にならないように、三日毎に書きます。

他人と比較すること無く自分の好きなことをコツコツ続けることが大事だなと思いました(2023年まとめ)

2023年をレビュします
今年は色々始めることが出来て、非常に良い1年だったなあと思います。
とても楽しいです。

レビュー

1月~3月

ずっとプロスピをやったり好きなコンテンツを追いかけて凡な高校生活を送っていました。

このときに初めて声出し有りのライブに参加しました。
虹ヶ咲のユニットライブ、開催前の下馬評では相対的にQU4RTZのライブが一番ハズレっぽかったのですがちょ~~~~~~~~~~~楽しかったです。
ところがどっこい、なんだか照れくさいところもあって心の底から盛り上がることが出来なかったのが悔しいです。
何かイベントに参加する時はたとえ照れくさくても心の底から声を出して盛り上がろうと決意しました。
ライブは高いし移動が面倒くさいけど楽しいので積極的に参加していきたいです。

4月~6月

大学に入学し、一人暮らしを始めました。
生成AIが面白いな~と思ってPythonの勉強を始めました。Google ColabでStable diffusionを動かしたりして遊んでいただけでしたが、とても楽しかったです。
セキュリティ・キャンプの存在を知り、ちょうどAIセキュリティクラスというものがあるっぽいので応募してみました。知識も経験もない状態でダラダラと書いた応募課題が通ってくれて嬉しかったです。自分が参加したクラスの応募課題は技術的な内容を問われることが少なかったのですがチューターさんの方曰く、セキュリティ・キャンプは色々なバックグラウンドの方を取っているみたいなことを言っていた気がするので、なるほどと思ったりしていました。

7月~9月

セキュリティ・キャンプに参加しました。
参加するまでの間にセキュリティ・キャンプについて色々調べていたので、ある程度どんなものか想像はついていたのですが実際参加してみると非常に衝撃的でした。
自分と年齢近め、もしくは高校生くらいの若い方が高い技術力を有していて、度肝を抜かれ戦々恐々とし開いた口が塞がらず唖然とし、呆気に取られ口があんぐりになっていました。
自分はちょうどその頃Atcoderにドはまりしていたのですが高校生で暖色や青色の人が実在していることに本当に驚きました。
熱心に研究に取り組んでいたり、AWSや自作サーバーで色々やってる方が居て今までそういう世界に触れることはなかったので本当にこんな世界があるんだ...と感動しました。
講義はもちろんのこと、それ以上に色々な人と出会えたことで刺激をもらえたのが一番大きかったと思います。
他人と比較してしまうと自分はとんでもない矮小な存在になってしまうので、自分の好きなことをとことんやり続けたいなという価値観になりました。

10月~12月

LLMサマースクールを修了しました。受講前のイメージとは違い、めちゃくちゃ数理的なことをやっていたのでどひゃあとなりました。1冊自然言語処理の本を買って色々やっていました。
www.ohmsha.co.jp
面白かったです。おすすめです。
11月は色々なイベントに行きました。
セキュリティ・ミニキャンプ in 北海道がとっても良かったです。全国大会の修了生も結構居て、なんだか嬉しかったです。地方大会もとっても楽しいので来年は地方大会全部行きたいです(お金があれば本当に行きたいです)。
後はラブライブ!蓮ノ空女学院スクールアイドルクラブ 1st Live Tour ~RUN!CAN!FUN!~東京公演に参加しました。実は30分くらい遅刻してしまって、今回も全力で楽しめたわけではなかったです。ところがどっこい女性声優を見ると元気が貰えるのでライブは本当に最高だなと思いました。
12月は異次元フェスに行きました。一番熱いライブで最高でした。心の底から盛り上がれて、声が枯れたのはもちろんちょっと酸欠になりかけました。
AWSのAmplifyを使ったweb開発も始めました。プロ野球のドラフトサイトみたいなものを作ろうと思っていて一応遊べるところまでは出来たのですがReactの認証機能を使った色々が面倒くさいことになっていたりユーザーが自分でテーブルを作成する機能が欲しいので、進めていくうちに色々機能が欲しくなっていくweb開発は難しいなあとなっています。(機能要件などを定義しておくのはめちゃくちゃ大事だなあと思いました)

↓デモ動画です。かなり音が大きいので下げたほうがいいかもしれないです。 youtu.be

総括

プログラミングやその周辺に触れたのがAIきっかけでしたが、色々取りんできた中でとっても楽しいなあという気持ちがあるので本当に嬉しいと思っています。
来年はもっと色々なことに挑戦したいと思いました。
キモいことに、虹ヶ咲に色々刺激を受けて虹ヶ咲仕草みたいなものが自分の心の中に芽生えました。今の自分が色々行動を起こせていたりするのはちょっとそのおかげでもあるのかなとか思ったりしていますが、結局行動するかどうかを決めるのも行動するのも自分自身なのでよく分かりません。でも本当に最高のコンテンツだなと思います。
1月にはそんな大好きコンテンツのライブが有り、それが非常に楽しみです。
虹ヶ咲学園スクールアイドル同好会さん、AtCoderさん、セキュリティ・キャンプさん、その他諸々の皆さん色々感謝しているので本当にありがとうございました♥

情報処理安全確保支援士を取りたいなどと思いました。

菊池律です。
実はまだ基本情報すらも合格しておらず、烏滸がましいのですが情報処理安全確保支援士を取りたいなあと思いました。
今日から基本情報技術者と並行して勉強できたら良いなあと思いました。
勉強内容をまとめます。

12/4
youtu.be
ここまでみました。
あとは基本情報の過去問をちょろっと
明日やりたいこと
・3,4本くらい見る
・基本情報の本をちゃちゃっと終わらせたい
・過去問道場を暇な時間にちょろっと

12/5
youtu.be
ここまでみました。
大学の往復で動画を見るようにしているのですが、無理しない程度になんとなく頭に入ってる気がするのでいい感じです

リバースプロキシ
ケルベロス認証
SAML

暗号のやりとりの方式で、共通鍵とか公開鍵とかやったと思うんですけど、こういう認証のときに使われているのかなーなどと考えました。セキュリティの勉強って実生活で使うサービスで一般化して考えることが難しい気がしていたので今日の内容で「なるほどです」と日常生活と紐付けることが出来てなんとなく成長できた気がする。暗号のやり取りをちゃんと分かっておくともっと面白いんだろうなと思った。
リバースプロキシ→同一ドメイン多め
ケルベロス認証→異なるドメイン多め
・異なるドメインでもケルベロスTGSの間に信頼関係を結んであげればTGSは、ケルベロスが認証しているTGTを受け入れてあげるだけでよくなるので
SAML→異なるドメイン多め
・やり取りがwebブラウザを介してIDプロバイダとwebサーバーでのやり取りで直接やり取りをすることがないので楽
明日やりたいこと
・まさるみる
・基本情報をちょっとやる

12/6
youtu.be
ここまで見た。
ちょろちょろっと過去問道場をやっていたら「公開鍵のやり取りの仕方について正しいのを選んでね」みたいな問題が出てきてよっしゃと思って解いたら余裕で間違えてて恥ずかしかった。
公開鍵暗号方式は、受信者側の公開鍵を使って平文を暗号化させて受信者が秘密鍵で復号するやつだったなとふと思い出しました。
今日やった署名は送信者が秘密鍵で署名を生成→受信者は公開鍵で検証っていうやつだったのでごっちゃにならないようにしたいなと思いました。

MACのやつ。
メッセージ認証のみで、エンティティ認証はできない。
なぜなら、共通鍵を使うということは送信者と受信者の両方で同じハンコを使うということであり送信者側が作った書類でなくても受信者側が作成した書類を、送信者の人に作らせたように偽造することが可能だから。
12/7
youtu.be
ここまで見た。
OCSPステープリングについて、

WEBブラウザからWEBサーバーにアクセスしようとするとサーバー証明書を発行してもらってそれをOCSPレスポンダにぶち込むことで署名や有効期限を確認することが分かったけど、応答に時間がかかってしまうこともあるらしくそれを解決するためにあらかじめOCSPレスポンダの応答を取っておくことで解決するみたいな裏技的手段が面白いなあと思いました。
それ以外覚えてません。ゆっくりやります。
12/11
久しぶりです。記事を書くのがめんどくさくてやめていました。一応勉強は続けていました。
youtu.be
ここまでみた。
こういう分野は面白いかもしれないです。認証とか暗号の技術は全く興味ないのですが、マルウェア辺りはとても楽しめそうだなと思いました。攻撃者側としてシステムをぶっ壊したりなどをしてみたいね。
12/21
10日ぶりです。
youtu.be
一応最後の動画手前まで見たのですが、どうやら別の再生リストがあるっぽいのでそっちをみようかな〜などとも考えています。
実際の攻撃手法とかその辺の話はやっぱり面白いな〜ってなるけど、試験に出ることが多そうな内容はネットワークのことっぽいし重要になりそうなのでちゃんとネットワークのことも勉強したいなあ

AWSのAmplifyを使ったチュートリアルがうまくいかないので対処

菊池率です。最近Webアプリの開発をしたいと思ってとりあえずAWSを色々触れているのですが、チュートリアルを読んでいるとうまくいかない箇所があったので対処法を記しておきます。
aws.amazon.com
問題のチュートリアルは↑なのですが、具体的にはモジュール4のフロントエンドコードの記述の箇所です。
チュートリアル通りに進めていくと、
export 'API' (imported as 'API') was not found in 'aws-amplify' (possible exports: Amplify)
というエラーが表示されると思います。
↓のissueやドキュメントを読んでみると問題の原因や解決法があるそうなのですが、私の環境ではそれでもうまくいかなかったので、自分なりにどう解決したかをまとめます。
github.com
どうやら、2週間くらい前に最新バージョン6.x.xに更新されたそうなのですが、API をエクスポートしなくなり、代わりにgenerateClient()がgraphqlの呼び出しを行うようになったそうです。

src/app.jsを以下のように書き換えます

import React, { useState, useEffect, FormEvent } from "react";
import {
  Button,
  Flex,
  Heading,
  Text,
  TextField,
  View,
  WithAuthenticatorProps,
  withAuthenticator,
} from "@aws-amplify/ui-react";
import "./App.css";
import "@aws-amplify/ui-react/styles.css";
import { listNotes } from "./graphql/queries";
import {
  createNote as createNoteMutation,
  deleteNote as deleteNoteMutation,
} from "./graphql/mutations";
import { CreateNoteInput, Note } from "@/src/API";

import { Amplify } from "aws-amplify";
import { generateClient } from "aws-amplify/api";
import awsconfig from "../src/amplifyconfiguration.json";

Amplify.configure(awsconfig);

const client = generateClient();

const App = ({ signOut }: WithAuthenticatorProps) => {
  const [notes, setNotes] = useState<Note[]>([])

  useEffect(() => {
    fetchNotes();
  }, []);

  async function fetchNotes() {
    const apiData = await client.graphql({ query: listNotes });
    const notesFromAPI = apiData.data.listNotes.items;
    setNotes(notesFromAPI);
  }

  async function createNote(event: FormEvent<HTMLFormElement>) {
    const form = event.target as HTMLFormElement;

    event.preventDefault();

    const formData = new FormData(form);

    const data: CreateNoteInput = {
      name: formData.get("name") as string,
      description: formData.get("description") as string,
    };

    await client.graphql({
      query: createNoteMutation,
      variables: { input: data },
    });

    await fetchNotes();

    form.reset();
  }

  async function deleteNote({ id }: Note) {
    const newNotes = notes.filter((note) => note.id !== id);

    setNotes(newNotes);

    await client.graphql({
      query: deleteNoteMutation,
      variables: { input: { id } },
    });
  }

  return (
    <View className="App">
      <Heading level={1}>My Notes App</Heading>
      <View as="form" margin="3rem 0" onSubmit={createNote}>
        <Flex direction="row" justifyContent="center">
          <TextField
            name="name"
            placeholder="Note Name"
            label="Note Name"
            labelHidden
            variation="quiet"
            required
          />
          <TextField
            name="description"
            placeholder="Note Description"
            label="Note Description"
            labelHidden
            variation="quiet"
            required
          />
          <Button type="submit" variation="primary">
            Create Note
          </Button>
        </Flex>
      </View>
      <Heading level={2}>Current Notes</Heading>
      <View margin="3rem 0">
        {notes.map((note) => (
          <Flex
            key={note.id || note.name}
            direction="row"
            justifyContent="center"
            alignItems="center"
          >
            <Text as="strong" fontWeight={700}>
              {note.name}
            </Text>
            <Text as="span">{note.description}</Text>
            <Button variation="link" onClick={() => deleteNote(note)}>
              Delete note
            </Button>
          </Flex>
        ))}
      </View>
      <Button onClick={signOut}>Sign Out</Button>
    </View>
  );
};

export default withAuthenticator(App);

issueの解決策通りに書き換えると、useState<Note[]>([])
エラー:Parsing error: Unexpected token (30:42)が起きます。
これはTypeScriptの型注釈をJavaScriptファイルで使用しているために発生します。
ファイル形式を.js→.tsxに変更する もしくは

const [notes, setNotes] = useState([]); // 型注釈を削除

でいけるはずです。
他にもissueに書かれたコードはTypeScriptで書かれていそうなので、私はファイルの形式を変更する方を選びました。
src/index.tsxも以下のように書き換えます

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App.tsx';
import reportWebVitals from './reportWebVitals';
import { Amplify } from 'aws-amplify';
import config from './aws-exports';
Amplify.configure(config);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

実行してみます。

ちゃんとできました

以上です。

友達は良いなと思いました。

今日、大学の最寄り駅で大学生と思われる男子2人組がはしゃいでいた。それ自体は気にも留めなかったのだがちょっと感慨深く思うことがあり、その男子2人組は改札を通った後、別々のホームに向かっていた。私はてっきり同じホームで同じ電車に乗って帰るものかと思っていたので驚かされた。私はつい半年くらい前まで高校生だったのだが一緒に帰る人は地元や中学が同じ人ばかりだったのでホームは同じだったし驚いたわけだ。
同時に友達ってこういうものなんだなと思わされた。
何か合わないところはあるけどそれでも仲良く出来て、完璧に自分の生き写しみたいな人間はいないけど一緒にいて楽しめるのが友達なんだなあと感じた。その"友達"っていうのが改札を通って別々のホームに向かう様子に表現されていて、ほんのちょっぴり電車でしみじみ思った。

18日に蓮ノ空女学院スクールアイドルクラブのライブに参加したのだけれど、キャストのMCで「この後ライブの感想わちゃわちゃしたりする人ー!」みたいなことを言ったら色んな人がブレードを振ったり声を上げたりしていて、良いなあと思いました。同じ趣味を分かち合える人が居るのは良いことです。それ繋がりで仲良くなれる人もいるだろうし、単純に自分が感じたことを伝えて理解してくれる人がいるというのは羨ましいです。ライブの帰り道、周りのオタクが「ド!ド!ド!がさ〜」とかライブの感想を言ってるのを間近に感じてしまって、イヤホンで聞いてるはずの素顔のピクセルに全く集中出来ませんでした。
私はこれからも1人でライブに参加してその感想をTwitter(現X)に吐くのだろうなあと思いました。それも楽しいから良いね。

余白
蓮ノ空女学院スクールアイドルクラブがおすすめなのでぜひ見てください。キャストが面白いです。
↓これが良いです。
https://youtu.be/yZWm3BP-hbg?si=KNYqChGSzAoJMMs6

ABC327 E

まとめです。  
https://atcoder.jp/contests/abc327/tasks/abc327_e  
要約すると、  
・テストをn回受けます。  
・n回のテストの中からいくつかの結果を選び、重みづけを行います。  
・重みづけして処理を行ったスコアのうち、最も高いものを出力してください。  
  
問題文から何となく単純そうなdpということが読み取れます。  
ただdpの各要素について一々重みづけを行うのはかなり面倒そうです。  
dp[i][j]=max(云々)という形式で表すのは難しいので、その形式で表せそうな部分だけにdpを適用します。  
分母は以下のように表せます。  
初項(0.9)**(k-1),公比0.9の等比数列の和であることから、k項の和は、  
∑(i=0,k-1){0.9}**i  
= (1-0.9**k)/(1-0.9)  
これにより、分母は10(1-(0.9)**k)になります。

同様に、1200/√kについても、dpを用いる必要はありません。  

したがって、今回dpで求めるのは分子のみでいけます。

ステップとしては

1.dpでk(1,2,3...n)について最も高いスコアを求める

2.dp[N][k](1,2,3...n)について、重みづけして答えを求める

という感じ。

雑なdpの遷移図(入力例1)

 

dp[i][j]=i個目までのテストで結果をj個選んだときのスコアとし、

dpの遷移は

dp[i][j]=max(dp[i-1][j],dp[i-1][j-1]*0.9+p[j])
になります。複雑かもしれないですがdp[i-1][j-1]*0.9+p[j]について

i-1個目までのテストでj個の結果を選んだ時のスコアかi-1個目までのテストでj-1個の結果を選んだ時のスコアにP[j]を追加したもののうち、大きい方になるというのは理解できます。ただ、テストの順番に応じて0.9で処理をしたいです。

現在解ける問題数を3つとしたとき、3つ目の問題のパフォも比較したい場合には

3個目までのテストで結果を3個選ぶと,順番通りにP[1,2,3]を選んだとして

0.81*P[1]+0.9*P[2]+1.0*P[3]
になります。

2個目までのテストで結果を2個選んだ時の最高スコアはP[1,2]を選んだとして

0.9*P[1]+1.0*P[2]
です。

したがって、i-1個目までの結果の中からj-1個選んだ時の最高スコアに0.9をかけることで良い感じになるので、それで比較します。

最後に、dp[-1][各々]について処理を行い結果を出力します。

dpの初期化は-1e9を使ったりすると0.9**5000にだまされるので-infを使っておくと0.9**5000とかに騙されないで済みます

atcoder.jp

まとめ

自分で書いてて、解説が分かりづらいなと思ったので後でまとめ直す

numpyを使って競プロの考察を簡単にできないかなあと考えました。

numpyを使って考察を簡単にしたいなあという次第です。
ABC101のD問題Snuke Numbersを使って云々したいと思います。
↓問題
atcoder.jp

とりあえず、すぬけ数の定義を理解するのが難しいです。
簡単に言うと、nより大きい任意の数字mについて、{\frac{x}{S(x)}}をした時、{\frac{n}{S(n)}}より小さい値が存在しないということです。
手を動かして実験をしてみるというのが一番簡単なのですがもうちょっと簡単かつグラフなどで可視化できないかなあという感じです。

import numpy as np
import matplotlib.pyplot as plt

test = 500
x = np.arange(1, test + 1)
y = np.array([num/sum(map(int, str(num))) for num in x])

# すぬけ数のインデックスを見つける
sunuke_indices = []
for i in range(len(y)):
    min_num = min(y[i:])
    if y[i] == min_num:
        sunuke_indices.append(i)

# グラフを描画
plt.figure(figsize=(10, 6))
plt.plot(x, y, label=r'$\frac{x}{\sum \, \mathrm{digits \, of} \, x}$')
plt.scatter(x[sunuke_indices], y[sunuke_indices], color='red', label='Sunuke Numbers')
plt.xlabel('x')
plt.ylabel(r'$\frac{x}{\sum \, \mathrm{digits \, of} \, x}$')
plt.title('Plot of Sunuke Numbers')
plt.legend()
plt.grid(True)
plt.show()

実行したやつ

適当に書いてみました。500までの数字でしか比較してませんが、以下のことが分かると思います。
・100の倍数の時、{\frac{x}{S(x)}}がめちゃくちゃでかくなっている
・上とは逆に、...99みたいな数字の時{\frac{x}{S(x)}}は小さくなっている
直感的ですが、{\frac{x}{y}}をなるべく小さくするには、yを最大化させxをなるべく小さくすればいいと考えられます。したがって、{\frac{x}{S(x)}}についても、S(x)を最大化させるにはとにかく9が多い方が良いんじゃないかなとなります。
この考察は、numpyとmatplotlibを使ってすぬけ数を可視化したおかげに違いないです。 でも本当は単純にコードを書いて直接すぬけ数を求めるのが楽かもしれない...。 参考までに、100000までの数字のすぬけ数を求めたいと思います。

test=10**5
x=list(i for i in range(1,test+1))
y=list(num/sum(list(map(int,list(str(num))))) for num in x)
for i in range(len(y)):
    min_num=min(y[i:])
    if y[i]==min_num:
        print(x[i])
1
2
3
4
5
6
7
8
9
19
29
39
49
59
69
79
89
99
199
299
399
499
599
699
799
899
999
1099
1199
1299
1399
1499
1599
1699
1799
1899
1999
2999
3999
4999
5999
6999
7999
8999
9999
10999
11999
12999
13999
14999
15999
16999
17999
18999
19999
20999
21999
22999
23999
...
...
100000

ということで、numpyライブラリなどを使って複雑なコードを書いておしゃれに考察をしようとするよりシンプルに考察をした方がいいという結論になりました。
僕はまだ上記の問題を解けていないのでもうちょっと頑張ります。皆さんも頑張ってください。 まとめ よく見たら今回の考察にnumpyはいらないですね。