lean-ja / lean-ja/lean-by-example

`twoSum` の実装(HashMap を使用するバージョンとそうでないバージョンがある)

Open
#1,790 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

メモ
Dominant language
Lean
Stars
188
Forks
15
Avg merge
9h 8m
Merged PRs (30d)
6

Description

import Plausible
/- # 1. Two Sum

整数の配列 `nums` と整数 `target` が与えられたとき、合計が `target` になるような 2つの数のインデックス を返してください。
各入力には 必ずちょうど1つの解が存在する と仮定してかまいません。また、同じ要素を2回使ってはいけません。
答えは どちらの順番でも構いません。
-/

/-- 素朴な解法。for文を二重に回して解を探す。
for文を二重に回すため、計算量は`O(|nums|²)`になる。 -/
def naiveTwoSum (nums : List Int) (target : Int) : Option (Nat × Nat) := Id.run do
  let n := nums.length
  for i in [0:n] do
    for j in [i+1:n] do
      if nums[i]! + nums[j]! == target then
        return some (i, j)
  return none

#guard
  let nums := [2, 7, 11, 15]
  let target := 9
  let result := naiveTwoSum nums target |>.get!
  nums[result.fst]! + nums[result.snd]! == target

#guard
  let nums := [3, 2, 4]
  let target := 6
  let result := naiveTwoSum nums target |>.get!
  nums[result.fst]! + nums[result.snd]! == target

#guard
  let nums := [3, 3]
  let target := 6
  let result := naiveTwoSum nums target |>.get!
  nums[result.fst]! + nums[result.snd]! == target

open Std in

/-- 辞書を使った高速な実装。辞書からの検索は定数時間なので、計算量は`O(|nums|)`になる。 -/
def twoSum (nums : List Int) (target : Int) : Option (Nat × Nat) := Id.run do
  let mut map : HashMap Int Nat := Std.HashMap.emptyWithCapacity
  for (num, i) in nums.zipIdx do
    let complement := target - num
    if let some j := map[complement]? then
      return some (j, i)
    map := map.insert num i
  return none

#guard
  let nums := [0, -1, 3, 2, 13]
  let target := 2
  let result := twoSum nums target |>.get!
  nums[result.fst]! + nums[result.snd]! == target

namespace Test
  /- テスト -/

  /-- 長さ `n` で、中身の値が絶対値 `bound` 以下であるようなリストをランダム生成する -/
  def randList (n : Nat) (bound : Nat) : IO (List Int) := do
    let mut out := []
    for _ in [0 : n] do
      -- ランダムに 0 以上 bound 以下の自然数を生成する
      let x ← IO.rand 0 bound
      let pos ← IO.rand 0 1
      let y : Int := if pos == 0 then x else -x

      -- 生成した自然数をリストに追加する
      out := y :: out
    return out

  -- 長いリストを生成して、両者の計算時間を計測する
  #eval show IO Unit from do
    let nums ← randList 9000000 10000
    let start_time ← IO.monoMsNow
    let result1 := naiveTwoSum nums 100
    let end_time ← IO.monoMsNow
    IO.println s!"naiveTwoSum took {end_time - start_time} ms"
    match result1 with
    | none => IO.println "No solution found"
    | some (i, j) =>
      IO.println s!"Found solution: {i}, {j}"

    let start_time ← IO.monoMsNow
    let result2 := twoSum nums 100
    let end_time ← IO.monoMsNow
    IO.println s!"twoSum took {end_time - start_time} ms"
    match result2 with
    | none => IO.println "No solution found"
    | some (i, j) =>
      IO.println s!"Found solution: {i}, {j}"

end Test

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating the example file associated with issue #1790 in the lean-by-example repository, then compare its surrounding structure with the supplied code. The issue provides naiveTwoSum, HashMap-based twoSum, #guard checks, and a benchmark; done means these examples are integrated in the appropriate location and the shown checks and evaluation run successfully.

Written by the indexing model from the issue text.

Assessment

Domain
documentation
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.