たつのおとしごのしっぽ

技術に楽しくしがみつく えんじにあ の備忘録

AtCoder Beginner Contest 179 「C - A x B + C」をC#で解いたら

C問題を解くが目標になっているため、勉強しています。
その過程で作ったAtCoderのC問題のコード、コメント多めで載せてみます。

順位表の解答が早い順で見れば、コードの解答は分かりますが、何をやっているのかいまいちわからないときがあります。
そんな時、コメント沢山ついているコードがあればいいなと思うことがあるため、誰かのためになれば良いなと思います。

問題は、C - A x B + Cからご確認ください。

コード

今回は全探索しました。 正整数の組と条件があるため、条件が合わなくなったら繰り返しをやめれば実行時間に間に合います。


using System;
using System.Collections.Generic;
using System.Linq;

namespace AtCoder179
{
    class Program
    {
        static void Main(string[] args)
        {
            // N: 正整数
            var N = long.Parse(Console.ReadLine());

            var half = N / 2;

            var ans = 0L;

            for(var i = 1; i < N + 1; i++)
            {
                // N - i + 1以降は、i * jの時点でNを超えてしまう
                for (var j = 1; j < N - i + 1; j++)
                {
                    var mod = N - i * j;

                    if(mod > 0)
                    {
                        ans++;
                    }
                    else
                    {
                        // これ以上jを上げても正整数の組はできない
                        break;
                    }
                }
            }

            Console.WriteLine(ans);
        
        }
    }
}

AtCoder Beginner Contest 178 「C - Ubiquity」をC#で解いたら

C問題を解くが目標になっているため、勉強しています。
その過程で作ったAtCoderのC問題のコード、コメント多めで載せてみます。

順位表の解答が早い順で見れば、コードの解答は分かりますが、何をやっているのかいまいちわからないときがあります。
そんな時、コメント沢山ついているコードがあればいいなと思うことがあるため、誰かのためになれば良いなと思います。

問題は、C - Ubiquityからご確認ください。

コード

ベン図を書いて整理すると、求め方はシンプルになります。(こんなの数学の問題では…?) DP (動的計画法)でも求められるようですが今回はシンプルな方で書きます。


今回は、Modした結果を出力するということで桁数の大きな値を扱うためいくつか注意が必要です。

答えを求めるのに単純にMath.Powを使うと、桁が大きくなりすぎてしまうため掛け算をするたびにModしないといけませんので、独自の関数を作りました。

また、Modを適宜することで、値によっては結果が負の数になったりするため、Modを足して調整したりします。

桁数の大きい値を扱うのは慣れが必要だなあと思います。


using System;
using System.Linq;

namespace Atcoder178
{
    class Program
    {
        const long Mod = 1000000007;

        static void Main(string[] args)
        {
            // n: 整数の長さ
            var n = long.Parse(Console.ReadLine());

            // a. 0-9のn桁の整数の組み合わせ(全体): 10^n
            // b. 全体に条件「0を含まない」を追加: 9^n
            // c. 全体に条件「9を含まない」を追加: 9^n
            // d. 全体に条件「0、9を含まない」を追加: 8^n

            // 全体に「0を1つ以上、9を1以上含む」を追加:
            // a - b - c + d

            var ans = powMod(10, n);
            ans -= powMod(9, n) * 2 % Mod; // 大きくなるためModする
            ans += powMod(8, n);

            // Modしている関係でadが小さくbcが大きくなり、
            // 答えが0以下になる場合があるため
            // Modを足して一旦正の値にする
            ans += Mod;

            // Modを足して大きくなっている場合があるためModする
            ans %= Mod;

            Console.WriteLine(ans);

            // これだとnの桁数が大きいとNaNになってしまう
            //var ans = Math.Pow(10, n) % Mod;
            //ans -= Math.Pow(9, n) % Mod * 2;
            //ans += Math.Pow(8, n);
        }

