【AtCoder:6回目】diverta 2019 Programming Contest 2の振り返り(Ruby)

【目次】

【本題】

振り返り

今回は 6/9(日)に開催されたAtCoder Beginner Contest 129の振り返りを行います。

今回は1問しか回答できませんでした・・・

やっぱりアルゴリズムの勉強しないと、300点以上の問題を突破するのは厳しいですね・・・

業務のキャッチアップがあるので、なかなか勉強する時間が取れない・・・

A - Ball Distribution

問題文 高橋君は N 個のボールを K 人に配ろうとしています。

それぞれの人がボールを 1 個以上受け取るような配り方の中で、ボールが最も多い人と最も少ない人のボールの個数の差が最大で何個になるか求めてください。

制約 1 ≤ K ≤ N ≤ 100 入力は全て整数である

全員に1個づつボールを配り切って、残りを一人に配れば、それが差が最大になるパターンです。

また、一人しかいない場合は、差は0にする必要があります。

そう考えて、作ったコードがこちらです。

n,k=gets.split.map &:to_i
 
if k == 1
  puts 0
else
  puts n - k
end

でも、もっと良い方法がありました。

n,k=gets.split.map &:to_i
puts n%k

剰余を求めても同じ結果が得られました・・・

ユーザーに仮登録を行わせる理由

【結論】

・会員登録の過程で、本登録用のリンクを記載したメールを送る(サイトへ必要事項を入力するだけで会員登録が出来ない)のには、いくつか理由がある

・一つは、悪意あるユーザーの不正登録を防ぐ為。もう一つは、ユーザーのメールアドレスに正常にメールを送れるのを確認する為。

・とはいえ、手順が複雑化してユーザーの離脱を招く恐れがある為、現在はソーシャルログインなどを活用して、メール認証の手順を省く努力がなされている。

【目次】

【本題】

Webサイトでの会員登録の手順

多くのサイトでは、会員登録を行う際に、以下のような手順を踏みます。

1:ユーザーに対してメールアドレスの入力を要求

2:そのメールアドレスに対して本登録用のリンクを記載したメールを送信

3:リンクから遷移した先のフォームに必要事項を入力

4:本登録が完了

なぜ、いきなり本登録の画面に遷移さえず、メール送信を経る必要があるのでしょうか?

今回は、その理由をまとめました。

不正登録を防ぐ

まず第一に、不正登録を防ぐという理由が上げられます。

もし、メール受信を経ずに、ページ上での必要事項の入力だけで会員登録が完結してしまうと、際限なくアカウントを作成する事ができます。

これにより、アカウントを不正利用されてしまう恐れがある為、手順を複雑にする事で、そういった不正登録を防ぐという理由です。

メールアドレスが有効か確認する

もう一つは、メールアドレスが有効か確認する為です。

ユーザーがメールアドレスを間違って入力してしまった場合、そのユーザーとの連絡手段が無くなってしまいます。

そうすると、緊急の要件が伝えられませんし、キャンペーンなどのお知らせも送る事ができません。

なので、メールアドレスを受信させる手順を経る事で、ユーザーの登録したメールアドレスが正常に受信できる状態にあるのか確認をしています。

面倒さへの対処(ソーシャルログイン)

とはいえ、こういったメール認証を経るのは、手順が増えるのでユーザーに敬遠されてしまい、会員登録のハードルを上げてしまいます。

なので、昨今はソーシャルログインによる会員登録の簡略化が図られています。

また、アプリであれば端末情報を読み込んでユーザーを識別する事もできるので、最近は会員登録不要で利用できるアプリも増えてきています。

参考情報

仮登録メール送信後の落とし穴 ユーザーを逃さないために覚えておきたいこと:MarkeZine(マーケジン)

https://www.glpgs.com/d-blog/archives/980

登録完了メールを送る~メールアドレスの認証の必要性 | 一からWebサービス&アプリを作る日誌!

Webサービス開発で知らないと損する!登録フォームでユーザーを逃さないのために必要な5つの手法 | Webロケッツマガジン

サイトの仮登録の意味について -最近あるサイトを利用しようと情報登録- その他(パソコン・スマホ・電化製品) | 教えて!goo

マウスオーバーすると、ビュッと表示されるアレ、「ツールチップ」って言うらしい

【結論】

・「ツールチップ」とは、アイコンなどをマウスオーバーした際に、表示される補足説明の表示

・見慣れないアイコンなどを理解する手助けになる

・そのまま表記するよりUIが簡潔になり、また最小限の動作で表示されるので、ユーザーの負担が少ない

【目次】

