0
1

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 3 years have passed since last update.

qnoteAdvent Calendar 2021

Day 9

そんなに難しくない、アルゴリズムの考え方

Posted at

#はじめに
アルゴリズムというと難しく考える人が多いなぁと感じることがあったので、今回はアルゴリズムと向き合うハードルが下がるような内容をお届けしようと思います。

アルゴリズムと聞いて「ソートのアレ」「ループ」「回帰」というものを想像することが多いかと思います。
それらも当然アルゴリズムのお話なのですが、Qiitaを読んでいる皆さんが普段書かれているプログラムもアルゴリズムと言えます。
各言語に用意されている「関数」がアルゴリズムであり、皆さんが自分で記述して作っている「関数」もアルゴリズムです。

関数を実行することで毎回同様の期待する結果が得られるのであればアルゴリズムと言えます。

正直なところ話はこれで終わりなんですが、意識というのは簡単に変えられないと思います。
ということで、簡単なサンプルコードをでっち上げたのでご査収ください

サンプルコードですが、アルゴリズムについて簡単に説明しているものを色々見てみたのですがどうやら料理で例えることが多いようです。
ここでもそれに倣って料理を作るアルゴリズムを考えていきます。

料理といえばもちろんカレーですよね

#カレーの作り方

##材料

  • じゃがいも
  • タマネギ
  • にんじん
  • サラダ油
  • バーモントカレー

##作り方

  1. 具材を一口大に切る
  2. 厚手の鍋でサラダ油を熱する
  3. 鍋に具材を入れ、タマネギがしんなりするまで炒める
  4. 水を入れ、具材が柔らかくなるまで煮込む(灰汁が出たらとる)
  5. 火を止めてルウを溶かす
  6. 時々かき混ぜながら、とろみが出るまで煮込む

作り方=アルゴリズムです。
材料や調理器具を適当なクラスを作り、手順を関数にしていきます。

#具材を一口大に切る

  • 一口大とは約3cm四方のこと
    • 四方という事は平面だと思うので高さは1cm
    • 体積が9㎤以下なら一口大と言えるはず
  1. 一口大にする関数を用意する
  2. 具材の配列を受け取る関数の中で一口大にする関数を繰り返し実行する
  3. 具材が一口大になる

##具材を一口大にするアルゴリズム

fun cut(ingredients: List<Ingredient>): List<Ingredient> {
    var cutIngredients = arrayListOf<Ingredient>()
    ingredients.forEach {
        ingredient ->
            val result = cutToBiteSize(ingredient)
            cutIngredients.add(result)
    }
    return cutIngredients
}

fun cutToBiteSize(ingredient: Ingredient): Ingredient {
    var result = ingredient
    while (result.size > 9) {
        result.size = result.size / 2
    }
    result.isCut = true
    return result
}

#厚手の鍋でサラダ油を熱する

  • 厚手の鍋とは?
    • 薄いものを使わない方がいい程度のニュアンスらしい(知らんけど)
  • サラダ油が熱された状態とは?
    • 鍋を傾けて油がサラリと流れればOKらしい(知らんけど)
  1. 鍋を温める関数を作り、鍋とサラダ油を渡す
  2. サラダ油の状態がisSarari==trueになる
  3. 熱されたサラダ油ができた!

##厚手の鍋でサラダ油を熱するアルゴリズム

fun heat(saucePan: SaucePan, cookingOil: CookingOil): SaucePan {
    saucePan.cookingOil = cookingOil
    while (!saucePan.cookingOil.isSarari()) {
        saucePan.cookingOil.sarariIndex += 1
    }
    return saucePan
} 

#鍋に具材を入れ、タマネギがしんなりするまで炒める

  • しん、なり…?
    • 食材から水分が出て少し柔らかくなった状態のこと
  1. 炒める関数を作り、先程の鍋と一口大にされた具材を渡す
  2. タマネギがisSinnari==trueになる
  3. 具材が炒め終わる!

##鍋に具材を入れ、タマネギがしんなりするまで炒めるアルゴリズム

