Scalaで定番なアルゴリズムを書いてみる
ユークリッドの互除法
最大公約数をもとめるやつです
GPT-4
// 再帰的なユークリッドの互除法
def gcd(a: Int, b: Int): Int = {
if (b == 0) {
a // bが0のとき、aが最大公約数
} else {
gcd(b, a % b) // そうでなければ、bとa % bで再帰的に求める
}
}
// テスト
val a = 56
val b = 98
println(s"最大公約数: ${gcd(a, b)}") // 予想される結果: 14
Claude3 Opus
def gcd(a: Int, b: Int): Int = {
if (b == 0) a else gcd(b, a % b)
}
// 使用例
val num1 = 24
val num2 = 36
val result = gcd(num1, num2)
println(s"$num1 と $num2 の最大公約数は $result です。")
GPT-4は一発で動くコードを書いてきた
Opusは変数と文字のスペースがなくてビルドエラーしてて、直させたコード
関数の書き方はOpusの方が簡潔にみえる
僕は仕事でJavaを書いているけどScalaって戻り値あっても
return書く必要ないっぽい!
最後の行が結果として返却される!
簡潔で良い!
Scala好き
エラトステネスのふるい
素数を求める
GPT-4
// エラトステネスのふるいを実装
def sieveOfEratosthenes(n: Int): List[Int] = {
// すべての数を素数と仮定し、後で確認していく
val primes = Array.fill(n + 1)(true) // 素数であるかどうかのフラグ
primes(0) = false // 0は素数ではない
primes(1) = false // 1も素数ではない
// 2から開始し、平方根まで範囲を広げる
for (p <- 2 to math.sqrt(n).toInt) {
if (primes(p)) { // pが素数なら
// pの倍数はすべて素数ではないとマーク
for (i <- p * p to n by p) {
primes(i) = false
}
}
}
// 素数のリストを返す
(2 to n).filter(primes(_)).toList
}
// テスト
val limit = 50 // 0から50までの素数を求める
println(s"素数: ${sieveOfEratosthenes(limit)}")
Claude3 Opus
def sieve(n: Int): List[Int] = {
val primes = collection.mutable.ListBuffer.range(2, n + 1)
val limit = math.sqrt(n).toInt
for (i <- 2 to limit) {
if (primes.contains(i)) {
primes --= (i * i to n by i)
}
}
primes.toList
}
// 使用例
val maxNumber = 100
val primeNumbers = sieve(maxNumber)
println(s"$maxNumber 以下の素数: $primeNumbers")
今回はOpusも一発でビルドできるコード書いてくれた
これはステップ数見ても明らかにOpusの方が
簡潔でみやすいコード書いてくれてる。
ただ、ChatGPTはコメントをつけてくれていて親切。
あとJavaやPythonで見慣れない記号があったので聞いてみた
なるほど。
関数型プログラミングは奥が深いな。
線形探索
GPT-4
// 線形探索を実装
def linearSearch[T](array: Array[T], key: T): Int = {
for (i <- array.indices) { // インデックスを使って探索
if (array(i) == key) { // キーと一致する場合
return i // インデックスを返す
}
}
-1 // 見つからなかった場合は -1 を返す
}
// テスト
val arr = Array(5, 2, 8, 3, 7, 1, 9)
val key = 7
val index = linearSearch(arr, key)
if (index != -1) {
println(s"要素 $key はインデックス $index にあります")
} else {
println(s"要素 $key は見つかりませんでした")
}
Claude3 Opus
def linearSearch[T](list: List[T], target: T): Option[Int] = {
val index = list.indexOf(target)
if (index != -1) Some(index) else None
}
// 使用例
val numbers = List(5, 2, 8, 12, 1, 6, 9, 3)
val target = 6
val result = linearSearch(numbers, target)
result match {
case Some(index) => println(s"$target は インデックス $index に見つかりました。")
case None => println(s"$target は見つかりませんでした。")
}
線形探索は順番に探していくだけなのでシンプルなアルゴリズムですね。
Opusは簡潔、GPT-4は親切ですね。
2分探索
GPT-4
// 2分探索を実装
def binarySearch(array: Array[Int], key: Int): Int = {
var low = 0 // 探索範囲の下限
var high = array.length - 1 // 探索範囲の上限
while (low <= high) { // 範囲がある限り探索を続ける
val mid = (low + high) / 2 // 中間点
if (array(mid) == key) {
return mid // キーが見つかった場合、中間点を返す
} else if (array(mid) < key) {
low = mid + 1 // キーが中間点よりも大きければ、下限を更新
} else {
high = mid - 1 // キーが中間点よりも小さければ、上限を更新
}
}
-1 // キーが見つからなかった場合、-1を返す
}
// テスト
val sortedArray = Array(1, 3, 5, 7, 9, 11, 13, 15)
val key = 7
val index = binarySearch(sortedArray, key)
if (index != -1) {
println(s"要素 $key はインデックス $index にあります")
} else {
println(s"要素 $key は見つかりませんでした")
}
Claude3 Opus
def binarySearch[T](list: List[T], target: T)(implicit ord: Ordering[T]): Option[Int] = {
def search(low: Int, high: Int): Option[Int] = {
if (low > high) {
None
} else {
val mid = (low + high) / 2
val midValue = list(mid)
if (ord.equiv(midValue, target)) {
Some(mid)
} else if (ord.lt(midValue, target)) {
search(mid + 1, high)
} else {
search(low, mid - 1)
}
}
}
search(0, list.length - 1)
}
// 使用例
val numbers = List(1, 2, 3, 5, 6, 8, 9, 12)
val target = 6
val result = binarySearch(numbers, target)
result match {
case Some(index) => println(s"$target は インデックス $index に見つかりました。")
case None => println(s"$target は見つかりませんでした。")
}
Opus側、繰り返し処理どこにあるんだ?
と思ったらsearch内で自分で自分を呼び出していた!
再帰関数というらしい。
勉強になるなぁ。