Open40

paiza Ruby学習ログ

アライ リョータアライ リョータ

標準入力

変数名 = gets # 標準入力を変数に代入
puts 変数名 # 入力した標準入力を出力
gets.to_i # 標準入力を整数に変換
アライ リョータアライ リョータ

複数行の標準入力を取得する

s = gets
puts s
s = gets
puts s

より短いコード例

puts N.times.map {gets.chomp}
# 入力
hello
world
# 出力
hello
world

上記のように、2回に分けて標準出力の取得・出力をすることで表示できる。

ループ処理

count = gets.to_i # 入力の1つ目を整数として取得

for i in 1..count # countに代入された整数の回数まで繰り返す
  s = gets # 2つ目の標準入力を取得すてlineに代入
  puts s 指定された回数まで入力の値を順に出力
end 
# 入力
3
hoge
fuga
piyo
# 出力
hoge
fuga
piyo
アライ リョータアライ リョータ

1行のデータの入力

s = gets.chomp
puts s

3行のデータの入力

# 入力を3つ用意してあげるだけ
s = gets.chomp
puts s
s = gets.chomp
puts s
s = gets.chomp
puts s

N行のデータの入力
入力される値

  • 1行目でNが与えられます。
  • 続くN行の各行で文字列が与えられます。
  • 入力値最終行の末尾に改行が1つ入ります。
  • 条件:1 <= N <= 10
n = gets.chomp.to_i
n.times do
  s = gets.chomp
  puts s
end

3つのデータの入力

  • 3つの文字列が半角スペース区切りで1行で与えれます。
  • 入力値最終行の末尾に改行が1つ入ります。
s = gets.chomp
t = s.split
puts t[0]
puts t[1]
puts t[2]

N個のデータの入力

n = gets.to_i
s = gets.chomp
t = s.split
n.times do |i|
  puts t[i]
end

カンマ区切りの3つのデータの入力

s = gets.chomp
t = s.split(',')
puts t

カンマ区切りのN個のデータの入力

n = gets.to_i
s = gets.chomp
t = s.split(',')
n.times do |i|
  puts t[i]
end
アライ リョータアライ リョータ

入力される値
入力は以下のフォーマットで与えられます。

a b

条件
すべてのテストケースにおいて、以下の条件をみたします。

0 ≦ a ≦ 100
0 ≦ b ≦ 100
# 自然数の範囲内での計算であることを示している。
# 自分で回答したコード
n = gets
t = n.split(" ")

a = t[0].to_i
b = t[1].to_i

puts a + b
# 回答例
a, b = gets.split.map(&:to_i)

puts a + b
アライ リョータアライ リョータ

期待する出力
A を B で割った商を X、A を B で割った余りを Y としたとき、X と Y を半角スペース区切り一行で出力してください。末尾に改行を入れ、余計な文字、空行を含んではいけません。

👉 X・Yを1行・半角スペース区切りで表示する必要がある。
上記の条件を満たす出力パターン例

puts "#{X} #{Y}" # 変数を文字列として扱い、1行・半角スペース区切りで表示する
puts X.to_s + " " + Y.to_s # 考え方は1つ目の例と同様(やや冗長)
print X,(" "),Y # printはputsと違い改行しない
puts [X, Y].join(' ') # joinで配列の要素を連結し、連結する要素の間に半角スペースを置く
アライ リョータアライ リョータ

数値演算結果による分岐

入力される値

A B C

期待する出力
YESまたはNOを出力してください。
末尾に改行を入れ、余計な文字、空行を含んではいけません。

条件
すべてのテストケースにおいて、以下の条件をみたします。
・ A, B, C は 1 以上 200 以下の整数

最初に提出したコード

n = gets.split.map(&:to_i)
a = n[0]
b = n[1]
c = n[2]

if a * b <= c
    puts "YES"
else
    puts "NO"
end

修正後のコード

a, b, c = gets.split.map(&:to_i)

if a * b <= c
    puts "YES"
else
    puts "NO"
end

さらに修正版のコード

a, b, c = gets.split.map(&:to_i)

puts a * b <= c ? "YES" : "NO"

こんな記述もできる

puts gets.split.map(&:to_i).then { |a, b, c| a * b <= c ? "YES" : "NO" }
アライ リョータアライ リョータ

ある数字までの出力

入力される値
正の整数 N が 1 行で与えられます。