fun fry(saucePan: SaucePan, cutIngredients: List<Ingredient>): SaucePan {
    //切ってない具材はダメ、絶対
    cutIngredients.forEach { ingredient -> if (!ingredient.isCut) throw IOException() }

    saucePan.ingredients = cutIngredients
    //具材の中にはタマネギが複数あるかも
    val onions: List<Onion> = saucePan.ingredients.filter { ingredient -> ingredient is Onion } as List<Onion>
    while (!isSinnariAll(onions)) {
        onions.forEach { onion ->
            onion.waterIndex -= 1
        }
    }
    return saucePan
}

fun isSinnariAll(onions: List<Onion>): Boolean {
    onions.forEach { onion -> 
        if (!onion.isSinnari()) return false
    }
    return true
}

#水を入れ、具材が柔らかくなるまで煮込む(灰汁が出たらとる)
ここには特に不審な点はないので手順だけ考えます

  1. 煮込む関数を作り、先程の鍋と水を渡す
  2. 灰汁が出たら除去する
  3. 各具材がisYawaraka==trueになる
  4. 具材は煮込まれた!

##水を入れ、具材が柔らかくなるまで煮込むアルゴリズム with 灰汁が出たらとる

fun simmer(saucePan: SaucePan, water: Water): SaucePan {
    //炒められていない具材はダメ、絶対
    saucePan.ingredients.forEach { ingredient -> if (!ingredient.isFried) throw IOException() }

    var isYawarakaAll = false
    saucePan.water = water
    while (!isYawarakaAll) {
        var notYet = false
        saucePan.ingredients.forEach { ingredient ->
            if (ingredient.isAkuAppeared) {
                saucepan.removeLye()
            }
            if (!ingredient.isYawaraka) notYet = true
        }
        isYawarakaAll = !notYet
    }
    return saucePan
}

#火を止めてルウを溶かす

  • 火を止める!?
    • 火のことは考えてなかったので一旦無視
  1. ルウを溶かす関数を作り、先程の鍋とバーモントカレーを渡す
  2. 「溶けろー」と念じ続けることでisDoroDoro==trueになることにする
  3. カレーっぽくなる!

##(火を止めて)ルウを溶かすアルゴリズム

fun dissolve(saucePan: SaucePan, vermontCurry: VermontCurry): SaucePan {
    saucePan.curry = vermontCurry
    var isDoroDoro = false
    while (! isDoroDoro) {
        isDoroDoro = saucePan.dissolve()
    }
    return saucePan
}

#時々かき混ぜながら、とろみが出るまで煮込む

  • いよいよ仕上げです
  • 時々は面倒曖昧なのでとにかく混ぜます
  1. 仕上げの関数を作り先ほどの鍋を渡す
  2. 混ぜた結果isToroToro==trueになるまでとにかく混ぜる
  3. 完成!

##時々とにかくかき混ぜながら、とろみが出るまで煮込むアルゴリズム

fun finish(saucePan: SaucePan): SaucePan {
    var isToroToro = false
    while (!isToroToro) {
        isToroToro = saucePan.mixing()
    }
    return saucePan
}

#カレーを作るアルゴリズム
これまで作ったアルゴリズムを順番に実行すれば誰でもカレーが作れます。

fun makeCurry(ingredients: List<Ingredient>, saladOil: SaladOil, water: Water, vermontCurry: VermontCurry, saucePan: SaucePan): Curry {
    val cutIngredients = cut(ingredients)
    var usingSaucePan = heat(saucePan, saladOil)
    usingSaucePan = fry(saucePan, cutIngredients)
    usingSaucePan = simmer(saucePan, water)
    usingSaucePan = dissolve(saucePan, vermontCurry)
    usingSaucePan = finish(saucePan)
    return usingSaucePan.curry
}

#最後に
アルゴリズムに対する意識は変わりましたでしょうか?
コードはでっち上げなので参考にしなくて良いですが、「アルゴリズムは難しいものじゃない」と考えてもらえるようになったなら幸いです。

0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?