【本題】

ツールチップについて

ツールチップとは、オブジェクト(アイコンなど)をマウスオーバーさせた際に、そのオブジェクトに対する補足説明などを表示させる機能です。

例えばQiitaだと、ユーザーアイコンをマウスオーバーさせると、そのユーザーの詳細情報が表示されます。

↓参考画像

ツールチップのメリット

ツールチップを利用するメリットとしては、UIを簡潔にする効果があります。

ツールチップを利用しないと、説明文をそのまま表記することになりますが、文字数が多くなるほど、画面構成が複雑になり、見辛いUIになります。

また、マウスオーバーするだけで表示されるので、少ない操作でユーザーに情報を提供する事ができます。

ツールチップが適さないシーン

この様に、非常に有用なツールチップですが、利用が適さない場合もあります。

それはモバイル端末など、タッチパネルで操作する端末で表示させる場合です。

モバイル端末ではマウスオーバーが出来ないので、非常に使い勝手が悪くなります。

また、マウスオーバーを外すと、表示が消えてしまうので、ツールチップ内の文字をコピーしたりクリックしたりする操作を組み込む事が出来ません。

それらに注意して、ツールチップは利用すると良いと考えられます。

参考情報

ツールチップ - Wikipedia

UIデザインにおける適切なツールチップの使い方 | UX MILK

【AtCoder:5回目】AtCoder Beginner Contest 129の振り返り(Ruby)

【目次】

【本題】

振り返り

今回は 6/9(日)に開催されたAtCoder Beginner Contest 129の振り返りを行います。

今回も2問しか回答できませんでした・・・

やはり何も練習しない状態のままだと全く太刀打ち出来ないですね・・・

A - Airplane

問題文 空港 A, B, C があり、それぞれの空港の間では、双方向に飛行機が運航しています。

空港 A, B 間の飛行時間は片道 P 時間、空港 B, C 間の飛行時間は片道 Q 時間、空港 C, A 間の飛行時間は片道 R 時間です。

いずれかの空港からスタートして他の空港に飛行機で移動し、さらにそのどちらでもない空港に飛行機で移動するような経路を考えます。

飛行時間の和は最短で何時間になるでしょうか。

経路のパターンは6通り存在しますが、向きが逆なだけのパターンが半分なので、飛行距離のパターンは3通りです。

そして飛行距離については、全ての合計から選択していない経路の距離を引けば導き出せます。

つまり、選択しなかった経路が最長の場合、最短の経路となると考えて、下記の様に実装しました。

p,q,r = gets.split(" ").map &:to_i
puts (p+q+r) - [p,q,r].max

他にも面白い方法がありました。

P, Q, R = gets.split(" ").map(&:to_i)
 
a = [P, Q, R]
a.sort!
puts a[0] + a[1]

これは各経路の距離を昇順で並べ替えて、小さい順に2つ合計するという方法です。

B - Balance

問題文 1 から N の番号がついた N 個の重りがあり、番号 i の重りの重さは W i です。

ある整数 1 ≤ T < N に対してこれらの重りを、番号が T 以下の重り と 番号が T より大きい重りの 2 グループに分けることを考え、それぞれのグループの重さの和を S 1 , S 2 とします。

このような分け方全てを考えた時、 S 1 と S 2 の差の絶対値の最小値を求めてください。

差の絶対値の理論上の最小値は0です。

そして0になるのは、二つの重りが両方とも合計値の半分になった時です。

なので、合計値の半分を超えるまで重りを足していき、超えた時点と超える直前の重りの合計の差を比較して、より小さい方を選択する方法で実装しました。

n = gets.to_i

w = []
while w1 = $stdin.gets do
  w << w1.chomp.split(" ")
end

sum = 0
w[0].each{|i| sum += i.to_i }

half_over = 0
index = 0
while half_over < sum/2
  half_over += w[0][index].to_i
  index += 1
end

half_less = half_over - w[0][index-1].to_i

if half_over - (sum - half_over)  > (sum - half_less) - half_less
  puts (sum - half_less) - half_less
else
  puts half_over - (sum - half_over)
end

よりコード量が少なくなる実装方法もありました。

n = gets.to_i
ary = gets.split.map(&:to_i)
 
sum1 = 0
sum2 = ary.inject(:+)
ans = 10000000
 
n.times do |i|
  sum1 += ary[i]
  sum2 -= ary[i]
  ans = [ans, (sum1 - sum2).abs].min
end
 
puts ans

こちらは全パターンで差の絶対値を求めて、最小値だった場合に「ans」へ格納する方法です。