        private static long powMod(long x, long y)
        {
            var ans = 1L;
            for(var i = 0L; i < y; i++)
            {
                ans *= x;
                ans %= Mod;
            }

            return ans;
        }
    }
}

AtCoder Beginner Contest 177 「C - Sum of product of pairs」をC#で解いたら

C問題を解くが目標になっているため、勉強しています。
その過程で作ったAtCoderのC問題のコード、コメント多めで載せてみます。

順位表の解答が早い順で見れば、コードの解答は分かりますが、何をやっているのかいまいちわからないときがあります。
そんな時、コメント沢山ついているコードがあればいいなと思うことがあるため、誰かのためになれば良いなと思います。

「C - Sum of product of pairs」の問題は、AtCoderさんからご確認ください。

コード

累積和の公式を知っていれば解ける!と思いきや、桁が大きいためこまめにmodしないとWAになってしまいます。

提出 #16317416 - AtCoder Beginner Contest 177のコードを参考に改良してみました。

公式は、異なる2つの項の積の和 - 高校数学.netを見ました。


using System;
using System.Linq;

namespace Atcoder177
{
    class Program
    {
        static void Main(string[] args)
        {
            long N = long.Parse(Console.ReadLine());

            long[] A = Console.ReadLine().Split(' ').Select(x => long.Parse(x)).ToArray();
            
            // Aの総和は、A * N ((10^9) * (2 * 10^5)) で 10^15くらい
            // longの最大値は、10^19満たないくらい
            long sum = 0;

            foreach (long a in A)
            {
                sum += a;
            }

            long mod = 1000000007;

            long ans = 0;

            // Nの時は、0なので計算しなくてもOK
            for (long i = 0; i < N -1; i++)
            {
                // ここでは、総和が必要であるためmodしない
                sum -= A[i];

                // ansを求めるときに初めてmodする
                // Aの総和にAをかけるとlongの最大値を超す可能性があるためmodする
                ans += (A[i] * (sum % mod));

                // 足したときもmodする
                ans %= mod;
            }

            Console.WriteLine(ans % mod);
        }

    }
}

AtCoder Beginner Contest 175 「C - Walking Takahashi」をC#で解いたら

C問題を解くが目標になっているため、勉強しています。
その過程で作ったAtCoderのC問題のコード、コメント多めで載せてみます。

順位表の解答が早い順で見れば、コードの解答は分かりますが、何をやっているのかいまいちわからないときがあります。
そんな時、コメント沢山ついているコードがあればいいなと思うことがあるため、誰かのためになれば良いなと思います。

「C - Walking Takahashi」の問題は、AtCoderさんのリンクからご確認ください。

コード

往復移動する前と往復移動する後で条件分けをして考えます。
往復移動に入ると+D, -Dを繰り返します。


using System;
using System.Linq;

class program
{
    static void Main()
    {
            var line = Console.ReadLine().Split(' ').Select(x => long.Parse(x)).ToArray();

            long X = line[0]; // 現在地
            var K = line[1]; // 移動回数
            var D = line[2]; // 移動距離

            long ans = X; // K回後の最小の絶対値

            // moveTimesToRoundTrip: 往復移動をするまでに必要な移動回数
            // Xがマイナスでも答えは変わらないため絶対値で計算
            // 切り捨ててもKは整数であるため同じ値になる
            var moveTimesToRoundTrip = Math.Abs(X) / D;

            if (moveTimesToRoundTrip >= K)
            {
                // 往復移動をしない場合
                // 現在地から移動回数*移動距離を足し引きして求める
                if (X >= 0)
                {
                    ans = X - K * D;
                }
                else
                {
                    ans = X + K * D;
                }
            }
            else
            {
                // 往復移動をする場合

                // distanceBeforeRoundTrip: 往復移動する前の距離
                var distanceBeforeRoundTrip = X % D;

                // roundTripTimes: 往復回数
                // 原点近くにたどり着くまでにmoveTimesToRoundTripかかる
                // 残りの回数roundTripTimesは原点をまたいで-D, +Dを繰り返す
                var roundTripTimes = K - moveTimesToRoundTrip;

                if (roundTripTimes % 2 == 0)
                {
                    // 偶数回移動するなら
                    // 往復移動しても往復移動する前の距離が答え
                    ans = distanceBeforeRoundTrip;
                }
                else
                {
                    // 奇数回移動するなら
                    // 往復移動する前の距離が正なら移動距離Dを引いて
                    // 往復移動する前の距離が負なら移動距離Dを足す
                    if (distanceBeforeRoundTrip > 0)
                    {
                        ans = distanceBeforeRoundTrip - D;
                    }
                    else
                    {
                        ans = distanceBeforeRoundTrip + D;
                    }
                }
            }

            Console.WriteLine(Math.Abs(ans));
                
    }
}