期待する出力
1 〜 N の整数を 1 から順に改行区切りで出力してください。
出力の末尾には改行を入れてください。

条件
すべてのテストケースにおいて、以下の条件をみたします。
・ 1 ≦ N ≦ 100

n = gets.to_i

(1..n).each do |i|
    puts i
end

その他の記述例

n = gets.to_i

1.upto(n) do |i|
  puts i
end
アライ リョータアライ リョータ

FizzBuzz

1 ~ 100 の整数に対して、3 と 5 の両方で割り切れるなら FizzBuzz を、 3 でのみ割り切れるなら Fizz 、5 でのみ割り切れるなら Buzz を改行区切りで出力してください。
また、どちらでも割り切れない場合は、その数字を改行区切りで出力してください。

入力される値
入力は与えられません、

期待される出力
3 と 5 の両方で割り切れるなら FizzBuzz を、 3 でのみ割り切れるなら Fizz 、5 でのみ割り切れるなら Buzz を改行区切りで出力してください。
また、どちらでも割り切れない場合は、その数字を改行区切りで出力してください。
出力の末尾には改行を入れてください。

条件
入力は与えられません。

(1..100).each do |i| # 整数1〜100をeach文で回す
    if i % 15 == 0
        puts "FizzBuzz"
    elsif i % 5 == 0
        puts "Buzz"
    elsif i % 3 == 0
        puts "Fizz"
    else
        puts i
    end
end

※timesメソッドを用いたコード例
カウンタは0から始まるため、1から始める場合は、num = i + 1のようなコードの指定が必要。

100.times do |i|
  num = i + 1
  if num % 15 == 0
    puts "FizzBuzz"
  elsif num % 5 == 0
    puts "Buzz"
  elsif num % 3 == 0
    puts "Fizz"
  else
    puts num
  end
end
アライ リョータアライ リョータ

1行・末尾に半角スペースを入れる

(1..10).each do |i|
  puts "#{i} " # 文字列として扱う形に変更している。#{}を用いているので''はNG!
end
アライ リョータアライ リョータ

joinメソッドは"配列"にしか使えない!

puts (1..10).to_a.join(' ') #Rangeオブジェクトをto_aメソッドで配列に変換し、joinメソッドを用いて、半角スペースで要素を連結している
# 1 2 3 4 5 6 7 8 9 10

1,000 個の数値を半角スペース区切りで出力
コード例

puts (1..1000).to_a.join(' ')
アライ リョータアライ リョータ

改行区切りの10個の入力データを1行・半角スペース区切りで出力

str = 10.times.map { gets.chomp }
puts str.join(' ')
アライ リョータアライ リョータ

大きな数値を 3 けたごとにカンマ区切りで出力

大きな数値 N が入力されます。3 けたごとにカンマ区切りで出力してください。
ただし、N のけた数は 3 の倍数です。

n = gets.chomp

puts n.to_s.reverse.scan(/\d{3}/).join(',').reverse
アライ リョータアライ リョータ

3 * 3 の出力

9 個の数値が半角スペース区切りで入力されます。この数値を 3 行 3 列の形式で出力してください。
具体的には、入力された数値を N_1 , N_2 , N_3 , ..., N_9 とするとき、 1 行目にはN_1 からN_3 を、2 行目には N_4 から N_6 を、3 行目には N_7 から N_9 を出力してください。
ただし、数値の間には半角スペースを、各行の末尾には、半角スペースの代わりに改行を入れてください。

自分が書いたコード

nums = gets.split(" ")
puts "#{nums[0]} #{nums[1]} #{nums[2]}"
puts "#{nums[3]} #{nums[4]} #{nums[5]}"
puts "#{nums[6]} #{nums[7]} #{nums[8]}"
#  一応、回答としては正しいけどセンスない…笑😂
アライ リョータアライ リョータ

N行の改行区切りの標準入力を一括で受け取る
※入力が3行の文字列の場合

lines = 3.times.map { gets.chomp }
puts lines

入力結果が
abc
def
ghi
だった場合、出力結果も同様に改行区切りで出力される。

※入力が3行かつ整数の和の場合

n, m, o = 3.times.map { gets.to_i } 
puts n + m + o
アライ リョータアライ リョータ

入力:1行・半角スペース区切り 出力:改行区切りの効率的なコード