TCP/IPモデルのレイヤー別のセキュリティ対策

【結論】

・インターネットの通信は、いくつかの通信プロトコルからなっており、それらの階層構造を表した代表的なものに「TCP/IP」がある

・「TCP/IP」は、アプリケーション層・トランスポート層・インターネット層・ネットワークインターフェース層に階層分けされている

・セキュリティ対策においても、それぞれに層に対して対策を講じる必要がある

【目次】

【本題】

TCP/IPモデルについて

ネットワークはLANケーブルや中を通るデータ、行き先を仕分けるプロトコルといった様々な仕組みが合わさって、目的地までデータを送ることができます。

これらの仕組みをOSIでは7層、TCP/IPモデルでは4層にレイヤー分けしています。

※画像引用元 TCP/IPとは

Webサービスのセキュリティ対策は、これらの層ごとに対策を講じる必要があります。

今回は、それぞれの層における代表的なセキュリテイ対策をまとめます。

ネットワークインターフェース層の対策

ネットワークインターフェース層は、TCP/IPモデルをECサイトに例えると、商品を運搬する為の道路やトラックなどのようなものです。

サーバーを、物理的に外部の影響に晒されないようにする対策が取られます。

クラウドサービスのAWSでは以下の対策を行っています。

・データセンタ所在地の秘匿

・徹底した入退室管理

・監視カメラや侵入検知システム

・アクセスの記録・監査

トランスポート層/インターネット層の対策

トランスポート層/インターネット層は、TCP/IPモデルをECサイトに例えると、商品の宛名ラベルや梱包材などのようなものです。

偽造された宛名を検知して不正なアクセスを遮断する対策などが取られます。

代表的な対策は下記のようなものです。

ファイヤーウォール(セキュリティグループ/ネットワークACL)

接続元IPアドレス、接続先IPアドレス、通信ポートで通信の制御を行っています。

・IDS/IPS

IDS:Intrusion Detection System(侵入検知システム)、IPS:Intrusion Prevention System(侵入防止システム)

ファイヤーウォールで通過したデータの内容を確認し、不正が無いかを確認。IDSではそれを検知、IPSでは防御します。

アプリケーション層の対策

アプリケーション層は、TCP/IPモデルをECサイトに例えると、商品そのものを表します。

ネットワーク上では判断できなかった攻撃を検知する対策が取られます。

代表的なものは下記です。

・WAF

ネットワーク上では判断できなかったアプリケーション層の内容を解析し、脆弱性を突くような内容のデータをブロックします。

参考情報

TCP/IPとは

「active_link_to」で表示中のページのリンクへ動的にclassを付与する方法

【結論】

・active_link_toとは、Railsのgemの一種。link_toと同じように、リンクを生成する「active_link_to」メソッドを利用できるようになる

・「active_link_to」メソッドは、リンク先のページを開いていると、リンクに「class=“active”」を付与する

・サイドバーをハイライトさせたり、リンクを無効化させる際に利用できる

【目次】

【本題】

サイドバーなどを制作する際、リンク先のページを開いている際には、該当リンクの背景色などを濃くしたりして、強調したい場合があります。

※参考例(Qiita)

Qiitaの場合、どの様にこれを実装しているのかデベロッパーツールで確認してみると、該当リンクに「p-home_menuItem-active」というクラスを付与している様です。

この様に動的にclassを付与したい場合、Railsでは「active_link_to」というgemを利用する方法があります。

「active_link_to」とは、Railsのgemの一種です。

これを導入すると、link_toと同じように、リンクを生成する「active_link_to」メソッドを利用できるようになります。

「active_link_to」メソッドでリンクを生成すると、リンク先を開いている時は、そのリンクに「class=“active”」を付与する事ができます。

後は、この「class=“active”」に対して、CSSを設定することで、リンクをハイライトさせたり、無効化させたりする事が可能となります。

実装方法

まずはgemを導入します。

gem 'active_link_to'

後は、link_toと同じ要領で、「active_link_to」を利用するだけで、動的にactiveクラスを付与できるリンクが生成されます。

active_link_to 'Users', '/users'
# => <a href="/users" class="active">Users</a>

参考情報

GitHub - comfy/active_link_to: Rails view helper to manage "active" state of a link

【AtCoder:5回目】AtCoder Grand Contest 034の振り返り(Ruby)

【目次】

【本題】

振り返り

今回は 6/2(日)に開催されたAtCoder Grand Contest 034の振り返りを行います。

AtCoder Grand Contest 034 - AtCoder

今回は、一番簡単な問題も配点400点という事もあり、一問も解けませんでした・・・