AtCoder Beginner Contest 173 「C - H and V」をC#で解いたら

C問題を解くが目標になっているため、勉強しています。
その過程で作ったAtCoderのC問題のコード、コメント多めで載せてみます。

順位表の解答が早い順で見れば、コードの解答は分かりますが、何をやっているのかいまいちわからないときがあります。
そんな時、コメント沢山ついているコードがあればいいなと思うことがあるため、誰かのためになれば良いなと思います。

「C - H and V」の問題は、AtCoderさんのリンクからご確認ください。

コード

考えられる組み合わせは、2の12乗にしかならないため全検索することを考えます。
2のX乗の場合は、Bit演算を考えると良いため、マス目や行列数をBitに変えて算出していきます。

解説動画を見ても実装方法が思いつかなかったため、gutti2011さんの提出コードを参考にしています。
分かりやすいようにコメントや変数名を変えています。


using System;
using System.Collections.Generic;
using System.Linq;

namespace atcoder173
{
    class Program
    {
        static void Main(string[] args)
        {

            var list = Console.ReadLine().Split(' ').Select(x => int.Parse(x)).ToArray();
            var H = list[0]; // 行数
            var W = list[1]; // 列数
            var K = list[2]; // 期待する黒の個数

            string[,] field = new string[H, W];
            for (int i = 0; i < H; i++)
            {
                var buf = Console.ReadLine();
                for (int j = 0; j < W; j++){
                    field[i, j] = buf[j].ToString();
                }
            }

            int ans = 0;

            // rowBitCounter
            // どの行を選ぶか全ての組み合わせ数を定義する
            // 行を選ぶ選ばない含め全種類を試すため(1 << H)通り
            // 例えばHが2ならどの行も選ばない、0行を選ぶ、1行を選ぶ、0と1行を選ぶで4通り
            for (int rowBitCounter = 0; rowBitCounter < 1 << H; rowBitCounter++)
            {
                var selectedRowBitNums = new List<int>();

                // どの行を赤く塗りつぶすかを決めて、塗りつぶす行をselectedRowBitNumsに追加する
                // (1 << i)がBitでの行を表している
                // 例えばHが2なら(1 << i)が01,10の2通りでこれは0列目,1列目を表す(iがちょうど列番号になる)
                // rowBitCounterはどの行を選ぶかが決まるためrowBitCounterが0の時は下1桁は0でどの行も選ばない
                // rowBitCounterが1の時は下1桁が1なので、下1桁が1である01(0列目)が選ばれ、0列目(i)がselectedRowBitNumsに格納される
                for (int i = 0; i < H; i++){
                    if ((rowBitCounter & (1 << i)) != 0){
                        selectedRowBitNums.Add(i);                     
                    }
                }

                // columnBitCounter
                // どの列を選ぶか全ての組み合わせ数を定義する
                // 列を選ぶ選ばない含め全種類を試すため(1 << W)通り
                for (int columnBitCounter = 0; columnBitCounter < 1 << W; columnBitCounter++)
                {
                    var selectedColumnBitNums = new List<int>();

                    // どの列を赤く塗りつぶすかを決めて、塗りつぶす列をselectedRowBitNumsに追加する
                    // (1 << i)がBitでの列を表している
                    for (int i = 0; i < W; i++){
                        if ((columnBitCounter & (1 << i)) != 0){
                            selectedColumnBitNums.Add(i);
                        }
                    }

                    var fieldForCalculation = (string[,])field.Clone();

                    // 選択した行番号は赤で塗りつぶすためrで置き換える
                    foreach (var v in selectedRowBitNums){
                        for (int i = 0; i < W; i++){
                            fieldForCalculation[v, i] = "r";
                        }
                    }


                    foreach (var v in selectedColumnBitNums){
                        for (int i = 0; i < H; i++){
                            fieldForCalculation[i, v] = "r";
                        }
                    }
                    
                    int blackNum = 0;
                    for (int i = 0; i < fieldForCalculation.GetLength(0); i++){
                        for (int j = 0; j < fieldForCalculation.GetLength(1); j++){
                            if (fieldForCalculation[i, j] == "#") blackNum++;
                        }
                    }

                    if (blackNum == K) ans++;
                }

            }

            Console.WriteLine(ans);
        }
    }
}