puts gets.split(' ')
puts gets.split # splitメソッドは空白文字で区切るため(' ')は書かなくても良い
アライ リョータアライ リョータ

三項演算子での記法

購入金額が1000円以上の場合、100円割引した金額を出力。
購入金額が1000未満の場合、そのままの価格を出力。

自分が最初に書いたコード

n = gets.to_i

if n >= 1000
    puts n - 100
else
    puts n
end

三項演算子なら

n = gets.to_i

puts n >= 1000 ? n - 100 : n
アライ リョータアライ リョータ

絶対値を取得するメソッド

n = gets.to_i # 入力された値がマイナスの値でも、正の値が返される
puts n.abs
アライ リョータアライ リョータ
n = gets.to_i
n.times do
  a = gets.to_i
  puts a
end

do ~ endの代わりに{ }を使用するコード例

n = gets.to_i
n.times {puts gets.to_i}

{ } と do ~ end の違い

{ } と do ... end はどちらもブロックを作成するために使用できる。
{ }:通常、一行で簡潔なブロックを書くときに使用される。
do ~ end:複数行にわたるブロックや、より複雑な処理を書く時に使用されることが多い。

each文の{ }記述例

n = gets.to_i
(1..n).each {puts gets.to_i}
アライ リョータアライ リョータ

1 行目で与えられる N 個の整数の入力

1行・半角スペース区切りで整数NとN個の整数a_1 ~ a_Nが与えられる。

先頭の整数Nだけを取り出すコード例

input = gets.chomp.split.map(&:to_i) # 入力を半角スペース区切りで配列に格納し、整数に変換
n = input.shift # 先頭の要素Nを取り除く
input.each do |a| # 残った整数 a_1, ..., a_N を改行区切りで出力する
  puts a
end
input = gets.chomp.split.map(&:to_i)
n = input.shift
input.each { |a| puts a } # より簡潔な記述

今さらだけど、map(&:to)って何?

map メソッド
map メソッドは配列の各要素に対してブロック内の処理を適用し、その結果を新しい配列として返す。
例:配列の各要素に10を足した新しい配列を作成するコード

[1, 2, 3].map { |n| n + 10 } # => [11, 12, 13]

to_iメソッド
String クラスのメソッドで、文字列を整数に変換する。
例: "123".to_i は整数123を返す

&: シンタックス(シンボルto_proc)
Ruby では &: に続けてメソッド名のシンボル(:to_i など)を書くことで、そのメソッドを呼び出すブロックを簡単に作成できる。
このシンタックスは「シンボルto_proc」と呼ばれ、次のように使用される。

["1", "2", "3"].map(&:to_i) # => [1, 2, 3]

上記のコードと等価(やや冗長な記述)

["1", "2", "3"].map { |s| s.to_i } # => [1, 2, 3]

map(&:to_i) の働き
map(&:to_i)は配列の各要素にto_i メソッドを適用し、それらの結果を新しい配列として返す。
つまり、上記のコードは、文字列の配列を整数の配列に変換している。

例:ユーザーからの入力を受け取りそれを数値の配列に変換する場合、以下のように書けえる。

input = gets.chomp.split.map(&:to_i) # "1 2 3" を入力すると [1, 2, 3] が得られる

これは以下のステップを実行している。
gets.chompでユーザーの入力(例: "1 2 3")から末尾の改行を取り除く。
splitで空白文字を区切りとして配列 ["1", "2", "3"] を作る。
map(&:to_i)で配列の各文字列を整数に変換し、新しい配列 [1, 2, 3] を作成する。

アライ リョータアライ リョータ

timesメソッドとeachメソッドで混乱しがち🌀

eachメソッド
配列やハッシュなどのコレクションを順に繰り返し処理するために使う。

[1, 2, 3].each do |element|
  puts element
end
# 配列の各要素を出力する

timesメソッド
指定された回数だけ繰り返し処理を行うために使う。

3.times do |i|
  puts i
end
# 0から始まって2までの数値(合計で3回)を出力する
アライ リョータアライ リョータ

Rubyの処理の理解度を深めるために、あえて遠回りして結果を出力してみる

1 行目に整数 N が与えられます。
2 行目以降に、N 組の整数 a_i と b_i が N 行で与えられます。(1 ≦ i ≦ N)
N 組の a_i と b_i を改行区切りで出力してください。

入力される値
以下の形式で標準入力によって与えられます。

