Closed71

アルゴ式をRubyで解く。【プログラミングに慣れ親しむ】

ハガユウキハガユウキ

AC (Accepted):提出したプログラムが正解であることを意味します (AC 以外のメッセージは不正解です)
WA (Wrong Answer):提出したプログラムの出力が正しくありません
TLE (Time Limit Exceeded):問題で指定された実行制限時間以内にプログラムが終了しませんでした
MLE (Memory Limit Exceeded):使用メモリ量が問題で使用制限メモリ量を超えました
CE (Compile Error):提出したプログラムのコンパイルに失敗しました
RE (Runtime Error):配列外参照、ゼロ除算などの原因により、プログラムの実行中にエラーが発生しました
IE (Internal Error):内部のエラー、つまりジャッジシステムのエラーです

ハガユウキハガユウキ

整数の一の位はその数を 10 で割った余りと等しいです。
たとえば 17 の一の位は 7 ですが、これは 17 を 10 で割った余りと一致します。

なんとなくイメージはできる

ハガユウキハガユウキ

n要素で考えた時でも成功するようなコードを書かないとなあ
その場しのぎ的なコードを書かない。n要素の方がどんな仕様変更が来ても、対応しやすい。

ハガユウキハガユウキ

「A が B の倍数である」ことは、「A が B で割りきれる」ことと等しいです。

ハガユウキハガユウキ

配列の要素同士の掛け算に関しての便利メソッドはなさそう。ループさせるしかないか

ハガユウキハガユウキ

多重代入でインデックス1以降の要素を配列として受け取りたい時

begin
  lines = readlines.map(&:chomp)
  # インデックス1以降の要素をまとめて配列として取得する
  _, *words = lines
  joined_word = words.join
  puts joined_word.length
rescue StandardError => e
  p "raise: error", e
end

実行結果

ruby src/become_familiar_with_programming/receive_input/many_stdin/practice_8.rb
3
hello
algo
method
15
ハガユウキハガユウキ
S = readlines.map(&:chomp)

lft = S.count "left"
rgt = S.count "right"

puts lft > rgt ? "left" : lft == rgt ? "same" : "right"

上のコードでもできなくないけど、topやdown等の方向が増えたときに、集計ロジックを変えないといけない。

begin
  lines = readlines.map(&:chomp)
  # インデックス1以降の要素をまとめて配列として取得する
  _, *directions = lines
  direction_counter = Hash.new(0)
  directions.each { |direction| direction_counter[direction] += 1 }
  if direction_counter["left"] > direction_counter["right"]
    puts "left"
  elsif direction_counter["left"] < direction_counter["right"]
    puts "right"
  elsif direction_counter["left"] == direction_counter["right"]
    puts "same"
  end
rescue StandardError => e
  p "raise: error", e
end

こっちのコードだったら、集計ロジックを書き換える必要はない。
(どちらのコードでも、if文は書き換える必要は出てくるけど)

ハガユウキハガユウキ

全探索とは

全探索は考えられる可能性を全て調べ上げる方法のことです。

線形探索法

このように、「一つ一つのデータを順に調べていく」方法のことを線形探索法と言います。 この考え方は一見シンプルですが、探索アルゴリズムの原点となる大切なものです。

ハガユウキハガユウキ

最初に初期値 init と self の最初の要素を引数にブロックを実行します。 2 回目以降のループでは、前のブロックの実行結果と self の次の要素を引数に順次ブロックを実行します。そうして最後の要素まで繰り返し、最後のブロックの実行結果を返します。

reduceのresultにははじめのループでは第一要素、2回目以降のループはblockの実行結果が入るのか

https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/inject.html

ハガユウキハガユウキ

ハッシュロケットを使えば、ハッシュのキーに文字列、シンボル以外に数値を指定できる
https://monmon.hateblo.jp/entry/20130116/1358321670

ハガユウキハガユウキ

このコードのデメリットは、number_counterが増えると辛い

begin
  lines = readlines.map(&:chomp)
  numbers = lines[1].split.map(&:to_i)
  number_counter = {
    1 => 0,
    2 => 0,
    3 => 0,
    4 => 0,
    5 => 0,
    6 => 0,
    7 => 0,
    8 => 0,
    9 => 0
  }

  numbers.each { |number| number_counter[number] += 1 }

  number_counter.each do |_, value|
    puts value
  end
rescue StandardError => e
  p "raise: error", e
end

ただ、配列のインデックスもだいぶトラップな気がする。

ハガユウキハガユウキ

Hash#valuesで、ハッシュの全valueを要素とする配列を取得できる。
https://docs.ruby-lang.org/ja/latest/method/Hash/i/values.html

ハッシュにもeachはある。Hash#each
https://docs.ruby-lang.org/ja/latest/method/Hash/i/each.html

Hash#keyで、valueに対応するキーを返す
https://docs.ruby-lang.org/ja/latest/method/Hash/i/key.html

begin
  lines = readlines.map(&:chomp)
  numbers = lines[1].split.map(&:to_i)
  number_counter = Hash.new(0)
  numbers.each { |number| number_counter[number] += 1 }
  maximum_count = number_counter.values.max
  most_appear_number = number_counter.key(maximum_count)
  puts most_appear_number