AtCoder Beginner Contest 174 「C - Repsept」をC#で解いたら

C問題を解くが目標になっているため、勉強しています。
その過程で作ったAtCoderのC問題のコード、コメント多めで載せてみます。

順位表の解答が早い順で見れば、コードの解答は分かりますが、何をやっているのかいまいちわからないときがあります。
そんな時、コメント沢山ついているコードがあればいいなと思うことがあるため、誰かのためになれば良いなと思います。

「C - Repsept」の問題は、AtCoderさんのリンクからご確認ください。

コード

解いている時は全く分からずでしたが、解説動画を見たら理解できました。
余りで考えるということがポイントで、項数が増えるたびに余りは0からk - 1の中のどれかを順に辿っていきます。

余りが0になるときは、0からk - 1の中をかぶらずに辿っていくうちに余り0にたどり着きます。
余りが0にならないときは、余りの計算をすると0からk - 1の中でどれかが被って処理がループします。

解説動画では、余りの計算をk以上処理したら余りの処理がループしているとのことだったためそこで計算を終了するような処理にしています。


using System;

namespace atcoder174
{
    class Program
    {
        static void Main(string[] args)
        {
            // kの倍数が登場するのは何項目かを解く
            var k = int.Parse(Console.ReadLine()); 
            var remainder = 7 % k;

            // k以上処理しても余りが0にならない場合
            // 余りが0からk - 1の中でループしているため処理を終了する
            for (var i = 1; i < k + 1 ; i++)
            {
                // 余りが0のためiを項数として出力する
                if (remainder == 0)
                {
                    Console.WriteLine(i);
                    return;
                }

                // 項数が増えるたびに数はn * 10 + 7増えるため余りも同じだけ増える
                // それをkで割った余りを再び計算する 
                remainder = (remainder * 10 + 7) % k;

            }

            // 割り切れなかった場合は、-1を出力する
            Console.WriteLine(-1);
        }
    }
}

Webを支える技術 -HTTP、URI、HTML、そしてREST読んだのでまとめてみた

Webを支える技術 -HTTP、URI、HTML、そしてREST を初めて読んだので、まとめや感想などを書きます。

Webの用途

  • Webサイト
  • UIとしてのWeb
    例えばデバイスの設定画面やHTMLによるヘルプなユーザー用IFとしての使い方。
  • APIとしてのWeb
    例えばブログサービスなどプログラム用IFとして使い方。

HTTP

情報を取得したり発注したりするプロトコル
HTTP 1.1だとメソッドは8つになり、かなりシンプル。

HTML

情報を表現する文書フォーマット。
Webは、ハイパーメディアシステムと分散システムの2つの側面がある。