それでもレートは上がるんですね・・・

A - Kenken Race

問題文 N 個の一列に並んだマス目があり、左から順に番号 1 , 2 , . . . , N がついています。長さ N の ., # からなる文字列 S が与えられ、 S の i 文字目が # のときマス目 i には岩が置かれており、 S の i 文字目が . のときマス目 i には何も置かれていません。

最初、マス目 A にすぬけ君、 B にふぬけ君がいます。

あなたは以下の操作を好きなだけ繰り返すことができます。

すぬけ君かふぬけ君を選び、 1 マス右か 2 マス右にジャンプさせる。このときジャンプ先にマスが存在しなければならず、またそのマスに岩が置かれていたりもう一人がいてはならない。 あなたはこの操作を繰り返し、マス目 C にすぬけ君が、 D にふぬけ君がいるようにしたいです。

このようなことが可能かどうかを判定してください。

途中まで書いたコードはこちらです

n,a,b,c,d = gets.split(" ").map &:to_i
s = gets.split("")

blocks = [n+1, n+2]

s.each.with_index(1) do |block, i|
  blocks << i if block == '#'
end

while !( a == c && b == d ) || !( blocks.include?(a+1) && blocks.include?(a+2) && blocks.include?(b+1) && blocks.include?(b+2) )
  if a != c
    if !blocks.include?(a+1) && !(a+1 == b)
      a += 1
    elsif !blocks.include?(a+2)
      a += 2
    end
  end

  if b != d
    if !blocks.include?(b+1)
      b += 1
    elsif !blocks.include?(b+2)
      b += 2
    end
  end
end

if a == c && b == d
  puts 'Yes'
else
  puts 'No'
end

ポイントは、下記の2点です。

・目的地までの途中で、岩が2個連続で続くと移動不可

・C > Dの時は、追い抜く為に3マスの空きが必要

これで作り変えたのが、下記のコードです

n, a, b, c, d = gets.chomp.split(' ').map(&:to_i)
s = gets.chomp
 
block_a = s[(a-1)..(c-1)]
block_b = s[(b-1)..(d-1)]
 
if c > d
  block = s[(b-2)..d]
  unless block.include?('...')
    puts 'No'
    exit
  end
end
 
if block_a.include?('##') || block_b.include?('##')
  puts 'No'
else
  puts 'Yes'
end

【AtCoder:4回目】M-SOLUTIONS プロコンオープンの振り返り(Ruby)

【目次】

【本題】

振り返り

今回は 6/1(土)に開催されたM-SOLUTIONS プロコンオープンの振り返りを行います。

M-SOLUTIONS Programming Contest - AtCoder

今回も2問しか回答できませんでした・・・

3問目は期待値を求めろという内容で、数学が完全に忘却の彼方の私にはハードルが高かったです・・・

A - Sum of Interior Angles

問題文 3 以上の整数 N が与えられます。 正 N 角形の内角の和を求めてください。

ただし、度数法を用いて、単位は出力しないでください。

多角形の内角の和は「 180 * (n-2)」で求められるので、それをそのまま当てはめれば良いと考えました。

n = gets.to_i
 
puts 180 * (n-2)

B - Sumo

問題文 高橋君は相撲の大会に参加しています。 大会は 15 日間行われ、高橋君は 1 日 1 番の取組を行います。 また、高橋君は 8 番以上勝つと次の大会にも参加できます。

k 日目までの取組が終了しました。 高橋君の取組の結果が o, x からなる長さ k の文字列 S で与えられます。 S の i 文字目が o ならば高橋君が i 日目の取組で勝ったことを、 x ならば負けたことをそれぞれ表します。

高橋君が次の大会にも参加できる可能性があるならば YES を、 そのような可能性がないならば NO を出力してください。

15回中、8回以上勝つと大会へ参加出来るという事は、8回負けると大会への参加の可能性が無くなると言い換えられます。

なので、8回以上負けた場合は「NO」で、それ以外は「YES」になると考えました。

s = gets.split("")
 
lose = 0
 
s.each do |i|
  lose +=1 if i == "x"
end
 
if lose >= 8
  puts "NO"
else
  puts "YES"
end

三項演算子を使えば、もっとシンプルになります

puts gets.count("x") >= 8 ? "NO" : "YES"

puts/print/pの違い(Ruby)

【結論】

Rubyで標準出力に出力する方法は、標準ライブラリに3種類(puts/print/p)存在する

・printは、引数のオブジェクトを文字列に変換し、標準出力に出力する。putsは、更に改行を加える。戻り値はnilになる。