rescue StandardError => e
  p "raise: error", e
end
ハガユウキハガユウキ

数値の全探索

「配列の全探索」では、配列の要素を調べあげる練習をしました。
この章では、特定の範囲の数をすべて調べ上げる「数字の全探索」を取り扱います。

ハガユウキハガユウキ

for文とグローバル変数countを使うことで、特定の範囲の数を全て調べられるのか。

ハガユウキハガユウキ

次の条件を満たすとき「 N は素数である」と言います。
N は 2 以上の整数である
N を割り切ることのできる 1 より大きい整数は N のみである

ハガユウキハガユウキ

与えられた数が素数かどうかを判定する

素数判定のアルゴリズム
もっちょい綺麗に描きたい

def prime_number?(target_number)
  return false if target_number < 2
  return true if target_number == 2

  (2..target_number - 1).each do |number|
    return false if (target_number % number).zero?
  end

  true
end

begin
  target_number = gets.chomp.to_i
  if prime_number?(target_number)
    puts "Yes"
  else
    puts "No"
  end
rescue StandardError => e
  p "raise: error", e
end
ハガユウキハガユウキ

最大公約する

ただし次の条件を満たすとき「 X は A と B の最大公約数である」と言います。
条件:X は A も B も割り切る 1 以上の整数の中で最大のものである

ハガユウキハガユウキ

こんな感じ

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

  loop do
    remainder_number = b % a
    break if remainder_number.zero?

    b = a
    a = remainder_number
  end

  puts a
rescue StandardError => e
  p "raise: error", e
end

rubyで配列の要素を別の2つの変数に入れるとかできたんやね。すごい

# aに1, bに2が代入される
a, b = [1, 2]
ハガユウキハガユウキ

ある要素以降の配列が欲しいなら、a[n..]を指定すれば良い

  words_count, words = lines[0].to_i, lines[1..]
ハガユウキハガユウキ

ループさせる配列が 3 つになってくると、流石にこのロジック計算量大丈夫かってなるな。

begin
  lines = readlines.map(&:chomp)
  x, y, z = lines[0].split.map(&:to_i)
  numbers = lines[1].split.map(&:to_i)
  other_numbers = lines[2].split.map(&:to_i)
  another_numbers = lines[3].split.map(&:to_i)
  equal_count = 0

  (0..x - 1).each do |i|
    (0..y - 1).each do |j|
      (0..z - 1).each do |k|
        numbers[i] + other_numbers[j] == another_numbers[k] && equal_count += 1
      end
    end
  end

  puts equal_count
rescue StandardError => e
  p "raise: error", e
end

ハガユウキハガユウキ

ペアの全探索

この章では、1 つの配列に対して過不足なく (同じ添字を 2 回調べることなく) ループを回す方法を学び練習します。

組みを取り出す場合、順列とは異なるから、

i は 0 から N−1 までの範囲を動く。
j は i+1 から N−1 までの範囲を動く。

と考えた方が、(i, j)の組みが重複することはない

ハガユウキハガユウキ

2重ループの場合、内側のループではiは固定される。そのiより大きい ~ 最大の範囲でjが動けば、iとjは絶対被らない。iが固定されていてjがiより大きい範囲とiの最大値の間で動いている図をイメージする。

ハガユウキハガユウキ

選び方って言われたら、順列とは違う。位置まで関心がない。組み合わせにしか関心がない

ハガユウキハガユウキ

maxの書き方思いつかなかった。確かに、ループが増えると条件文がめっちゃ長くなるから、maxのが良いか

before

begin
  lines = readlines.map(&:chomp)
  numbers_count = lines[0].to_i
  numbers = lines[1].split.map(&:to_i)
  middle_maximum_number_count = 0

  (0..numbers_count - 1).each do |i|
    (i + 1..numbers_count - 1).each do |j|
      (j + 1..numbers_count - 1).each do |k|
        numbers[j] >= numbers[i] && numbers[j] >= numbers[k] && middle_maximum_number_count += 1
      end
    end
  end

  puts middle_maximum_number_count
rescue StandardError => e
  p "raise: error", e
end

after

begin
  lines = readlines.map(&:chomp)
  numbers_count = lines[0].to_i
  numbers = lines[1].split.map(&:to_i)
  middle_maximum_number_count = 0

  (0..numbers_count - 1).each do |i|
    (i + 1..numbers_count - 1).each do |j|
      (j + 1..numbers_count - 1).each do |k|
        numbers[j] == [numbers[i], numbers[j], numbers[k]].max && middle_maximum_number_count += 1
      end
    end
  end

  puts middle_maximum_number_count
rescue StandardError => e
  p "raise: error", e
end

ハガユウキハガユウキ

1の位を求めたいなら、10で割ったあまりを求めればよい

irb
irb(main):001:0> 123 % 10
=> 3
irb(main):002:0> 123 % 100
=> 23
irb(main):003:0>

z = ab + cの形を想像すれば簡単か。

このスクラップは2023/10/17にクローズされました