ハイパーメディア

メディアをハイパーリンクで結びつけて構成したシステム。
非線形的にユーザーがリンクを選択して情報を取得する。
ハイパーリンクとは、情報同士を結びつける機構。

分散システム

複数のPCを組み合わせて処理を分散させる形式。
Webは、世界中のサーバーに世界中のブラウザがアクセスするため、分散システムといえる。

REST

ネットワークシステムのアーキテクチャスタイル。
ネットワークシステムのアーキテクチャスタイルであるクライアント/サーバから派生したアーキテクチャスタイル。
ただし、RESTはWebのアーキテクチャスタイルを指す場合が多い。

リソース

Web上にある名前をもった情報のこと。
リソースの名前はURIのこと。

アーキテクチャスタイル

(1)クライアント/サーバー
プロトコルHTTPでクライアントとサーバーが通信する。
メリット:
クライアントとサーバーの処理が分離される。
クライアントをマルチプラットフォームに出来る。

(2)ステートレスサーバー
サーバーがクライアントのアプリケーション状態を管理せず、通信の度に全情報を送る。
メリット:
サーバーの実装が簡単になる。

(3)キャッシュ
一度取得したリソースをクライアントが使いまわせるようにする。
メリット:
クライアント、サーバー間の通信を減らせる。

(4)統一インターフェース
リソースに対する操作を統一されたインターフェイスで行う。
HTTPのインターフェイスは、HTTPメソッドGET、POST、PUT、DELETEを利用する。
メリット:
クライアントとサーバーの独立性が増す。
クライアント/サーバーを実現するために必要な制約。

(5)階層化システム
統一インターフェイスが実現されていれば、システムが階層になっていても接続先を変える必要がない。
メリット:
HTTPが実現されていないようなシステムでも、Webアプリケーションサーバーを挟めばHTTPインターフェイスを介してクライアント接続できる。

(6)コードオンデマンド
プログラミングをクライアントにダウンロードして実行する。
メリット:
クライアントを後から拡張できる。

上記の制約はいくつか除外しても構わない。
ほとんどの制約が外れるならば、P2Pなど他のアーキテクチャスタイルを検討するべき。

また、アドレス可能性(URIによって情報が表現できている)や接続性(リソースをリンクで接続する)もRESTの思想の一つ。
詳しくは、
RESTful APIとは何なのか
プラットフォームとしてのWeb 2.0とRESTの関係 | 日経クロステック(xTECH)

URI

URIは、情報を指し示す識別子。

URIとURLとURN

URNは、リソースを識別する恒久的な名前を示す。
リソースに恒久的なIDをふるための仕様が検討された。

URLは、リソースの場所を示す。最近はURLで十分永続的になっている。

URI = URL + URNなイメージ。
サイトのアドレスは、URLでありURIである。
詳しくは、 URL と URI の違い - Qiita

URIの設計

大きく分けると以下の点に注意するべきである。

HTTP

同期型プロトコル
クライアントがリクエストを出したらレスポンスが返るまで待つ。

ステートフルとステートレス

ステートフルだと、セッション状態をサーバーで管理しなければいけないためクライアント数が増えたときにスケールアウトさせにくい。

ステートレスは、サーバー側のシステムが単純になる。しかし、データ量が多くなったり、認証などサーバーに負荷のかかる処理を繰り返すなどパフォーマンスは低下する。
ネットワークトラブルが起きたときに、そのリクエストが正しく処理されたかが知れない。

HTTPメッセージ

リクエストメッセージ

GET /background.png HTTP/1.0

リクエストライン: リクエストメッセージの一行目。メソッド、リクエスURIプロトコルバージョンから構成される

リクエストヘッダーは下記の画像が参考になります。
https://mdn.mozillademos.org/files/13821/HTTP_Request_Headers2.png HTTP メッセージ - HTTP | MDN

レスポンスメッセージ

HTTP/1.1 404 Not Found.