1 行目に 整数 N
2 行目から (N + 1) 行目に N 組の整数 a_i, b_i が N 行で与えられます。

N
a_1 b_1
...
a_N b_N

期待される出力
N 組の a_i と b_i を改行区切りで出力してください。
また、末尾に改行を入れ、余計な文字、空行を含んではいけません。

a_1 b_1
...
a_N b_N

要件を満たすシンプルなコード

n = gets.to_i # 入力1行目の整数Nをnに代入

n.times { puts gets.chomp } # 入力2行目〜N行目までの値を文字列として出力する作業をn回繰り返す

# 上記を省略しない場合
n.times do
  puts gets.chomp
end

解答例

n = gets.to_i

# 配列を持った2つの変数を用意する
a = Array.new(n) # n個のnilを持った配列を変数aに代入する
b = Array.new(n) # n個のnilを持った配列を変数bに代入する

n.times { |i| a[i], b[i] = gets.split(' ').map(&:to_i) }

n.times { |i| puts a[i].to_s + ' ' + b[i].to_s }

解答例を短縮しない場合のコード例

n = gets.to_i

a = Array.new(n)
b = Array.new(n)

n.times do |i|
  input = gets.split.map(&:to_i)
  a[i] = input[0]
  b[i] = input[1]
end

n.times do |i|
  puts a[i].to_s + ' ' + b[i].to_s
end

変数a, bにからの配列を用意する場合のコード例

n = gets.to_i

a = [] # 配列が空のため、後の処理で配列のサイズが動的に成長する
b = [] # 配列が空のため、後の処理で配列のサイズが動的に成長する

n.times do |i|
  input = gets.split.map(&:to_i)
  a[i] = input[0]
  b[i] = input[1]
end

n.times do |i|
  puts a[i].to_s + ' ' + b[i].to_s
end

どちらのアプローチも有効だが、前者の方法であらかじめ配列のサイズを指定すると、意図したサイズの配列が確実に生成されるため、配列のサイズを事前に知っている場合はこちらのほうがわずかに効率的。
また、配列のサイズが大きくなるときに、動的な配列のリサイズ処理のコストを避けることができる。

アライ リョータアライ リョータ

整数の組からの選択

1 行目に整数 N が与えられます。
2 行目以降に、N 組の整数 a_i と b_i が N 行で与えられます。(1 ≦ i ≦ N)
8 組目の a_i と b_i を出力してください。

入力される値
以下の形式で標準入力によって与えられます。
1 行目に 整数 N
2 行目から (N + 1) 行目に N 組の整数 a_i, b_i が N 行で与えられます。

N
a_1 b_1
...
a_N b_N

入力値最終行の末尾に改行が1つ入ります。
文字列は標準入力から渡されます。

期待する出力
8 組目の a_i と b_i を出力してください。
また、末尾に改行を入れ、余計な文字、空行を含んではいけません。

a_8 b_8

自分が想定したコードの組み立てイメージ

# 1行目の標準入力を受け取り、変数nに代入する
# 2行目以降の標準入力(a_1 b_1 ~)を、配列として変数に格納する
# 8行目のみを出力する

n = gets.to_i
lines = []

n.times { lines << gets.chomp }

puts lines[7]

解答例

n = gets.to_i

a = Array.new(n)
b = Array.new(n)
n.times { |i| a[i], b[i] = gets.split(' ').map(&:to_i) }

puts a[7].to_s + ' ' + b[7].to_s

a_1 b_iの形式で、aが文字列・bが整数の場合のコード例

n = gets.to_i # 1行目の整数Nをnに代入

a = Array.new(n) # 配列を格納する変数aを用意
b = Array.new(n) # 配列を格納する変数bを用意

n.times { |i| a[i], b[i] = gets.chomp.split } # a, bに文字列として半角文字の前後をそれぞれ格納

n.times { |i| puts a[i] + ' ' + b[i].to_s } # bの値を整数に変更 ※やらなくても要件は満たせるし、結局文字列に変換してるから意味なくね?

解答例

n = gets.to_i

s = Array.new(n)
a = Array.new(n)
n.times do |i|
  s[i], a[i] =
    gets.split(' ').map.with_index { |val, j| j == 1 ? val.to_i : val } # 先に型変換をしている(文字列として扱うか、整数として扱うか)