・pは、引数のオブジェクトを分かりやすい文字列にして標準出力に出力します。戻り値は引数のオブジェクトになる。

【目次】

【本題】

標準出力に出力する方法

ターミナル上でコマンドを実行して、標準出力(画面)に出力する場合、Rubyでは3種類の方法があります。

今回は、それらの違いについてまとめます。

print

printは、引数のオブジェクトを文字列に変換し、標準出力に出力するメソッドです。

str = "ピコピコ太郎"
arr = ["apple", "pen", "pineapple"]

print str
ピコピコ太郎=> nil

print arr
["apple", "pen", "pineapple"]=> nil

puts

putsも、引数のオブジェクトを文字列に変換し、標準出力に出力するメソッドです。

printとの違いは、改行を追加するという点です。

str = "ピコピコ太郎"
arr = ["apple", "pen", "pineapple"]

puts str
ピコピコ太郎
=> nil

puts arr
apple
pen
pineapple
=> nil

p

pは、引数のオブジェクトを分かりやすい文字列にして標準出力に出力します。

また、戻り値はnilではなく、引数にしたオブジェクトになります。

str = "ピコピコ太郎"
arr = ["apple", "pen", "pineapple"]

p str
"ピコピコ太郎"
=> "ピコピコ太郎"

p arr
["apple", "pen", "pineapple"]
=> ["apple", "pen", "pineapple"]

参考情報

https://ref.xaio.jp/ruby/classes/kernel/puts

https://ref.xaio.jp/ruby/classes/kernel/print

https://ref.xaio.jp/ruby/classes/kernel/p

文字を省略する際に、全角/半角を考慮する方法と、中間で省略する方法(Ruby)

【結論】

・文字を省略する際、全角/半角を考慮しないとレイアウトが崩れる場合がある。また末尾で省略すると、ファイル名なら拡張子が見えなくなるという問題が発生する。

・全角/半角を考慮する場合は、bytesizeメソッドを利用する

・中間で省略する場合は、文字を前半部と後半部に分けて結合する

【目次】

【本題】

文字の省略を柔軟に行いたい

前回文字を省略して表示する方法を投稿しましたが、これには欠点があります。

ryoutaku-jo.hatenablog.com

それは、文字を中間で省略できないという事と、文字の長さではなく数を基準に省略する位置を決めるので、同じ文字数で省略しても、全角/半角で最終的な長さが大きくズレてしまうという事です。

#文字を中間で省略できない
str = "abcdefg"
str.truncate(6)
=> "abc..."
#同じ文字数で省略しても、全角/半角で最終的な長さが大きくズレてしまう
short = "abcdefghijklnmop"
long = "あいうえおかきくけこさしすせそ"

short.truncate(10)
=> "abcdefghij..."

long.truncate(10)
=> "あいうえおかきくけこ..."

文字が末尾で省略されてしまうと、ファイル名を出力する場合は、拡張子が見えないという問題が発生します。

全角と半角で最終的な長さが異なると、意図せずレイアウトが崩れてしまう要因となり得ます。

この問題に対処する方法を、今回はまとめます。

文字を中間で省略する

今回の方法は、文字を前半部と後半部で分けて、間に「...」を挟んで結合するというやり方です。

str = "abcdefghijk"
str_size = str.size
split_point = 2

str[0, split_point] + '...'+ str[str_size-split_point, str_size] 
=> "ab...jk"

この様にして、文字を中間で省略する事が可能です。

全角/半角を見分ける

Rubyには全角を見分けるメソッドはありませんが、文字のバイト数を調べるメソッドは存在します。

https://ref.xaio.jp/ruby/classes/string/bytesize

文字のバイト数は、半角で1バイト、全角で3バイトなので、この情報を基に識別します。

例えば、全角を半角の2倍として、文字数が半角10文字相当より多い場合は、前半・後半を2文字づつに分割して中間で省略する場合の、下記の様に行います。

def truncate_str(str)
  size = str.size
  byte = str.bytesize
  total_size = ((byte - size) / 2) + size
  if total_size > 10
    split_point = 2
    str[0..split_point-1] + '...' + str[(size - split_point)..size]
  else
    str
  end
end

short = "abcdefghij"
long = "あいうえおかきくけこ"

truncate_str(short)
=> "abcdefghij"

truncate_str(long)
=> "あい...けこ"

これで全角と半角で長さに大きな差が開くことを防げます。

全て全角だった場合に合わせて、省略する文字数を調整すれば、レイアウトが崩れる事は無くなります。

参考情報

https://ref.xaio.jp/ruby/classes/string/bytesize