リクエストライン: リクエストメッセージの一行目。プロトコルバージョン、ステータスコード、テキストフレーズから構成される

レスポンスヘッダーは下記の画像が参考になります。
https://mdn.mozillademos.org/files/13823/HTTP_Response_Headers2.png

HTTP メッセージ - HTTP | MDN

POSTとPUTの使い分け

POST
リソースを作成する時、クライアントがリソースのURIを指定できない。サーバー側で決定する。
URIを自動で決定して良い場合はPOSTが一般的。

PUT
リソースを作成する時、クライアントがリソースのURIを指定できる。
ただし、リソースの上書きを避けるためクライアント側でURIが存在するかチェックする必要がある。
また、クライアントがURIの制約を知る必要があるためサーバーと密な関係になる。 特別な理由がなければリソース作成はPOSTが一般的。

べき等性と安全性

べき等性: ある操作を何度行っても結果が同じ
安全性: 操作対象のリソース状態を変化させない

PUT、DELETE
べき等だが安全でない。
PUTとDELETEは何度行っても同じ結果が返る。
同じ内容であればリソースを何度更新しても何度削除しても同じ結果である。
よって、クライアントが重複を恐れず何度も送信できる。

POST
べき等でも安全でもない。
POSTを複数回送るときは慎重になる必要がある。
メソッドの誤用に注意。
それぞれのべき等性や安全性を守ること。
万能なPOSTで他のメソッドを実現しないこと。

HTTP認証

リソースにアクセス制限がかかっている場合、ステータスコード401 Unauthorizedが返ってWWW-Authenticateヘッダーにリソースへのアクセスに必要な認証情報を通知する。

WWW-Authenticateヘッダー

WWW-Authenticate: <type> realm=<realm>

type : 認証の種類。Basic認証など。
realm: リソースが属しているURI空間の名前。
ヘッダーの値をチャレンジと呼ぶ。
HTTP 認証 - HTTP | MDN

Basic認証

Base64 を使用してエンコードされたユーザー ID とパスワードのペアによる認証方法。
Basic64は簡単にデコードが可能。利用するときはBasic 認証と組み合わせて HTTPS/TLS を使用する必要があります。
TLS : インターネット上のデータ通信を暗号化するプロトコルSSLTLSの後継。
HTTPS: SSL/TLSを使って通信をする。

キャッシュ

サーバーから取得したリソースをローカルストレージに蓄積して再利用する。
ヘッダーからキャッシュしても良いかどうか、有効期限はどれくらいかなど指定される。

microformats

HTMLの中でさらに意味のあるデータ、ライセンス情報などを表現するための技術。
プログラムで処理可能な情報の意味を記述する仕様RDFの問題点(記述が複雑、統一した記述がしにくい、RDFが対象とは独立したメタデータになる)を解消した技術でもある。
microformatsの問題点を解決するRDFaという技術もある。
詳しくは、
Big Sky :: 今さら聞くのは恥ずかしい「microformatsとは何か?」
microformats の利点と欠点 – カラクリ.jp

Atom

Atomは、XMLフォーマット、データフォーマットの規定。様々なWebサービスのWebAPIとして利用できる。
AtomPubは、Atomを利用したリソース編集プロトコルの既定。CRUD操作を実現する。

AtomPubに向いているWeb API
ブログサービスや検索機能を持つデータベースAPIなど

AtomPubに向いていないWeb API
リアルタイム性が重要なAPIや映像のストリーム配信など、HTTP以外のプロトコルを必要とするAPIなど
また、「タイトル」「作者」「更新日時」など、Atom フォーマットが用意するメタデータが不要なAPI

JSON

JSONは、データ記述言語。

メリット
XMLに比べて冗長性が低く、データ表現のフォーマットとして利用される。 クロスドメイン通信ができ、Ajaxでは必須の技術。

Webサービスの設計

ここからが設計者として一番重要な章。
WebサービスとWebAPIは分けて考えないことが重要。