end
# |val, j|は、valが配列の要素、jがそのインデックス
# j == 1 ? val.to_i : valは三項演算子で、インデックスjが1のとき(つまり二番目の要素、整数が来るべき場所)、その要素を整数に変換し(val.to_i)、それ以外の場合(文字列)はそのままvalを使う

n.times { |i| puts s[i] + ' ' + a[i].to_s }

with_indexって何?

with_index メソッドは、イテレータ(例:map, each等)とチェーンして使われることが多いメソッド。
これを使用すると、イテレータが繰り返している要素のインデックス(位置)にアクセスすることができる。

具体的には、map.with_indexを使用すると、mapメソッドが繰り返し実行されるブロックに、現在の要素のインデックスが2番目の引数として渡される。
このインデックスはデフォルトで0から始まるが、with_indexに引数を与えることで、開始インデックスを変更することもできる。

with_indexの用例

["a", "b", "c"].each.with_index do |item, index|
  puts "#{index}: #{item}"
end

このコードは、配列の各要素とそのインデックスを出力する。

0: a
1: b
2: c
result = ["a", "b", "c"].map.with_index do |item, index| 
  [item, index]
end

# result => [["a", 0], ["b", 1], ["c", 2]]

このコードは、各要素とそのインデックスをペアにして新しい配列を作成する。

with_index が便利なのは、単に要素を繰り返すだけでなく、その要素が元の配列や列挙可能オブジェクトの中でどの位置にあるかを同時に知りたい場合。
特に、元の要素とインデックスの両方に基づいて処理を行いたいときに役立つ。

アライ リョータアライ リョータ

3 行 3 列の整数の入力

3 行 3 列の行列が与えられます。上から i 番目、左から j 番目の整数は a_{i,j} です。
3 行 3 列の行列をそのまま出力してください。

入力される値

3 行 3 列の行列が以下の形式で標準入力によって与えられます。

a_{1,1} a_{1,2} a_{1,3}
a_{2,1} a_{2,2} a_{2,3}
a_{3,1} a_{3,2} a_{3,3}

期待される出力

3 行 3 列の行列をそのまま出力してください。
また、末尾に改行を入れ、余計な文字、空行を含んではいけません。

a_{1,1} a_{1,2} a_{1,3}
a_{2,1} a_{2,2} a_{2,3}
a_{3,1} a_{3,2} a_{3,3}

自分が記述したコード

# 標準入力が3行とわかっていて、形も変更する必要がないと判断
# timesメソッドを使用して、3回出力を繰り返す

3.times {puts gets.chomp}

解答例

a = Array.new(3).map { Array.new(3) }

3.times do |i|
  row = gets.split(' ').map(&:to_i)
  3.times { |j| a[i][j] = row[j] }
end

a.each { |row| puts row.join(' ') }

解答の解説

a = Array.new(3).map { Array.new(3) }
p aの実行結果 => [[nil, nil, nil], [nil, nil, nil], [nil, nil, nil]]

# 2次元配列を初期化している。
# Array.new(3)は要素が3つの新しい配列を作成し、その各要素に対してブロック { Array.new(3) }を実行する。
ブロック内のArray.new(3)は、再び要素が3つの新しい配列を作成する。
結果として、3x3の2次元配列ができる(全ての要素は nil で初期化される)。
# 2次元配列:表(マトリックス)のような構造を持つデータ構造のこと
1  2  3
4  5  6
7  8  9
# 2次元配列で表現すると↓
[
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]
a = Array.new(3, Array.new(3))
a[0][0] = 1
a[1][1] = 2
a[2][2] = 3
p a  # => [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
3.times do |i|
  row = gets.split(' ').map(&:to_i) # 受け取った入力(文字列)を整数に変換している
  # => [8, 1, 3]
  # => [1, 2, 3]
  # => [3, 1, 8]

  3.times { |j| a[i][j] = row[j] }
  # rowは入力された行(i)の数値が格納された1次元配列
  # rowの列データ(配列に入った各要素)を示すのがj
  # row配列の各要素(列データ)を、ループ外の2次元配列aの i 番目の行の j 番目の列へと代入処理している
end

a.each { |row| puts row.join(' ') } # 2次元配列aをeach文で回す(空白文字で結合して出力)

アライ リョータアライ リョータ

2次元配列の応用問題

N行3列の整数の入力の場合のコード例

n = gets.to_i

