1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Caesar暗号解読の練習

Last updated at Posted at 2015-02-21

問題

Caesar暗号を解読するプログラムを作り,暗号を解読してください。
暗号鍵(何文字ずらすか)は不明ですが、文字列に"person"が含まれることがわかっています
暗号文:qdq-gi.q-a ziatmxxitmdqibtqi-ustbi ri.qmoqrcxi.qbubu zir -ibtqi-qp-qaai ripmymsqkir -ibtqi-qy dmxi ri.cnxuoi rruoumxakir -ibtqiqzmobyqzbkii-q.qmxi -imyqzpyqzbi rixmeaki -puzmzoqai -i-qscxmbu zaimzpir -i btq-iymbbq-a;iz -iatmxximzgi.q-a zinqiuzimzgiemgipuao-uyuzmbqpimsmuzabir -ia. za -uzsiacotiimi.qbubu zj

こちらのサイトから引用
※暗号文は長いのでテキストファイルにして読み込む形にしました

解答

caesar_cipher.rb
# 必ず含まれるワード
KEY_WORD = "person"

# 読み込んだデータを格納する変数
cipher_word = ""

# 使用される文字を格納する配列
# メソッドの中でも使いたいからグローバル
$character_array = []

# aからzまでループで格納
("a".."z").each do |alphabet|
  $character_array.push(alphabet)
end

# 残りの記号は手動で格納
$character_array.push(" ")
$character_array.push(".")
$character_array.push(",")
$character_array.push("-")

# 文字データだと扱いづらい
# 文字データをcharacter_arrayと比較して
# 数字の配列に変更するメソッド
# input_character:入力文字列
# 返り値:数字の配列
def character_to_number(input_character)
  # return用の配列
  return_number_array = []
    
  #入力文字列を1文字ずつ取り出す
  input_character.each_char do |current_character|
    # character_arrayの中身と1つずつ比較
    $character_array.each_with_index do |character, index|
      if current_character == character
        # 一致したらその時のindexを配列に格納
        return_number_array.push(index)
      end
    end
  end
  
  # returnする
  return_number_array
end

# 数字の配列を文字列に変換するメソッド
# character_to_numberの逆
# input_number_array:数字の配列
# 返り値:文字列
def number_to_character(input_number_array)
  return_charcter = ""
  input_number_array.each do |number|
    # $character_array配列の該当箇所の文字を加えていく
    return_charcter.concat($character_array[number])
  end
  
  return_charcter
end

# 指定された数だけずらすメソッド
# cipher_number_array:ずらしたい配列
# declination_number:ずらす数
# 返り値:ずらした後の配列
def declination(cipher_number_array, declination_number)
  # 入力された配列を1つずつ取り出す
  cipher_number_array.length.times do |index|
    # declination_numberの数だけ増やす
    cipher_number_array[index] += declination_number
    # 使用される文字数の数を超えたらその分減らす
    if cipher_number_array[index] > $character_array.length - 1
      cipher_number_array[index] -= $character_array.length
    end
  end
  # return
  cipher_number_array
end

# 必ず含まれる文字列の情報を元に何文字分ずれているか計算するメソッド
# param_cipher_word:入力された暗号文
# param_key_word:必ず含まれる文字列
# 返り値:ずれている文字数
def calc_cryptographic_key(cipher_word, key_word)
  # 文字列を数字の配列に変換
  cipher_number_array = character_to_number(cipher_word)
  key_number_array    = character_to_number(key_word)
  
  # 1文字ずつずらしていき、key_wordと一致する数字を探す
  1.upto($character_array.length - 1) do |declination_number|
    # 1文字ずらす
    cipher_number_array = declination(cipher_number_array, 1)
    # 0番目から探す
    cipher_number_index = -1
    # 一致した回数
    accord_count = 0
    # key_number_arrayから1つずつ取り出す
    key_number_array.each do |key_number|
      # cipher_number_arrayから1つずつ取り出す
      cipher_number_array.each_with_index do |cipher_number, index|
        # 連続して探したいから、前回見つかった場所まではスキップ
        next if cipher_number_index >= index
        if key_number == cipher_number
          # 一致したらそのindexの次の番号から探索
          cipher_number_index = index
          # 一致した回数を増やす
          accord_count += 1
          # 一致した回数がKEY_WORDの長さと同じだったら終了
          if accord_count == key_number_array.length
            # 0から始めたから1つ増やす
            return declination_number
          end
          # 一致したら次のKEY_WORDを調べる
          break
        else
        # 一致しなかったら回数を0に戻す
          accord_count = 0
        end
      end
    end
  end
  
  # 一致しなかったら-1を返す
  puts "一致する文字列はありませんでした"
  return -1
end

# 暗号ファイルの読み込み
cipher_file = open("Data/cipher.txt", "r")
# 読み込んだデータをcipher_wordに格納
cipher_file.each {|line| cipher_word = line}
# ファイルを閉じる
cipher_file.close

# 暗号鍵を求める
cryptographic_key = calc_cryptographic_key(cipher_word, KEY_WORD)

puts "暗号鍵は#{cryptographic_key}です"

# 暗号文の文字列を数字の配列に変換
cipher_number = character_to_number(cipher_word)

# 暗号鍵の分シフト
cipher_number = declination(cipher_number, cryptographic_key)

# 数字の配列から文字列に戻す
puts "元の文章は#{number_to_character(cipher_number)}"

結果

暗号鍵は18です
元の文章はevery person shall have the right of peaceful petition for the redress of damage, for the removal of public officials, for the enactment,  repeal or amendment of laws, ordinances or regulations and for other matters nor shall any person be in any way discriminated against for sponsoring such  a petition.

参考

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?