リソース指向アーキテクチャ(ROA)

本書では、まずリソース指向アーキテクチャ(ROA)でアプローチしている。
詳しくは、
リソース指向アーキテクチャ(ROA)とは何なのか - Qiita

本書では、以下の手順で設計している。
1.Webサービスで提供するデータを特定する
2.データをリソースに分ける
3.リソースにURIので名前を付ける
4.クライアントに提供するリソースの表現を設計する
5.リンクとフォームを利用してリソース同士を結び付ける
6.イベントの標準的なコースを検討する
7.エラーについて検討する

以下にそれぞれの手順での注意点を示す。

1.Webサービスで提供するデータを特定する, 2.データをリソースに分ける
このWebサービスでは、どんな情報を提供するのか?を基準に機能をリソースに落とし込む。
この時、機能の結果をリソースとしてとらえることが重要。例えば、検索結果が1つのリソースになる。

3.リソースにURIので名前を付ける
URIは、人間にとって読みやすいURIとプログラムにとって読みやすいURIが異なる場合がある。
その時は、代替リソースとしてプログラムにとって読みやすいURIとすることも考える。

4.クライアントに提供するリソースの表現を設計する
適切なXML表現やフォーマット、マルチメディア表現などを選択する。
XML表現は独自のフォーマットを作り出さないように選択する。

5.リンクとフォームを利用してリソース同士を結び付ける
リンク関係を図にまとめるなどする。

6.イベントの標準的なコースを検討する
Webサービスが提供すると思われる利用コースを検討する。矛盾などがないか検討する。

7.エラーについて検討する
エラーを検討してステータスコードを検討する。

また、書き込み可能なWebサービスの場合、以下の検討が新たに必要になる。

他の設計手法

リソース指向アーキテクチャ(ROA)での以下の2つが一番の肝のためある程度確立されている手法で補う。
1.Webサービスで提供するデータを特定する
2.データをリソースに分ける

本書では、以下の方法をあげている。それぞれの導出できないものがあるため特徴を知って使うようにする必要がある。
a.関係モデルのER図
b.オブジェクト指向モデルのクラス図
c.情報アーキテクチャ

a.関係モデルのER図
リソースの設計は、正規化を崩す。
Webサービスは、一度のリクエストでクライアントが必要な情報が取得できるように設計するため。
リソースの導出はしやすいイメージ。

ただ、トップレベルリソース、URI階層構造や一部リソースを導出できない。

b.オブジェクト指向モデルのクラス図
階層やリソース間のリンク関係の検討はしやすいイメージ。

ただ、トップレベルリソースを導出できない。

c.情報アーキテクチャ
本書では、おすすめの手法。
情報アーキテクチャをもつWebサイトの構造は、ほぼWebサービスにそのまま適用できる。
ROAの苦手なリソース分類を補える。
詳しくは、
情報アーキテクチャとは | murashun.jp
ホームページ制作に役立つ「情報アーキテクチャ(IA)」とは。インフォメーションアーキテクトとの違いと基本的なポイントを解説|ferret
情報アーキテクチャ(IA)の基礎と基本 | ブログ | 株式会社モンゴロイド|Webマーケティング

感想

簡単な技術が流行るという話が本書ではあるが、まさにソフトウェアの設計はシンプルこそ正義だなと普段開発してて思います……。

認証関係もあまり詳しくないため以下のサイトとか見て勉強しました。
認可と認証、その種類 - 研鑽の日々

あと、情報アーキテクチャはどうやるかは本書では分かりにくいです。本書でも詳しくは、Web情報アーキテクチャ 第2版へと書かれています。
今は、Web情報アーキテクチャ 第4版でしょうか?

基本的なWebってどんな技術で成り立ってるの?と設計がどうやってしているの?が知れました。
本格的にWebサービスを作るなら設計についてはもうちょっと情報が欲しいなと思いました。