a = Array.new(n).map { Array.new(3) } # # 列は3行と明示されているので注意

n.times do |i|
  row = gets.split.map(&:to_i)
  3.times { |j| a[i][j] = row[j] } # 列は3行と明示されているので3.timesで指定
end

a.each { |b| puts b.join(' ') }

3行M列の整数の入力の場合のコード例

m = gets.to_i

a = Array.new(3).map { Array.new(m) } # 行は3と明示されているので注意

3.times do |i| # 行は3と明示されているので、3.timesで指定
  row = gets.split.map(&:to_i)
  m.times { |j| a[i][j] = row[j] }
end

a.each { |b| puts b.join(' ') }

N行M列の整数の入力の場合のコード例

n, m = gets.split.map(&:to_i) # 1行目の標準入力で"N M"が与えられているのでそれぞれ変数に代入

a = Array.new(n).map { Array.new(m) }

n.times do |i|
  row = gets.split.map(&:to_i)
  m.times { |j| a[i][j] = row[j] }
end

a.each { |b| puts b.join(' ') }
アライ リョータアライ リョータ

N行M列のデータの入力:行ごとに要素数の異なる整数列の入力

shiftメソッドで、先頭の値を取り除く

n = gets.to_i

n.times do
  input = gets.split.map(&:to_i)
  input.shift
  puts input.join(' ')
end

dropメソッドを使用するコード例

n = gets.to_i

n.times do
  line = gets.split.map(&:to_i)

  values = line.drop(1) # 配列から先頭の要素を除外した新しい配列を作成
  
  puts values.join(' ') # 新しい配列の要素をスペース区切りで出力
end

dropメソッドは非破壊的変更。
shiftメソッドは破壊的変更。
https://docs.ruby-lang.org/ja/latest/method/Array/i/drop.html

アライ リョータアライ リョータ

文字列から指定した文字列を抽出するメソッド

s = gets.chomp
puts s[0,3] # s[開始位置, 長さ]
puts s.slice(0, 3) # s.slice(開始位置, 長さ)
puts s.chars[0, 3].join # s.chars[開始位置, 長さ]で、["p", "a", "i"]という配列を作り、joinメソッドで結合
アライ リョータアライ リョータ

九九の表の出力

timesメソッドを使用した場合のコード例

a = Array.new(9).map { Array.new(9) }

9.times do |i|
  9.times { |j| a[i][j] = (i + 1) * (j + 1) }
  # 最初に初期化した変数aの2次元配列の、i行目のj列の値を上書きする処理を繰り返す
  # aの配列インデックスにアクセスするためには0~8で指定する必要があるため+1している
  # 9.times { |j| a[i - 1][j - 1] = (i) * (j) }
end

a.each { |row| puts row.join(' ') }

eachメソッドを使用した場合のコード例

a = Array.new(9).map { Array.new(9) }

(0..8).each do |i|
  (0..8).each { |j| a[i][j] = (i + 1) * (j + 1) }
  # 9.times { |j| a[i][j] = (i + 1) * (j + 1) }でも可
end

a.each { |row| puts row.join(' ') }

もっとシンプルに記述する

(1..9).each do |i|
  row = (1..9).map { |j| i * j }
  puts row.join(' ')
end
アライ リョータアライ リョータ

【n * n の 2 次元配列の表示】N * N の九九表の出力

# ここでの標準入力は「3」とする
n = gets.to_i # 標準入力「3」を受け取り、nに代入

(1..n).each do |i| # 外側のループ:1〜3を繰り返す
  row = (1..n).map { |j| i * j } # 内側のループ:計算結果をrowに代入
  # 外側1回目のループ(i = 1):1 * 1, 1 * 2, 1 * 3の計算結果を、mapメソッドで配列化
  # 外側2回目のループ(i = 2):2 * 1, 2 * 2, 2 * 3の計算結果を、mapメソッドで配列化
  # 外側3回目のループ(i = 3):3 * 1, 3 * 2, 3 * 3の計算結果を、mapメソッドで配列化
  puts row.join(' ') # 半角スペースで結合して出力
end
アライ リョータアライ リョータ

【行によって長さが違う二次元配列の表示】すべての行の長さと値が不定な 2 次元配列の出力

入力される値

N M
A_1 A_2 A_3 ... A_N
B_1 B_2 B_3 ... B_M

期待される出力

