辺見庸『もの食う人びと』 感想文
初めてこの本を見たのは確か2ヶ月くらい前だったか。隣のクラスの下駄箱にそのクラスの担任の先生が置いていたこの本をなんの気無しにパラパラとめくってみたら,残飯を受け取る筆者と,彼や彼を撮るカメラを不思議そうに見つめるバングラデシュの人たちの写真が目に入ってきた。衝撃である。残飯がこんもりと盛られた大皿から小皿に入れてもらい,受け取るところを写したであろうその写真は,その後も僕の頭の中にくっきりと焼き付いていてなかなか離れなかった。この写真の何が僕に衝撃を与えたのかを確かめなくてはならない,そう思うようになった。
そうしてこの本を読んで当面の目標は達成したように思えたのだけど,読み終わった今,僕はこの本について何かしら書かなくてはならない,書くべきことがあると,そう感じている。読まなくてはならないと感じて読んだら,次は書かなくてはだ。まったく忙しいものである。
よって以下は生まれてから一度も食べ物に困ったことがない高校生が,飢餓など世界中の「食う」ことに関して書いた本を読んだ感想である。特に大したことは書いていないけど。
そもそも僕は「食う」という言葉が嫌いだ。他人にを強制するつもりはさらさらないが,自分では「飯を食う」という表現は「ご飯を食べる」に言い換えるように心がけている。理由は,自分でも分かっていなかった。なんとなく汚い感じがしていたからだ。それがこの本を読んで,ようやくその答えが見つかった(ような気がした)。
「食う」という言葉は,僕が今まで思っていたように汚いことではなかった。「食う」という言葉は,人の命や文化をつなぐ行為を指し示す言葉であった。
この本に出てくる人たちは皆,「食う」ことに真摯に向き合っている。それが環境のなせる業なのか,民族特有の空気なのかは分からないが,彼らのようにその日その日の「食う」行為に真剣に向き合う光景は日本ではまずお目にかかれない。
思うに,日本に住んでいる限り,「食う」という行為を感じたり行うことはほぼ不可能なのではないか。筆者の言う飽食の国では「食う」ではなく「食べる」ことしかできない。だから僕は「食う」という言葉が嫌いなのかもしれない。ファミレスや家で友人や家族とご飯を「食べる」行為はどう頑張っても「食う」という行為に行為にはなりえないのだから,僕は「食う」という言葉を避けてきたのだろう。意識するとせざるとに関わらず。
食える世の中は平和だ。人々は争うことで自らが食うことを困難にしている。筆者も,「果てしない殺しあいより,食う楽しみを取り戻すほうがいいと,胃袋で理解できはしないか」と書いている。"Love&Eat"とでも言ったところか。結構なことじゃあないか。それが実現可能か否かは別として。
でも確かに,食うことで分かり合える可能性がないわけではない。食べ物の大切さは,「何処どこでは食べれない人がいて〜」みたいな語られ方ではなく,それがなせることを持って語られるようになって欲しいと思った。
筆者はミクロな視点から自分が訪れた地域の「食う」ことについて書いている。「〜では全体的に食料が足りず云々…」みたいなことは口が裂けても言わない。この状況についてどうするべきか,なんてことも書かない。あくまで自分がその土地で出会った人々の「食う」ことに寄り添い,なにをどう食いそれについてどう考えているのかを,時々筆者がそれについて思ったことを交えて淡々と綴っているだけである。ゆえに,そこには決して押し付けがましくない「現実」が存在しているだけだ。その現実をどう受け止めるかは読んだ人次第だ。
ただ,僕は今のこの現実が,人々が皆「食べる」ことができるとまでは行かなくても,世の中で行われている「食う」ことについて理解することができる現実になって欲しいなと思った。そのためになにをすればいいかは未だに分かっていない。だから僕はこのブログを今書いているのかもしれない。一人でも多くの人が,世界中の「食う」ということについて考え,理解しようと思えるように,その手助けになればと思い書いているのかもしれない。
最後にこの本の中でも印象に残った話を,各章から一つずつ。
1章 残飯を食らう
バングラデシュにて,筆者が旅で最初の飯を食おうと思ったらなんとそれは残飯であったという,僕が一番最初に見て衝撃を受けた写真の話。
読んでる時も衝撃を受けていたが,読み進めているとこれと同等かまたはそれ以上の話でこの本は溢れていた。飯にありつける者と,そうでない者の差がこの国ではあまりにハッキリとしすぎている。
2章 菩提樹の香る村
民族同士の争いによって,仲の良かった者同士が敵になる。殺しあう。食えなくなる。菩提樹の香りがキツいほどする,廃墟のような村で筆者が見た戦争と食。
筆者が出会ったのは,戦争のため廃墟のようになった村でものを一人食うおばあさんただ一人。
3章 チェロ弾きの少女
この章の話の衝撃の度合いなら「兵士はなぜ死んだのか」と「禁断の森」が群を抜いている。
しかし,この話が僕の目を引いたのは「オウム真理教」や「ショウコウ・アサハラ」という言葉がでてきたところだろう。まさかこんなところでこの言葉を目にするとは思ってもみなかったのでとても驚いた。
話自体は母子の食について,それも身勝手な母親に振り回される子の話である。
4章
この章の話についてとやかく言うことは僕の本望ではない,という風に書いて逃げることが良いことなのかは分からない。が,この章に書かれていることは今も議論がなされていることであって,そのことについて書くのはあまりにリスクが大きすぎるように思える。正直言って,筆者に反感を覚えるところもあった。故に,印象に残った話を述べるのも避けておきたいと思う。
正直言って,20年以上も前に書かれたこの本に載っている話が今の世の中でそっくりそのまま通用するとは思わない。この本にでてくる人たちだって死んでしまっている人が大半だろうし,書かれている戦争や紛争などの問題も解決してしまっているかもしれない。それによって食のあり方も変わっただろう。
でも,考えてみれば「食う」ことなんてそんなものなのかもしれない。その時々の社会情勢に振り回され,そして人々を振り回す。僕はそんなところに「食う」という行為の大切さや儚さを感じることができた。
Twitterをしばらく離れてた理由と,それによって分かったこと,あと僕からのアドバイス
お久しぶりです。
しばらくTwitterやめてました。理由は単純に面倒になったからです,いろんなことが。
なんの前触れも無くある日なんとなくTwitterの公式クライアントからアカウントを消してしまったので,また再開する日も得には決めていませんでした。決めたところで本当に再開するかどうかも分からなかったし。
疲れるんだよ,Twitterでの色々が
際限なく流れてくるtweetをひたすら読むのはものすごく疲れます。ただのtweetだけならまだしもアニメのキャラの誕生日tweet,政治的主張,誹謗,中傷,炎上,etc… 要するに悪意のあるないに関わらず相手をイラだたせるような文章で溢れています。こうなるとずっと画面を見てる目の疲れはもちろん,精神的疲れも馬鹿にできなくなってきます。で,そんな疲れからTwitterが嫌になってしまいました。いや,ほんと勝手な話ですよねまったく。
Twitterに限らず,メールやSNSなど文章だけでのやりとりは相手の表情は声の調子が分からないがゆえに誤解を引き起こしやすいです。僕の精神的な疲れも誤解によってもたらされたものである可能性もあります。でも疲れちまったもんはしょうがないしね,そういうわけでやめました。
なんでTwitterをやっていたんだっけ…
プログラミングをやっている関係で,その界隈の方々をフォローしていました。有益な情報を得るために。確かに,色々な人と仲良くなれたのは素晴らしいことだと思います。しかし,それ以上に仲良くなったがゆえに彼らとのリプライでの会話に時間を費やすことも増えてきました。これじゃあなんの意味もないよねぇ…
TLに流れてくるtweetの一つ一つの価値はとてつもなく低いです。有益な情報なんてめったに流れてきません。それどころか,僕の場合は時間を大いに溶かす結果になってしまいました。こんなところ,一刻も早く抜け出したいですよね?つーわけでやめました。
文章力の低下
オタク構文というものがある。詳しくは調べてもらえば分かると思いますが,簡単に言えば,少ない文字数でその言葉を知っている人たちに自分の思ったことや経験を語ることのできる言葉です。ネット用語とかネットスラングみたいなのと同義だと思ってもらえればいいです。
この言葉,めちゃくちゃ便利なんです。頭をたいしてひねることなくウケのいいtweetが書けてしまうんです。故に,決まりきったオタク構文を使わずに文章を書くことが困難になってしまいます。実際,いまこのブログを書きながらも僕は「あれ,この言葉ってこういうふうにつかっていいんだっけ…?」みたいになることが何回かありました。言葉の使い方だけでなく,文章の書き方すら迷ってしまう始末です。Twitterでオタク構文ばっか使っていると文章力が死にます。死に絶えます。そのことに対する危機感と,オタク構文に対する気持ち悪さもあって,Twitterやめました。
え?ああ,僕の文章力が元々低いって?その話は今は置いておいてね…
今の使い方
少し前までは一切アカウントを開いてませんでしたが,最近はちょくちょく見ています。それでも本当に暇なときにTLを眺める程度で,自分でtweetをすることも減りました。過去のtweetもすべて消しました。多分ですが,これが僕にとって一番いいTwitterの使い方だと思っています。そういうわけだから僕のアカウント,少し静かになったよ。
今後,どういうふうにTwitterを使っていくかは分かりません。ここからまたどういうわけかアカウントを消すことになる可能性もなきにしもあらずです。その時はそうすることが僕とTwitterの一番の距離感だと思ってそうだし,なにがどうなるかは本当に自分でも分からないです。
実際にやめてみた人からのいくつかのアドバイス
もしTwitterをやめるかどうかで悩んでいる人がいたら,Twitterをやめることのメリットやデメリットだけでなく,Twitterをやっていることのメリットとデメリットを考えてもらいたいです。今回の僕のように実際にやめてみるのもいいかもしれません。
また,やめるつもりはなくても,暇つぶしや楽しむためのツールに時間を取られたり,疲れてしまっている人は今一度ツールとの距離感について考えてみるのがいいかもしれません。Twitter以外にも人とつながる方法なんていくらでもあるんだから。
以上,Twitter中毒とも言えた僕が,ある日急に冷めてしまうこともある。というわけで,「人生どうなるか分からないね」ってお話でした。
競技プログラミング for beginners! ~C++実装テク編~
少し早いけど,メリークリスマス!
この記事は中高生プログラマ Advent Calendar 2015 - Adventarの22日目として書かれたモノです。
自己紹介
競技プログラミングを主にやってる高校生。
人生のバイブルは天才バカボン。「競プロerなら蟻本だろ。」って怖い人に突っ込まれそうですね。蟻本持ってないから許せ。
Twitterを見ればどんな人か分かると思いますので詳しいことは省きます。
もし僕についてもっと知りたい人がいたら声をかけてくれれば1時間でも2時間でも話しますので,遠慮せずに。
twitter.com
前置きと諸注意
タイトルに実装テク編と書いたものの,アルゴリズム編やデータ構造編を書く能力も気力もないので,あまり過度な期待はしないほうがいいです。
こうやってタイトルに書いておくことで「ググられビリティ」みたいなのが高まる感覚ない?僕はあるよ。
「こういう書き方もあるんだな」っていう程度の参考にしていただければ結構です。
競プロ出来ない奴が書いてるからもしかしたら間違えがあるかもです。その時はコメントかTwitterで教えてください。
また,この文章を読んでいるとき、画面をスクロールするためにマウスを使うと手の油分や汗が付着する可能性があるので、ソレが気になる方は手袋をすると良いでしょう。使い捨てのゴム手袋などが理想的ですね。
スマホで読んでいる人はスマホ対応型手袋を使いましょう。
この文章を必要ないなと感じて読み飛ばした人は論理的に考えて行動出来る人だと思うので競プロ強い人です。
そうじゃないよく読んでいた人は問題文を誤読する心配が無さそうなのでやっぱり競プロ強い人です。
この記事が対象にしている人
・C++でループ,条件分岐ができる人
・C++以外の言語でプログラミング経験のある人
・競プロerのコードを見てギョッとした人
上記以外でプロの方はブラウザの戻るボタンを押してください。怖いんで。。。
そうじゃない初心者は入門サイトなどを一通り読めばこの記事は問題なく読めると思います。
C++入門
C++入門~bituse~
競プロにおいてはそこまでクラスとか仮想関数とか気にする必要ないかもですね。
知っていると書き方の幅が広がるのは確かですが。
そろそろ競プロの話,しません?
しましょう。
変数名
タイプ数や時間短縮のための1文字変数がやたらと多い。
例
int t; //時間 int p; //ポイント int r, h, y; //平面図形,座標が関わってくる問題で縦,y座標を表す。 int c, w, x; //上と同じく横,x座標を表す。
確かに楽ですが,何を表したモノなのか分かりにくくなるのでバグの原因になることがある。
マクロ
for構文
REPマクロなどはタイプ数がかなり減る。
#define REP(i, n) for(int i = 0;i < n;i++) #define REPR(i, n) for(int i = n;i >= 0;i--) #define FOR(i, m, n) for(int i = m;i < n;i++) int main(){ REP(i, 10){...} //i=0~i=9までのループ FOR(i, 3, 10){...} //i=3~i=9までのループ REPR(i, 10){...} //i=10~i=0までのループ }
INF
INFでものすごく大きな数や,-INFでものすごく小さい数を表すことができます。
INF,-INFで初期化しておくことで最小値や最大値を求める時に下のような書き方ができる。
#define REP(i, n) for(int i = 0;i < n;i++) #define INF 999999999 int main(){ int a[4]={2, 4, 6, 8}; int mini=INF, maxi=-INF; REP(i, 4){ mini=min(mini, a[i]); maxi=max(maxi, a[i]); } }
上のプログラムでminiには最小値である2が,maxiには最大値である8が代入されることになる。
またINFを32bit整数型の最大値などあまりに大きすぎる数にしていると以下のような時に正しく動かなくなる。
#define INF 2147483647 int main(){ int mini=INF, tmp=1; mini=min(mini+tmp, 0); }
まあ当たり前と言えば当たり前なんだけどね…
私もコレでバグってからはおとなしく999999999にしてます,はい。
#define INF 1145141919
コレでは臭すぎるので論外。
long long
数字が32bitに収まらないなら64bitにすれば良いじゃない。
そんな時にはlong long。
これも楽しましょう。
#define llong long long int main(){ llong n=1145141919810; }
llとかしちゃう人もいます。
dy, dx
座標上を探索する時に便利。
int dy[]={0, 0, 1, -1}; int dx[]={1, -1, 0, 0}; int main(){ int ny, nx; //今いる座標。 REP(i, 4){ int ty=ny+dy[i]; int tx=nx+dx[i]; //探索対象にする座標。 } }
斜めに移動できるとかいう条件があればその都度足していけば良い。
bits/stdc++.h
#include <bits/stdc++.h>
僕も最近知ったんだけどこれでC++の標準ライブラリが全部インクルードできるんだと。
すごい時代になったもんだよ,まったく。
え?使わないモノをわざわざインクルードしたくないって?
お前育ち良いね。
STLのデータ構造
vector, set, map, queue, stack, priority_queueといろいろあるよ!うれしいね!!
使い方なんてその都度ググっていけば良いよ。
stackとかqueueとかなんだよって人はデータ構造について勉強しましょう。
リンク先のコレクションってところです。
アルゴリズムとデータ構造 | ++C++; // 未確認飛行 C
STLのアルゴリズム
sort, stable_sortなどこっちもいろいろあるよ!よかったね!!
sortとかstable_sortとかなんだよって人はアルゴリズムについて勉強しましょう。
リンク先のアルゴリズムってところです。
アルゴリズムとデータ構造 | ++C++; // 未確認飛行 C
他のSTLアルゴリズムをもっと詳しく知りたいって人は下のリンク先を見ておくと良いでしょう。
標準テンプレートライブラリ(STL) アルゴリズム - Qiita
共通アルゴリズム - STL
競プロでよく使うのはsort, upper_bound, lower_bound, min, max, swapあたりでしょうかね。
長くなったし疲れたしそろそろ終わりにしたい。
そうしましょう。
最後に,僕が競プロ特有な書き方を覚える時に使用したサイトや方法と僕の競プロ用テンプレートを置いておきます。
AtCoder (アットコーダー)
日本人で競プロやってる人はほぼみんな知ってるであろうAtCoder。
終わったコンテストでは他人のソースコードを見ることができるので参考にするのが良い。
競技プログラミング特有の変な実装テク // ichyo.jp
この記事の内容と結構かぶっているところがあるのは僕がこのいちょう(@ichyo)さんの記事を参考にAtCoderなどでソースコードを読んでいたからです。
僕よりも数百倍詳しく説明してくれてます。
競技プログラミング Wiki*
C++だけじゃなくJavaについても書いてあるので,Javaの経験ある人はココ見てみると良いかも。
~競プロ用テンプレート~
#include <bits/stdc++.h> #define REP(i, n) for(int i = 0;i < n;i++) #define REPR(i, n) for(int i = n;i >= 0;i--) #define FOR(i, m, n) for(int i = m;i < n;i++) #define FORR(i, m, n) for(int i = m;i >= n;i--) #define SORT(v, n) sort(v, v+n); #define VSORT(v) sort(v.begin(), v.end()); #define llong long long #define pb(a) push_back(a) #define INF 999999999 using namespace std; typedef pair<int, int> P; typedef pair<llong, llong> LP; typedef pair<int, P> PP; typedef pair<llong, LP> LPP; int dy[]={0, 0, 1, -1, 0}; int dx[]={1, -1, 0, 0, 0}; int main(){ }
JOI 予選 2015/2016 参加記
せっかくはてなブログを作ったのこのあいだのJOI予選について書きます。
ソースコードはあまりに汚い上,間違えてると思うので読まないほうが良いです。
すいません読まないでください!なんでもしますから!
9~10月
競プロの勉強会で知り合った人にJOI参加を勧められたのでとりあえず参加を志す。
今までAtCoderメインでやっていた競プロをAOJでJOIの過去問を解くようにした。
「DPわかんない」って週一で言うようになる。
11月
問題4から先がネックだと思ったので類似問題(DP中心)を解くようにした。
とか
が難しかったけどいい問題だと思った。
「bitDP頭良いなぁ」とか思ってたら期末試験の1週間前に突入したので競プロをしなくなった。
期末は12月8日までだったのでJOIまで残り5日ってところまでこの記事も飛びます。
あと競プロに集中するためにツイ禁(知り合いしかフォローしてない別アカは見てた。)をしたけどこれといって特に意味はなかった。
もう二度とやらねぇ。
12月8~12日
問題5, 6を解くようになったけど難しいのは本当に難しいから解説みたり他人のコード見て実装するだけ。
「こんなん本番で思いつけなんて無茶じゃね??」ってなる。
JOIの間にTwitterやって不正にならないようにこのあたりからなるべくTwitterやその他SNSに触れないようにしてた。
つもり。
12月13日が近づくにつれて”今更問題解いたところでどうにもならないだろ感”が襲ってくるようになってきたのでそんなにコーディングしてなかった。
予選
問題1
やるだけ。
なんだけど問題よく見てなくて思いのほか時間がかかってしまった。
int n[4]; int main(){ int maxi=0; REP(i, 4){ cin >> n[i]; } sort(n, n+4); FOR(i, 1, 4) maxi+=n[i]; int maxi2=0; int p=0; REP(i, 2){ cin >> p; maxi2=max(maxi2, p); } cout << maxi+maxi2 << endl; }
問題2
これもやるだけ。
なんだけど問題が分かりにくいなぁと思った。
問題1より時間はかからなかった。
int n, m; vector<int> a(100); int main(){ cin >> n >> m; REP(i, n) cin >> a[i]; FOR(j, 1, m+1){ REP(i, n-1){ if((a[i]%j)>(a[i+1]%j)){ swap(a[i], a[i+1]); } } } REP(i, n) cout << a[i] << endl; }
問題3
プーチン。
全探索した。
よくわかんないけどあってると思う。
vector<string> str(50); int n, m; int p(int h, bool w, bool b, bool r){ if(h==n){ if(!w||!b||!r) return INF; return 0; } int ret=0; if(!w&&!b&&!r){ int W=0; REP(j, m){ if(str[h][j]!='W') W++; } ret+=W; ret+=p(h+1, 1, b, r); } if(w&&!b&&!r){ int W=0; int B=0; REP(j, m){ if(str[h][j]!='W') W++; if(str[h][j]!='B') B++; } ret+=min(p(h+1, 1, 0, r)+W, p(h+1, 1, 1, r)+B); } if(w&&b&&!r){ int R=0; int B=0; REP(j, m){ if(str[h][j]!='R') R++; if(str[h][j]!='B') B++; } ret+=min(p(h+1, 1, 1, r)+B, p(h+1, 1, 1, 1)+R); } if(w&&b&&r){ int R=0; REP(j, m){ if(str[h][j]!='R') R++; } ret+=p(h+1, 1, 1, 1)+R; } return ret; } int main(){ cin >> n >> m; REP(i, n){ cin >> str[i]; } cout << p(0, 0, 0, 0) << endl; }
問題4
問題読んですぐに「これDPじゃないなぁ」って分かった。うれしい。
問題4でDPじゃないのはエラい久しぶりなことらしい。
頭悪いので愚直にシミュレートしたらケース1しか答えが出せなかった。
お昼ごはんを食べながら解いてた。
この時食べてたサンドイッチはやたらとおいしかった気がする。
int n, t, q; vector<llong> a(100000); vector<llong> x(100000); vector<bool> d(100000, 0); vector<bool> c(100000); map<llong, llong> m; int main(){ cin >> n >> t >> q; REP(i, n){ int f; cin >> a[i] >> f; d[i]= (f==1 ? 1 : 0); } REP(i, q){ cin >> x[i]; x[i]--; } FOR(i, 1, t+1){ REP(j, n){ if(d[j] && !c[j]){ if(m.find(a[j]+1)!=m.end()){ c[j]=1; c[m[a[j]+1]]=1; m.erase(a[j]); a[j]++; } else{ m[a[j]+1]=j; m.erase(a[j]); a[j]++; } } else if(!d[j] && !c[j]){ if(m.find(a[j]-1)!=m.end()){ c[j]=1; c[m[a[j]-1]]=1; m.erase(a[j]); a[j]--; } else{ m[a[j]-1]=j; m.erase(a[j]); a[j]--; } } } } REP(i, q){ cout << a[x[i]] << endl; } }
問題5
問題文がファンタジーすぎて「は?」ってなったけどよく読んだらまったくコレ。
探索+ダイクストラっていう解き方はあってるっぽいけど実装が悪すぎて答えだすのに時間がかかりすぎる。
ベルマンフォードでも間に合うらしい。
int n, m, k, s; int p, q; vector<int> c(100000, 0); vector<vector<bool> > v(100000, vector<bool>(100000, 0)); int bfs(int z){ queue<pair<int, int> > q; q.push(make_pair(z, s)); vector<bool> f(n, 0); f[z]=1; while(!q.empty()){ int u=q.front().first; int rr=q.front().second; q.pop(); if(rr<=0) continue; REP(i, n){ if(v[u][i] && !f[i]){ if(c[i]==0) c[i]=1; f[i]=1; if(rr-1>=0) q.push(make_pair(i, rr-1)); } } } } int dijkstra(int s){ priority_queue<pair<int, int> > pq; pq.push(make_pair(0, s)); vector<bool> f(n, 0); vector<llong> d(n, INF); d[s]=0; while(!pq.empty()){ int u=pq.top().second; pq.pop(); f[u]=1; if(u==n-1) return d[u]-c[n-1]; REP(i, n){ if(c[i]==-1) continue; if(!f[i] && v[u][i]){ if(d[i]>d[u]+c[i]){ d[i]=d[u]+c[i]; pq.push(make_pair(d[i]*(-1), i)); } } } } return d[n-1]-c[n-1]; } int main(){ cin >> n >> m >> k >> s; cin >> p >> q; int t[k]; REP(i, k){ ifs >> t[i]; t[i]--; c[t[i]]=2; } REP(i, m){ int f, t; ifs >> f >> t; f--; t--; v[f][t]=v[t][f]=1; } REP(i, k) bfs(t[i]); REP(i, n){ if(c[i]==2) c[i]=-1; else if(c[i]==1) c[i]=q; else c[i]=p; } cout << dijkstra(0) << endl; }
予選終了後
Twitterで答え合わせをするなどした。
一応提出ミスとかそもそも答え間違えてるとかなければ380点っぽいです。
ボーダー何点なんかなぁ…
JOI予選落ちの音がする #五七五
— YuMai (@YuMai6) December 13, 2015
12月18日 追記
問題5のケース2が間違っていたので,360点でした。
ボーダーが360点らしいのでギリギリAランクで本選に出場できることになりました。
本選まであと2ヶ月弱はあるので精進していきたい。
ボーダーが360点なのでギリギリってやつだ。 pic.twitter.com/d6fAJFKlwe
— YuMai (@YuMai6) 2015, 12月 17