はじめに
コードを書かせるとき、どの Gemini モデルを選べばいいのか。フラッグシップの gemini-3.5-flash、軽量な gemini-3.1-flash-lite、上位モデルの gemini-3.1-pro-preview の3つを、定番のアルゴリズム問題15問で実際に比較しました。
採点は、コードを読んで正誤を判断するのではなく、生成されたコードを実際に実行し、テストケースが通るかをプログラムで機械的に判定しています。
検証環境は Gemini Developer API(GEMINI_API_KEY)、SDK は google-genai です。
結論
まず結論をお伝えします。
※実際の出力結果については、付録として掲載しているので必要に応じてご参照ください。
- 易しい問題からLeetCode定番の難問まで15問すべてで、3モデルとも全問正解だった。正答率では差がつかなかった
- 一方で速度には大きな差があった。生成スループットは
gemini-3.1-flash-liteが最速(毎秒159.7トークン)で、gemini-3.1-pro-previewが最も遅く(毎秒17.9トークン)、その差は約9倍だった
検証方法
15問を難易度別に用意しました。すべて正解が確定している定番のアルゴリズム問題です。
- 易しい5問:回文判定、ネストしたリストの平坦化、最頻値、シーザー暗号、アナグラムのグループ化
- 普通5問:最長共通接頭辞、括弧の対応チェック、区間のマージ、フィボナッチ数、ランレングス圧縮
- 難しい5問:単語の変換系列(Word Ladder)、コイン問題の最小枚数、最長増加部分列、雨水を溜める問題(Trapping Rain Water)、編集距離(レーベンシュタイン距離)
各問題は、関数シグネチャと簡単な説明だけを与え、コードのみを出力するよう指示しています。
次のPython関数を実装してください。関数シグネチャは変更しないでください。
```python
def edit_distance(word1: str, word2: str) -> int:
"""word1をword2に変換するために必要な最小編集回数(挿入・削除・置換)を返す(レーベンシュタイン距離)。"""
pass
返ってきたコードから関数定義部分を抜き出し、あらかじめ用意したテストケースと突き合わせて、実際にサブプロセスで実行しました。全テストケースが通れば正解、1つでも失敗すれば不正解として扱っています。temperature=0 で固定しました。
正答率は3モデルとも満点だった
結果です。
| モデル | 正答数 |
|---|---|
| gemini-3.5-flash | 15 / 15 |
| gemini-3.1-flash-lite | 15 / 15 |
| gemini-3.1-pro-preview | 15 / 15 |
雨水を溜める問題や編集距離のような、LeetCodeで「Hard」に分類される問題も含めて、3モデルとも全問でテストが通りました。これらの問題はアルゴリズム学習の教材として広く知られているため、モデルが解法パターンを十分に学習していることがうかがえます。少なくとも、この種の定番問題を解かせる用途では、モデル選びが正答率を左右することはなさそうです。
速度には約9倍の差があった
正答率では差がつかなかった一方、生成にかかった時間ははっきり分かれました。
| モデル | 合計時間(15問) | 1問あたり平均 | 出力トークン合計 | スループット |
|---|---|---|---|---|
| gemini-3.5-flash | 52.1秒 | 3.47秒 | 2046 | 39.3 トークン/秒 |
| gemini-3.1-flash-lite | 13.5秒 | 0.90秒 | 2163 | 159.7 トークン/秒 |
| gemini-3.1-pro-preview | 107.3秒 | 7.16秒 | 1918 | 17.9 トークン/秒 |
gemini-3.1-flash-lite は他の2モデルよりも多くのトークンを出力していながら、最も速く仕上げています。スループットで見ると、最速の flash-lite と最も遅い pro-preview の差はおよそ9倍です。難しい5問(Word Ladder、コイン問題、最長増加部分列、雨水、編集距離)に絞ってもこの傾向は変わらず、flash-lite は平均1.09秒、pro-preview は平均8.38秒でした。
出力トークン数を見ると、pro-preview はむしろ最も少ない出力(1918トークン)で最も時間がかかっています。つまり、時間がかかっている原因は「書く量が多いから」ではなく、モデルの処理そのものが重いためだと考えられます。
実装内容がすべて同じになるわけではない
正答率もテストの通過も同じでしたが、生成されたコードの中身を見ると、たまに違いが見つかりました。編集距離(レーベンシュタイン距離)の問題で、3モデルの実装方針を比べてみます。
gemini-3.5-flash は、1次元配列だけで計算する、メモリ効率の良い実装を書きました。
def edit_distance(word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
dp = list(range(n + 1))
for i in range(1, m + 1):
prev = dp[0]
dp[0] = i
for j in range(1, n + 1):
temp = dp[j]
if word1[i - 1] == word2[j - 1]:
dp[j] = prev
else:
dp[j] = min(prev, dp[j], dp[j - 1]) + 1
prev = temp
return dp[n]
一方 gemini-3.1-flash-lite と gemini-3.1-pro-preview は、どちらも教科書的な2次元のテーブルを使う実装でした。
def edit_distance(word1: str, word2: str) -> int:
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
return dp[m][n]
どちらも正解ですが、flash のほうは使うメモリを文字列の長さに比例する量まで削っています。ただし、これは全問題を通じた一貫した傾向ではありません。単語の変換系列やコイン問題、最長増加部分列といった他の難しい問題では、3モデルとも同じ効率的なアルゴリズム(幅優先探索、動的計画法、二分探索を使ったO(n log n)の解法など)に収束しており、実装方針が割れたのはこの編集距離の問題だけでした。
おわりに
定番のアルゴリズム問題を解かせる限り、gemini-3.5-flash や gemini-3.1-flash-lite、gemini-3.1-pro-preview のどれを選んでも、正答率は変わりませんでした。この種のタスクでモデルを選ぶ基準は、精度ではなく速度になりそうです。実際、最も軽量な flash-lite が最も速く、しかも正答率は他の2モデルと並んでいました。
もちろん、今回試したのは正解がはっきり決まっている教科書的な問題です。仕様があいまいな実務のコーディングタスクや、大規模なコードベースを踏まえた変更では、また違う結果になる可能性があります。ただ、少なくとも「よく知られたアルゴリズムを実装させる」という用途では、上位モデルに時間をかけて待つ理由は見当たりませんでした。
付録:全45回答の生成コード
要約せず、実際に生成されたコードをすべてそのまま載せます。すべて実行してテストが通ることを確認済みです。
E1(easy): is_palindrome(s: str) -> bool
文字列が回文かどうかを判定する。大文字小文字を無視し、英数字以外の文字は無視する。
gemini-3.5-flash ○(2.25秒 / 出力76トークン)
def is_palindrome(s: str) -> bool:
"""文字列が回文かどうかを判定する。大文字小文字を無視し、英数字以外の文字は無視する。"""
cleaned = "".join(c.lower() for c in s if c.isalnum())
return cleaned == cleaned[::-1]
gemini-3.1-flash-lite ○(0.69秒 / 出力80トークン)
def is_palindrome(s: str) -> bool:
"""文字列が回文かどうかを判定する。大文字小文字を無視し、英数字以外の文字は無視する。"""
filtered_chars = [char.lower() for char in s if char.isalnum()]
return filtered_chars == filtered_chars[::-1]
gemini-3.1-pro-preview ○(5.44秒 / 出力74トークン)
def is_palindrome(s: str) -> bool:
"""文字列が回文かどうかを判定する。大文字小文字を無視し、英数字以外の文字は無視する。"""
cleaned = [c.lower() for c in s if c.isalnum()]
return cleaned == cleaned[::-1]
E2(easy): flatten(nested: list) -> list
任意の深さでネストしたリストを1次元のリストに平坦化する。
gemini-3.5-flash ○(5.04秒 / 出力85トークン)
def flatten(nested: list) -> list:
"""任意の深さでネストしたリストを1次元のリストに平坦化する。"""
result = []
for item in nested:
if isinstance(item, list):
result.extend(flatten(item))
else:
result.append(item)
return result
gemini-3.1-flash-lite ○(0.8秒 / 出力85トークン)
def flatten(nested: list) -> list:
"""任意の深さでネストしたリストを1次元のリストに平坦化する。"""
result = []
for item in nested:
if isinstance(item, list):
result.extend(flatten(item))
else:
result.append(item)
return result
gemini-3.1-pro-preview ○(5.0秒 / 出力62トークン)
def flatten(nested: list) -> list:
result = []
for item in nested:
if isinstance(item, list):
result.extend(flatten(item))
else:
result.append(item)
return result
E3(easy): most_frequent(lst: list)
リストの中で最も頻繁に出現する要素を返す(最頻値は1つに決まるものとする)。
gemini-3.5-flash ○(2.28秒 / 出力65トークン)
from collections import Counter
def most_frequent(lst: list):
"""リストの中で最も頻繁に出現する要素を返す(最頻値は1つに決まるものとする)。"""
return Counter(lst).most_common(1)[0][0]
gemini-3.1-flash-lite ○(0.64秒 / 出力58トークン)
def most_frequent(lst: list):
"""リストの中で最も頻繁に出現する要素を返す(最頻値は1つに決まるものとする)。"""
return max(set(lst), key=lst.count)
gemini-3.1-pro-preview ○(5.19秒 / 出力59トークン)
def most_frequent(lst: list):
counts = {}
for item in lst:
counts[item] = counts.get(item, 0) + 1
return max(counts, key=counts.get)
E4(easy): caesar_cipher(text: str, shift: int) -> str
シーザー暗号で文字列を暗号化する。アルファベットのみをシフトし、大文字小文字は保持し、記号や空白はそのままにする。
gemini-3.5-flash ○(2.15秒 / 出力165トークン)
def caesar_cipher(text: str, shift: int) -> str:
"""シーザー暗号で文字列を暗号化する。アルファベットのみをシフトし、大文字小文字は保持し、記号や空白はそのままにする。"""
result = []
for char in text:
if char.isupper():
result.append(chr((ord(char) - 65 + shift) % 26 + 65))
elif char.islower():
result.append(chr((ord(char) - 97 + shift) % 26 + 97))
else:
result.append(char)
return "".join(result)
gemini-3.1-flash-lite ○(1.07秒 / 出力182トークン)
def caesar_cipher(text: str, shift: int) -> str:
"""シーザー暗号で文字列を暗号化する。アルファベットのみをシフトし、大文字小文字は保持し、記号や空白はそのままにする。"""
result = []
shift %= 26
for char in text:
if 'a' <= char <= 'z':
result.append(chr((ord(char) - ord('a') + shift) % 26 + ord('a')))
elif 'A' <= char <= 'Z':
result.append(chr((ord(char) - ord('A') + shift) % 26 + ord('A')))
else:
result.append(char)
return "".join(result)
gemini-3.1-pro-preview ○(8.57秒 / 出力175トークン)
def caesar_cipher(text: str, shift: int) -> str:
"""シーザー暗号で文字列を暗号化する。アルファベットのみをシフトし、大文字小文字は保持し、記号や空白はそのままにする。"""
result = []
for char in text:
if 'a' <= char <= 'z':
result.append(chr((ord(char) - ord('a') + shift) % 26 + ord('a')))
elif 'A' <= char <= 'Z':
result.append(chr((ord(char) - ord('A') + shift) % 26 + ord('A')))
else:
result.append(char)
return "".join(result)
E5(easy): group_anagrams(words: list) -> list
アナグラム同士をグループ化して、リストのリストとして返す(グループ内・グループ間の順序は問わない)。
gemini-3.5-flash ○(2.43秒 / 出力101トークン)
from collections import defaultdict
def group_anagrams(words: list) -> list:
"""アナグラム同士をグループ化して、リストのリストとして返す(グループ内・グループ間の順序は問わない)。"""
groups = defaultdict(list)
for word in words:
sorted_word = "".join(sorted(word))
groups[sorted_word].append(word)
return list(groups.values())
gemini-3.1-flash-lite ○(0.73秒 / 出力102トークン)
def group_anagrams(words: list) -> list:
"""アナグラム同士をグループ化して、リストのリストとして返す(グループ内・グループ間の順序は問わない)。"""
from collections import defaultdict
groups = defaultdict(list)
for word in words:
key = "".join(sorted(word))
groups[key].append(word)
return list(groups.values())
gemini-3.1-pro-preview ○(5.63秒 / 出力65トークン)
from collections import defaultdict
def group_anagrams(words: list) -> list:
anagrams = defaultdict(list)
for word in words:
anagrams[tuple(sorted(word))].append(word)
return list(anagrams.values())
M1(medium): longest_common_prefix(strs: list) -> str
文字列のリストに共通する最長の接頭辞を返す。共通の接頭辞がなければ空文字列を返す。
gemini-3.5-flash ○(5.45秒 / 出力113トークン)
def longest_common_prefix(strs: list) -> str:
"""文字列のリストに共通する最長の接頭辞を返す。共通の接頭辞がなければ空文字列を返す。"""
if not strs:
return ""
s1, s2 = min(strs), max(strs)
for i, c in enumerate(s1):
if s2[i] != c:
return s1[:i]
return s1
gemini-3.1-flash-lite ○(0.84秒 / 出力114トークン)
def longest_common_prefix(strs: list) -> str:
"""文字列のリストに共通する最長の接頭辞を返す。共通の接頭辞がなければ空文字列を返す。"""
if not strs:
return ""
shortest = min(strs, key=len)
for i, char in enumerate(shortest):
for other in strs:
if other[i] != char:
return shortest[:i]
return shortest
gemini-3.1-pro-preview ○(8.89秒 / 出力82トークン)
def longest_common_prefix(strs: list) -> str:
if not strs:
return ""
s1 = min(strs)
s2 = max(strs)
for i, c in enumerate(s1):
if c != s2[i]:
return s1[:i]
return s1
M2(medium): is_valid_parentheses(s: str) -> bool
()[]{}の文字列が正しく対応・入れ子になっているかを判定する。
gemini-3.5-flash ○(2.34秒 / 出力125トークン)
def is_valid_parentheses(s: str) -> bool:
"""()[]{}の文字列が正しく対応・入れ子になっているかを判定する。"""
stack = []
mapping = {")": "(", "]": "[", "}": "{"}
for char in s:
if char in mapping:
top_element = stack.pop() if stack else '#'
if mapping[char] != top_element:
return False
else:
stack.append(char)
return not stack
gemini-3.1-flash-lite ○(0.79秒 / 出力128トークン)
def is_valid_parentheses(s: str) -> bool:
"""()[]{}の文字列が正しく対応・入れ子になっているかを判定する。"""
stack = []
mapping = {')': '(', ']': '[', '}': '{'}
for char in s:
if char in mapping:
top_element = stack.pop() if stack else '#'
if mapping[char] != top_element:
return False
else:
stack.append(char)
return not stack
gemini-3.1-pro-preview ○(5.01秒 / 出力128トークン)
def is_valid_parentheses(s: str) -> bool:
"""()[]{}の文字列が正しく対応・入れ子になっているかを判定する。"""
stack = []
mapping = {")": "(", "}": "{", "]": "["}
for char in s:
if char in mapping:
top_element = stack.pop() if stack else '#'
if mapping[char] != top_element:
return False
else:
stack.append(char)
return not stack
M3(medium): merge_intervals(intervals: list) -> list
重なり合う区間をマージし、開始位置でソートされたリストとして返す。
gemini-3.5-flash ○(3.63秒 / 出力146トークン)
def merge_intervals(intervals: list) -> list:
"""重なり合う区間をマージし、開始位置でソートされたリストとして返す。"""
if not intervals:
return []
sorted_intervals = sorted(intervals, key=lambda x: x[0])
merged = []
for interval in sorted_intervals:
if not merged or merged[-1][1] < interval[0]:
merged.append(list(interval))
else:
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
gemini-3.1-flash-lite ○(0.91秒 / 出力137トークン)
def merge_intervals(intervals: list) -> list:
"""重なり合う区間をマージし、開始位置でソートされたリストとして返す。"""
if not intervals:
return []
intervals.sort(key=lambda x: x[0])
merged = [intervals[0]]
for current in intervals[1:]:
last = merged[-1]
if current[0] <= last[1]:
last[1] = max(last[1], current[1])
else:
merged.append(current)
return merged
gemini-3.1-pro-preview ○(9.12秒 / 出力129トークン)
def merge_intervals(intervals: list) -> list:
if not intervals:
return []
sorted_intervals = sorted(intervals, key=lambda x: x[0])
merged = [list(sorted_intervals[0])]
for current in sorted_intervals[1:]:
if current[0] <= merged[-1][1]:
merged[-1][1] = max(merged[-1][1], current[1])
else:
merged.append(list(current))
return merged
M4(medium): fibonacci_memo(n: int) -> int
n番目のフィボナッチ数を返す(fib(0)=0, fib(1)=1)。n=30程度でも高速に動作するよう、メモ化または反復で実装すること。
gemini-3.5-flash ○(3.96秒 / 出力138トークン)
def fibonacci_memo(n: int) -> int:
"""n番目のフィボナッチ数を返す(fib(0)=0, fib(1)=1)。n=30程度でも高速に動作するよう、メモ化または反復で実装すること。"""
if n < 0:
raise ValueError("n must be a non-negative integer")
if n == 0:
return 0
a, b = 0, 1
for _ in range(n - 1):
a, b = b, a + b
return b
gemini-3.1-flash-lite ○(0.72秒 / 出力117トークン)
def fibonacci_memo(n: int) -> int:
"""n番目のフィボナッチ数を返す(fib(0)=0, fib(1)=1)。n=30程度でも高速に動作するよう、メモ化または反復で実装すること。"""
if n <= 1:
return n
a, b = 0, 1
for _ in range(2, n + 1):
a, b = b, a + b
return b
gemini-3.1-pro-preview ○(6.76秒 / 出力113トークン)
def fibonacci_memo(n: int) -> int:
"""n番目のフィボナッチ数を返す(fib(0)=0, fib(1)=1)。n=30程度でも高速に動作するよう、メモ化または反復で実装すること。"""
if n <= 0:
return 0
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
M5(medium): run_length_encode(s: str) -> str
文字列をランレングス圧縮する。例: 'aaabbc' -> 'a3b2c1'
gemini-3.5-flash ○(3.05秒 / 出力80トークン)
from itertools import groupby
def run_length_encode(s: str) -> str:
"""文字列をランレングス圧縮する。例: 'aaabbc' -> 'a3b2c1'"""
return "".join(f"{key}{sum(1 for _ in group)}" for key, group in groupby(s))
gemini-3.1-flash-lite ○(0.89秒 / 出力156トークン)
def run_length_encode(s: str) -> str:
"""文字列をランレングス圧縮する。例: 'aaabbc' -> 'a3b2c1'"""
if not s:
return ""
result = []
count = 1
for i in range(1, len(s)):
if s[i] == s[i - 1]:
count += 1
else:
result.append(f"{s[i - 1]}{count}")
count = 1
result.append(f"{s[-1]}{count}")
return "".join(result)
gemini-3.1-pro-preview ○(5.82秒 / 出力135トークン)
def run_length_encode(s: str) -> str:
if not s:
return ""
result = []
current_char = s[0]
count = 1
for char in s[1:]:
if char == current_char:
count += 1
else:
result.append(f"{current_char}{count}")
current_char = char
count = 1
result.append(f"{current_char}{count}")
return "".join(result)
H1(hard): word_ladder_length(begin: str, end: str, word_list: list) -> int
beginからendまで、word_list内の単語のみを経由し1文字ずつ変えて到達する最短の変換系列に含まれる単語数を返す。到達不能なら0を返す。
gemini-3.5-flash ○(6.07秒 / 出力290トークン)
def word_ladder_length(begin: str, end: str, word_list: list) -> int:
word_set = set(word_list)
if end not in word_set:
return 0
begin_set = {begin}
end_set = {end}
word_set.discard(begin)
length = 1
word_len = len(begin)
chars = 'abcdefghijklmnopqrstuvwxyz'
while begin_set and end_set:
if len(begin_set) > len(end_set):
begin_set, end_set = end_set, begin_set
next_set = set()
for word in begin_set:
for i in range(word_len):
for c in chars:
next_word = word[:i] + c + word[i+1:]
if next_word in end_set:
return length + 1
if next_word in word_set:
next_set.add(next_word)
word_set.remove(next_word)
begin_set = next_set
length += 1
return 0
gemini-3.1-flash-lite ○(1.18秒 / 出力267トークン)
from collections import deque
def word_ladder_length(begin: str, end: str, word_list: list) -> int:
word_set = set(word_list)
if end not in word_set:
return 0
queue = deque([(begin, 1)])
visited = {begin}
length = len(begin)
while queue:
current_word, count = queue.popleft()
if current_word == end:
return count
for i in range(length):
original_char = current_word[i]
for char_code in range(ord('a'), ord('z') + 1):
char = chr(char_code)
if char == original_char:
continue
next_word = current_word[:i] + char + current_word[i+1:]
if next_word in word_set and next_word not in visited:
visited.add(next_word)
queue.append((next_word, count + 1))
return 0
gemini-3.1-pro-preview ○(11.79秒 / 出力221トークン)
from collections import deque
def word_ladder_length(begin: str, end: str, word_list: list) -> int:
word_set = set(word_list)
if end not in word_set:
return 0
queue = deque([(begin, 1)])
if begin in word_set:
word_set.remove(begin)
while queue:
current_word, step = queue.popleft()
if current_word == end:
return step
for i in range(len(current_word)):
for c in 'abcdefghijklmnopqrstuvwxyz':
next_word = current_word[:i] + c + current_word[i+1:]
if next_word in word_set:
word_set.remove(next_word)
queue.append((next_word, step + 1))
return 0
H2(hard): coin_change_min(coins: list, amount: int) -> int
指定金額を作るために必要な最小の硬貨数を返す。作れない場合は-1を返す。
gemini-3.5-flash ○(3.51秒 / 出力136トークン)
def coin_change_min(coins: list, amount: int) -> int:
"""指定金額を作るために必要な最小の硬貨数を返す。作れない場合は-1を返す。"""
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for i in range(coin, amount + 1):
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
gemini-3.1-flash-lite ○(0.99秒 / 出力152トークン)
def coin_change_min(coins: list, amount: int) -> int:
"""指定金額を作るために必要な最小の硬貨数を返す。作れない場合は-1を返す。"""
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for i in range(coin, amount + 1):
if dp[i - coin] + 1 < dp[i]:
dp[i] = dp[i - coin] + 1
return int(dp[amount]) if dp[amount] != float('inf') else -1
gemini-3.1-pro-preview ○(5.5秒 / 出力130トークン)
def coin_change_min(coins: list, amount: int) -> int:
dp = [float('inf')] * (amount + 1)
dp[0] = 0
for coin in coins:
for i in range(coin, amount + 1):
if dp[i - coin] != float('inf'):
dp[i] = min(dp[i], dp[i - coin] + 1)
return dp[amount] if dp[amount] != float('inf') else -1
H3(hard): longest_increasing_subsequence(nums: list) -> int
最長狭義増加部分列の長さを返す。
gemini-3.5-flash ○(3.69秒 / 出力103トークン)
import bisect
def longest_increasing_subsequence(nums: list) -> int:
"""最長狭義増加部分列の長さを返す。"""
sub = []
for x in nums:
idx = bisect.bisect_left(sub, x)
if idx == len(sub):
sub.append(x)
else:
sub[idx] = x
return len(sub)
gemini-3.1-flash-lite ○(1.17秒 / 出力119トークン)
import bisect
def longest_increasing_subsequence(nums: list) -> int:
"""最長狭義増加部分列の長さを返す。"""
if not nums:
return 0
tails = []
for x in nums:
idx = bisect.bisect_left(tails, x)
if idx < len(tails):
tails[idx] = x
else:
tails.append(x)
return len(tails)
gemini-3.1-pro-preview ○(4.41秒 / 出力104トークン)
import bisect
def longest_increasing_subsequence(nums: list) -> int:
"""最長狭義増加部分列の長さを返す。"""
sub = []
for num in nums:
i = bisect.bisect_left(sub, num)
if i == len(sub):
sub.append(num)
else:
sub[i] = num
return len(sub)
H4(hard): trap_rain_water(heights: list) -> int
標高マップが与えられたとき、雨が降った後に溜まる水の総量を返す(Trapping Rain Water問題)。
gemini-3.5-flash ○(2.84秒 / 出力214トークン)
def trap_rain_water(heights: list) -> int:
"""標高マップが与えられたとき、雨が降った後に溜まる水の総量を返す(Trapping Rain Water問題)。"""
if not heights:
return 0
left, right = 0, len(heights) - 1
left_max, right_max = 0, 0
water = 0
while left < right:
if heights[left] < heights[right]:
if heights[left] >= left_max:
left_max = heights[left]
else:
water += left_max - heights[left]
left += 1
else:
if heights[right] >= right_max:
right_max = heights[right]
else:
water += right_max - heights[right]
right -= 1
return water
gemini-3.1-flash-lite ○(1.01秒 / 出力197トークン)
def trap_rain_water(heights: list) -> int:
"""標高マップが与えられたとき、雨が降った後に溜まる水の総量を返す(Trapping Rain Water問題)。"""
if not heights:
return 0
left, right = 0, len(heights) - 1
left_max, right_max = heights[left], heights[right]
water = 0
while left < right:
if left_max < right_max:
left += 1
left_max = max(left_max, heights[left])
water += left_max - heights[left]
else:
right -= 1
right_max = max(right_max, heights[right])
water += right_max - heights[right]
return water
gemini-3.1-pro-preview ○(7.59秒 / 出力166トークン)
def trap_rain_water(heights: list) -> int:
if not heights:
return 0
left, right = 0, len(heights) - 1
left_max, right_max = heights[left], heights[right]
water = 0
while left < right:
if left_max < right_max:
left += 1
left_max = max(left_max, heights[left])
water += left_max - heights[left]
else:
right -= 1
right_max = max(right_max, heights[right])
water += right_max - heights[right]
return water
H5(hard): edit_distance(word1: str, word2: str) -> int
word1をword2に変換するために必要な最小編集回数(挿入・削除・置換)を返す(レーベンシュタイン距離)。
gemini-3.5-flash ○(3.42秒 / 出力209トークン)
def edit_distance(word1: str, word2: str) -> int:
"""word1をword2に変換するために必要な最小編集回数(挿入・削除・置換)を返す(レーベンシュタイン距離)。"""
m, n = len(word1), len(word2)
dp = list(range(n + 1))
for i in range(1, m + 1):
prev = dp[0]
dp[0] = i
for j in range(1, n + 1):
temp = dp[j]
if word1[i - 1] == word2[j - 1]:
dp[j] = prev
else:
dp[j] = min(prev, dp[j], dp[j - 1]) + 1
prev = temp
return dp[n]
gemini-3.1-flash-lite ○(1.11秒 / 出力269トークン)
def edit_distance(word1: str, word2: str) -> int:
"""word1をword2に変換するために必要な最小編集回数(挿入・削除・置換)を返す(レーベンシュタイン距離)。"""
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1])
return dp[m][n]
gemini-3.1-pro-preview ○(12.62秒 / 出力275トークン)
def edit_distance(word1: str, word2: str) -> int:
"""word1をword2に変換するために必要な最小編集回数(挿入・削除・置換)を返す(レーベンシュタイン距離)。"""
m, n = len(word1), len(word2)
dp = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m + 1):
dp[i][0] = i
for j in range(n + 1):
dp[0][j] = j
for i in range(1, m + 1):
for j in range(1, n + 1):
if word1[i - 1] == word2[j - 1]:
dp[i][j] = dp[i - 1][j - 1]
else:
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) + 1
return dp[m][n]