A_1 ... A_{B_1}
A_{B_1+1} ... A_{B_1+B_2-1}
A_{B_1+B_2} ... A_{B_1+B_2+B_3-1}
...
A_{B_1+...+B_{M-1}} ... A_{B_1+B_2+...+B_M}

入力例

10 4
1 2 3 4 5 6 7 8 9 10
2 6 1 1

出力例

1 2
3 4 5 6 7 8
9
10

入力値最終行の末尾に改行が1つ入ります。
文字列は標準入力から渡されます。

n, m = gets.split.map(&:to_i)
a = gets.split.map(&:to_i)
b = gets.split.map(&:to_i)

start = 0 # aの開始位置を初期化

b.each do |i|
  puts a[start, i].join(' ') # a[開始位置, 要素の数]
  start += i # putsの実行後に開始位置にiを加算
end
# 処理イメージ
a = [1, 2, 3, 4, 5, 6, 7, 8]
b = [3, 2, 3]
の場合、ループは以下のように動作する。

1回目のループ:i = 3、start = 0、aから [1, 2, 3] を取り出して出力
2回目のループ:i = 2、start = 3(前回のループで更新)、aから[4, 5]を取り出して出力
3回目のループ:i = 3、start = 5(前回のループで更新)、aから [6, 7, 8] を取り出して出力
アライ リョータアライ リョータ

罫線入りの九九の表の出力

コード例
むずい…。

kuku = Array.new(9) { Array.new(9) }

(0..8).each do |i|
    kuku[i] = (0..8).map { |j| sprintf('%2d', (i + 1) * (j + 1)) }.join(' | ')
end

kuku.each_with_index do |row, index|
  puts row
  puts '=' * row.length if index < 8
end
アライ リョータアライ リョータ

ペアの数値が入った表を罫線入りで出力

h, w, a, b = gets.split.map(&:to_i)

row = Array.new(w, "(#{a}, #{b})").join(' | ')

eq_line = '=' * row.length

h.times do |i|
    puts row
    puts eq_line unless i == h - 1
end
アライ リョータアライ リョータ

ピラミッドの作り方

ピラミッドは1段ごとに×1人の人員が必要になる。n段で必要になる人員数を求めよ。
入力:ピラミッドの段数を表す整数Nが与えられる

自分が書いたコード

n = gets.to_i

human = 0

n.times do |i|
  human += (1 * (i + 1)) # i(カウンタ変数)は0から始まるので+ 1とする
end

puts human

処理速度を意識したコード例 ※GPTに聞いた

n = gets.to_i
total_people = (n * (n + 1)) / 2
puts total_people

公式の解説

もちろんです。ピラミッドの問題で使われる等差数列の和の公式を詳しく説明します。

等差数列の和の公式は、等差数列(各項が一定の差で進む数列)の最初の項からある項までの合計を求めるための公式です。ピラミッドの問題では、各段に必要な人数が1人、2人、3人...と1人ずつ増えていくので、これは等差数列になります。

ここで、n はピラミッドの段数です。この公式を使うと、特定の段数のピラミッドに必要な総人数を計算することができます。

公式の導出
この公式の導出は、等差数列の性質を利用します。例えば、ピラミッドが3段ある場合、必要な人数は次のようになります:

1段目: 1人
2段目: 2人
3段目: 3人

これを合計すると、1 + 2 + 3 = 6 人が必要です。これは 3 * (3 + 1) / 2 = 6 と計算できます。

一般化すると、任意の段数 n に対して、必要な人数の合計は n * (n + 1) / 2 となります。この式は、1から n までの自然数の和を計算する公式です。

公式の意味
公式における n * (n + 1) の部分は、1から n までの数を2回(一度は順番に、もう一度は逆順に)足し合わせたものに相当します。それを2で割ることで、平均的な値を乗じた回数(つまり、元の和)を求めることができます。

この公式を使用すると、大きな数値に対しても高速に計算を行うことが可能です。そのため、AtCoderのようなプログラミングコンテストでは、ループを使用するよりもこの公式を使った方が効率的です。

アライ リョータアライ リョータ

指定範囲の文字列の取得

開始位置と終了位置を指定する

s = gets.chomp
i, j = gets.split.map(&:to_i)

puts s[i - 1..j - 1] # [開始位置, 終了位置]

開始位置と長さを指定する

puts s[i - 1, j - 1] # [開始位置, 長さ]