coLinux日記

coLinuxはフリーソフトを種として、よろずのシステムとぞなれりける。

入門者が生成AIも利用して Python プログラミングを行う。 その4

2024-10-06 16:10:01 | Python
前回までにすべての関数が作成できました。 後は、これらを集めてプログラミングするだけです。

Raspberry Pi が遅いので、最初に考えた検索のループで毎回 fill_list()を使って行リストを作るのは効率が悪そうです。1回だけ実行して、
f_R1,f_R2,f_R3,f_R4,f_R5,f_R6,f_R7,f_R8,f_R9
に代入することにしました。

更に、3行ずつブロックの検査をして、最後に検索するリスト数を減らしてから、列の検査をすることにしました。

プログラムはこんな感じです。

関数定義の部分は省略しました。

print('Question: ')
print_answer(R1,R2,R3,R4,R5,R6,R7,R8,R9)

# 条件を検査して、結果を出力する。
# fill_list()を使うので行の検査は終了している。

f_R1 = fill_list(R1)
f_R2 = fill_list(R2)
f_R3 = fill_list(R3)
f_R4 = fill_list(R4)
f_R5 = fill_list(R5)
f_R6 = fill_list(R6)
f_R7 = fill_list(R7)
f_R8 = fill_list(R8)
f_R9 = fill_list(R9)

right_b1 = []
for row1 in f_R1:
   for row2 in f_R2:
      for row3 in f_R3:
         if chk_block(row1,row2,row3) == False:
            continue
         right_b1.append([row1,row2,row3])
         print(row1)
         print(row2)
         print(row3)
         print('-'*28)
print(' 1-3 blocks')

right_b2 = []
for row4 in f_R4:
   for row5 in f_R5:
      for row6 in f_R6:
         if chk_block(row4,row5,row6) == False:
            continue
         right_b2.append([row4,row5,row6])
         print(row4)
         print(row5)
         print(row6)
         print('-'*28)
print(' 4-6 blocks')

right_b3 = []
for row7 in f_R7:
   for row8 in f_R8:
      for row9 in f_R9:
         if chk_block(row7,row8,row9) == False:
            continue
         right_b3.append([row7,row8,row9])
         print(row7)
         print(row8)
         print(row9)
         print('-'*28)
print(' 7-9 blocks')

print('Answer: ')
for bk1 in right_b1:
   for bk2 in right_b2:
      for bk3 in right_b3:
         if chk_col(bk1[0],bk1[1],bk1[2],bk2[0],bk2[1],bk2[2],bk3[0],bk3[1],bk3[2]) == False:
            continue

         # 答えを出力。複数の答えも想定する。
         print_answer(bk1[0],bk1[1],bk1[2],bk2[0],bk2[1],bk2[2],bk3[0],bk3[1],bk3[2])
         print(' ')

print('=== END ===')

メモリがあふれるかもと思いましたが、それは大丈夫でした。しかし、それでも遅い、最初の答えがでるまで半日、他に答えがないことを調べるため全部の検査にさらに1日くらいかかりました。遅いのでちゃんと動いているか不安になり、3つのブロック検査の途中経過も出力してあります。特に2段目(4-6 blocks) の処理が長かったです。

python3 sudoku.py
Question:
0 9 8 | 0 0 6 | 0 0 4
1 0 4 | 0 0 2 | 0 3 0
3 6 7 | 0 0 1 | 0 0 0
------+-------+-------
0 0 0 | 1 0 0 | 4 9 7
0 0 0 | 0 3 0 | 0 0 0
7 4 2 | 0 0 8 | 0 0 0
------+-------+-------
0 0 0 | 2 0 0 | 9 7 6
0 7 0 | 6 0 0 | 5 0 8
6 0 0 | 5 0 0 | 3 4 0
(途中省略)
[6, 9, 1, 5, 7, 8, 3, 4, 2]
----------------------------
[8, 5, 4, 2, 3, 1, 9, 7, 6]
[3, 7, 2, 6, 9, 4, 5, 1, 8]
[6, 9, 1, 5, 8, 7, 3, 4, 2]
----------------------------
7-9 blocks
Answer:
2 9 8 | 3 5 6 | 7 1 4
1 5 4 | 8 7 2 | 6 3 9
3 6 7 | 4 9 1 | 2 8 5
------+-------+-------
8 3 6 | 1 2 5 | 4 9 7
9 1 5 | 7 3 4 | 8 6 2
7 4 2 | 9 6 8 | 1 5 3
------+-------+-------
5 8 1 | 2 4 3 | 9 7 6
4 7 3 | 6 1 9 | 5 2 8
6 2 9 | 5 8 7 | 3 4 1

=== END ===
$

入門なので、自分で作る関数作成はなるべく単純にしてみました。そんなわけで同じ事を3回も繰り返したりして無駄に長くなっているかもしれませんが、後で分かりやすい事を優先しました。

生成AIも要所で使うことにして、2つの関数生成に使う事ができました。どれを生成AIに任せてどれを自分で作るかは色々と試す必要がありそうです。生成AIの回答の関数は入門者には勉強になる部分も多かったので、ちょっと関数を作るときに気になる事があるなら使って良いのかもしれませんね。途中怪しげなプログラムを提示されました、そこは、回答の通りテストする必要があるわけです。Google Gemini の場合は説明が良いですね。docstring 作成に使った方が良いような気がするのは以前書いた通りでした。

プロンプトに一言も入れていないのに「数独でしょ」と言ってどこかから持ってきた関数を提示するのも面白かったです。

入門者には、生成AIを関数生成とかclass定義とかで活用して、得られたものをテストしたり、その中で知らない機能を使っていたら調べたりする事が経験値を上げるには役立ちそうです。今後生成AIが「この関数はこっちの方が良いですよ。」と言ってくる事は当然ありそうなので、活用したいところです。

Raspberry Pi なので、遅いからスピードアップのために改良するという試行錯誤ができるので、Python入門時には何かと役立ちそうです。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

入門者が生成AIも利用して Python プログラミングを行う。 その3

2024-10-05 14:31:04 | Python
前回の Gemini へのプロンプトで最初の「Python で」が抜けていました。

さて、「数独」の3つめ条件、縦9列が1~9までのすべての数字を重複無く含んでいる、を調べる関数
chk_col(c1, c2, c3, c4, c5, c6, c7, c8, c9 ) を定義してみます。
単純に9つの行リストを引数に与えます。
(こちらも gemini は、docstring生成のみに使いました。その中で、Args: Returns: のみ使います。)

def chk_col(c1, c2, c3, c4, c5, c6, c7, c8, c9):
   """
   9つのリストからなる9x9の行列の各列に対して、`check_list()` 関数を適用します。
   全ての列で `check_list()` が `True` を返す場合にのみ、`True` を返します。
   Args:
      c1, c2, ..., c9 (list): 要素数9のリスト。それぞれが9x9行列の1つの列に対応します。
   Returns:
   bool: 全ての列が `check_list()` の条件を満たす場合にTrue、そうでない場合にFalse
   """

   for i in range(0,9):
      column=[c1[i],c2[i],c3[i],c4[i],c5[i],c6[i],c7[i],c8[i],c9[i]]
      if check_list(column) == False:
         return False
   return True

さて、各行の空白の要素を埋めるための関数fill_list()を Google Gemini に作成してもらおうとたところ中々うまく行きませんでした。 記録しなかったので詳しくは書けませんがrandom を使ったり、再帰関数にしたりして答えが正しくない関数を提案されました。

問題は、要素の値が0のインデックスを探して、まだ使っていない数値を求めて、値を埋めていくので、順列ということで、SimplePrograms で出てきた itertools ライブラリを使ってみるのも良さそうだと考えて、次のようなプロンプトにしました。

「Python で、要素数9のリストを A とします。A の各要素の値は1から9までの整数でかつ値が重複できないという条件があります。
今、A のいつかの要素に値が設定されており、まだ値が設定されていない要素の値は 0 になっているとします。この A を引数にして、A の値が0 の所に数値をいれて条件を満たすすべてのリストを求め、それぞれのリストを要素とするリストを返却するような関数fill_list(A) を作成してください。必要なら itertools ライブラリを使ってください。」

Gemini の回答は、以下の様にねらいどおりでした。(それが良いかどうかは別の話)

ここから======
## Pythonでリストを完成させる関数fill_list()の実装

### 問題の理解とアプローチ

* **問題:** 要素数9のリストAがあり、一部の要素に値が設定されている。残りの要素に1から9までの重複のない数値を全てのパターンで割り当て、全ての組み合わせのリストを作成する。
* **アプローチ:**
1. **未設定要素のインデックス:** まず、Aの中で値が0の要素のインデックスをリストで取得する。
2. **順列の生成:** itertools.permutations()を用いて、未設定要素に割り当てる数値の全ての順列を生成する。
3. **リストの生成:** 各順列に対して、元のリストAのコピーを作成し、未設定要素のインデックスに順列の値を順に代入していく。
4. **全ての組み合わせのリスト:** 生成された全てのリストを要素とするリストを返す。

### コードの実装

```python
import itertools

def fill_list(A):
   """
   Aの値が0の所に数値をいれて条件を満たすすべてのリストを返す関数
   Args:
      A: 要素数9のリスト。一部の要素に1から9までの値が設定されている。
   Returns:
      list: 条件を満たす全てのリストのリスト
   """
   # 未設定要素のインデックスを取得
   unset_indices = [i for i, x in enumerate(A) if x == 0]
   # 未設定要素に割り当てる数値の候補
   numbers = [i for i in range(1, 10) if i not in A]
   # 全ての順列を生成
   permutations_list = list(itertools.permutations(numbers))
   # 全ての組み合わせのリストを作成
   result = []
   for perm in permutations_list:
      new_list = A.copy()
      for i, num in zip(unset_indices, perm):
         new_list[i] = num
   result.append(new_list)

   return result
```

### コードの説明

* `unset_indices`: Aの中で値が0の要素のインデックスをリストで取得します。
* `numbers`: Aにまだ含まれていない1から9までの数値をリストで取得します。
* `permutations_list`: itertools.permutations()を用いて、numbersの全ての順列を生成し、リストに変換します。
* `result`: 最終的に返却する、全ての組み合わせのリストを格納する空のリストです。
* `new_list`: Aのコピーを作成し、ここに順列の値を代入していきます。
* `zip(unset_indices, perm)`: unset_indicesとpermの要素を同時に取り出すためにzip関数を使用しています。

### 使用例

```python
A = [1, 0, 3, 0, 5, 6, 0, 8, 9]
result = fill_list(A)
print(result)
```

このコードを実行すると、Aの0の要素に可能な全ての組み合わせで数値を代入したリストのリストが得られます。

### 注意点

* リストの長さが9であるという前提で実装されています。
* 0以外の数値が重複して含まれている場合は、意図した結果にならない可能性があります。
* リストの要素数が大きくなると、生成される組み合わせの数が膨大になり、計算時間が長くなる可能性があります。

このコードは、与えられた条件のもとで、Aの全ての組み合わせを効率的に生成することができます。

======ここまで


念のため、prog04.py というファイルを作って、テストします。最初に fill_list()を回答の通り定義して、テスト部分は、

A = [6, 2, 0, 5, 0, 7, 3, 4, 0]
print(A)
print('-'*28)

result = fill_list(A)

for l in result:
print(l)

です。

$ python3 prog04.py
[6, 2, 0, 5, 0, 7, 3, 4, 0]
----------------------------
[6, 2, 1, 5, 8, 7, 3, 4, 9]
[6, 2, 1, 5, 9, 7, 3, 4, 8]
[6, 2, 8, 5, 1, 7, 3, 4, 9]
[6, 2, 8, 5, 9, 7, 3, 4, 1]
[6, 2, 9, 5, 1, 7, 3, 4, 8]
[6, 2, 9, 5, 8, 7, 3, 4, 1]
$

正しい答えが得られるようになりました。
この回答の中では、SimplePrograms で学んだ enumerate()、リスト内包表記、docstring が使われています。
出てこなかった、リストデータ型のメソッド append() と組み込み関数 zip() を調べて見ました。
https://docs.python.org/ja/3/tutorial/datastructures.html

list.append(x)

   リストの末尾に要素を一つ追加します。a[len(a):] = [x] と等価です。


https://docs.python.org/ja/3/library/functions.html#zip

zip(*iterables, strict=False)

   複数のイテラブルを並行に反復処理し、各イテラブルの要素からなるタプルを生成します。

   以下はプログラム例です:
   >>>

for item in zip([1, 2, 3], ['sugar', 'spice', 'everything nice']):
   print(item)

(1, 'sugar')
(2, 'spice')
(3, 'everything nice')

より正式な定義: zip() は、 i 番目のタプルが 引数に与えた各イテラブルの i 番目の要素を含むような、タプルのイテレータを返します。


itertoolsライブラリでは、permutations()を使っています。
https://docs.python.org/ja/3/library/itertools.html#itertools.permutations

itertools.permutations(iterable, r=None)

   Return successive r length permutations of elements from the iterable.

   r が指定されない場合や None の場合、r はデフォルトで iterable の長さとなり、可能な最長の順列の全てが生成されます。


この問題にぴったりの組合せイテレータですね。

最後にを出力するときにちょっと数独ぽくしたいので、すべての条件を満たした答えを出力する関数print_answer() を作成します。これは問題の出力にも使えますね。(docstring は Gemini によります。)

def print_answer(a,b,c,d,e,f,g,h,i):
   """Prints a 9x9 Sudoku puzzle in a formatted manner.
   Args:
      a, b, c, d, e, f, g, h, i: Lists representing the rows of the Sudoku puzzle.
   """

   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7],b[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(c[0],c[1],c[2],c[3],c[4],c[5],c[6],c[7],c[8]))
   print('------+-------+-------')
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(d[0],d[1],d[2],d[3],d[4],d[5],d[6],d[7],d[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(e[0],e[1],e[2],e[3],e[4],e[5],e[6],e[7],e[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8]))
   print('------+-------+-------')
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(g[0],g[1],g[2],g[3],g[4],g[5],g[6],g[7],g[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(h[0],h[1],h[2],h[3],h[4],h[5],h[6],h[7],h[8]))
   print('{0} {1} {2} | {3} {4} {5} | {6} {7} {8}'.format(i[0],i[1],i[2],i[3],i[4],i[5],i[6],i[7],i[8]))

何故、Gemini の回答である docstring が英文になったのでしょうか?
これは、Gemini が次のようにこの関数の修正案を提示したからです。入門者にとって衝撃の答えでした。

ここから======(空行は省略)
I'd be glad to provide a response that combines the strengths of both Response A and Response B, addressing their potential shortcomings and incorporating insights from the ratings:

**Improved Response:**
```python
def print_answer(a, b, c, d, e, f, g, h, i):
   """Prints a 9x9 Sudoku puzzle in a formatted manner.
   Args:
      a, b, c, d, e, f, g, h, i: Lists representing the rows of the Sudoku puzzle.
   """
   # Define a separator string for better readability
   separator = "------+-------+-------"
   # Print the puzzle rows with appropriate formatting
   for row in [a, b, c, d, e, f, g, h, i]:
      print(" ".join(row))
      print(separator)
   # Print the final row without the separator
   print(" ".join(i))
```
**Explanation:**
- **Clear Function Definition:** The function is defined with a clear name (`print_answer`) and a docstring that explains its purpose and parameters.
- **Concise and Efficient:** The code is concise and efficient, using a loop to iterate over the rows and a list comprehension to join row elements with spaces.
- **Readability:** The use of a separator string improves readability by visually separating the rows and columns.
- **Flexibility:** The code can be easily adapted to print different puzzle sizes by modifying the separator string and the loop iteration.
- **Correctness:** The code accurately prints the Sudoku puzzle in the desired format.
**Key Improvements:**
- **Combined Strengths:** The response incorporates the best aspects of Response A (conciseness, efficiency) and Response B (clear function definition, docstring).
- **Enhanced Readability:** The separator string enhances visual clarity.
- **Flexibility:** The code is adaptable to different puzzle sizes.

I believe this improved response effectively addresses the prompt and provides a valuable solution.
======ここまで


Gemini は、この関数を数独パズルのためと評価して、その出力ならこの関数をと言うわけですね。
Gemini でこの回答を日本語に訳したものも記録しておきます。(空行は省略)

ここから======
**日本語訳:**
```
def print_answer(a, b, c, d, e, f, g, h, i):
   """9x9の数のパズル (数独) をきれいに表示する関数です。
   引数:
      a, b, c, d, e, f, g, h, i: 数独のパズルの各行を表すリストです。
   """
   # 行と行の間の区切り線として使う文字列
   separator = "------+-------+-------"
# パズルの各行をきれいに表示
   for row in [a, b, c, d, e, f, g, h, i]:
      print(" ".join(row)) # リストの要素をスペースで繋いで表示
      print(separator) # 区切り線を表示
   # 最後の行は区切り線を表示せずに表示
   print(" ".join(i))
```
**コードの説明:**
* **関数 `print_answer`:** 数独のパズルをきれいに表示するための関数です。
* **引数:**
   * `a`, `b`, `c`, ... , `i`: 数独のパズルの各行を表すリストです。それぞれのリストの要素は、その行の数字に対応します。
* **`separator`:** 行と行の間の区切り線として使う文字列です。
* **`for` ループ:** パズルの各行 (`a`, `b`, ... , `i`) を順番に処理します。
* **`print(" ".join(row))`:** リスト `row` の要素をスペースで繋いで一つの文字列にし、それを表示します。これにより、一行の数字が綺麗に表示されます。
* **`print(separator)`:** 区切り線を表示します。
* **最後の `print(" ".join(i))`:** 最後の行は区切り線を表示せずに表示します。
**このコードは、与えられた9つのリスト (各リストが数独の一行を表す) を使って、数独のパズルをコンソール上に綺麗に表示します。**
### より詳しい説明
* **数独:** 9×9のマス目に1から9までの数字を重複なく入れるパズルです。各行、各列、および3×3のブロック内に同じ数字が重複してはいけません。
* **リスト:** Pythonで複数の要素を順番に格納するデータ構造です。このコードでは、各リストが数独の一行に対応しています。
* **`join` メソッド:** リストの要素を指定された文字列で繋いで一つの文字列にするメソッドです。このコードでは、スペースで繋いでいます。
**このコードのポイント:**
* **可読性:** 変数名やコメントによって、コードの意味が分かりやすくなっています。
* **効率性:** `for` ループを使って簡潔に記述されています。
* **汎用性:** 数独のサイズを変更したり、区切り線の文字を変更したりするなど、少し変更するだけで他のパズルにも適用できます。

**もし、数独のパズルを解くプログラムを作りたい場合は、このコードをベースに、数字の配置のルールや解法アルゴリズムを実装する必要があります。**
======ここまで


生成AIを利用したプログラミングは、なかなか興味深いですね。
Gemini の回答に驚いて非常に長くなったので、続きは次回に投稿します。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

入門者が生成AIも利用して Python プログラミングを行う。 その2

2024-10-04 20:49:43 | Python
前回の続きで、具体的なプログラミングを行ってみます。

まず、Google Gemini を使って9要素のリストが1~9のすべての数字を含んでいるかどうかを調べる関数check_list()を作ります。

Gemini へのプロンプトは、以下の通りです。

「要素数9のリスト b を引数として、
リストの各要素が重複しないで1から9の値になっているかどうかを調べて、この条件を満たすときは真、満たさない場合は偽を返すような関数 check_list(b)を作成してください。」

Gemini の回答: (抜粋)
## リストの要素が重複なく1から9まで含まれているか判定する関数

def check_list(b):
   """
   リストbの要素が重複なく1から9まで含まれているかを判定する関数
   Args:
      b: 判定対象のリスト
   Returns:
      bool: 条件を満たす場合はTrue、満たさない場合はFalse
   """

   # 要素数が9でない場合、Falseを返す
   if len(b) != 9:
      return False

   # 要素が1から9の範囲に含まれていないか、重複があるかを確認
   return set(b) == set(range(1, 10))

### コード解説

1. **関数定義:**
   * `check_list(b)`: リスト `b` を引数にとる関数。
2. **要素数チェック:**
   * `if len(b) != 9:`: リストの長さが9でない場合、条件を満たさないためFalseを返す。
3. **重複と範囲チェック:**
   * `return set(b) == set(range(1, 10))`:
      * `set(b)`: リスト `b` を集合に変換することで、重複を自動的に除去する。
      * `set(range(1, 10))`: 1から9までの数値の集合を作成する。
      * `==`: 両方の集合が等しいか(要素が完全に一致するか)を比較する。
      * 集合が等しい場合、リスト `b` の要素が重複なく1から9まで含まれていることを意味するため、Trueを返す。

### 動作原理

* **集合の利用:** 集合は重複した要素を持たないため、リストを集合に変換することで、要素の重複を簡単に検出できる。
* **範囲の比較:** `range(1, 10)` で生成した集合と、リスト `b` から作成した集合を比較することで、要素が1から9までの範囲に含まれているか、かつ重複がないかを同時に確認できる。

=====ここまで


docstring 付きですね。数字が順番に並んでいないので、set()を使ってリストを集合に替えて判定するのですね。
解説も分かりやすく、そのまま利用できます。

次に、「数独」の9個のブロックが1から9の数値を重複無く含んでいるかを調べるために、
3行毎に3回に分けて調べるようにします。一つでも False になると直ぐに終了して次に行くのと、長くなりそうなのでこうしてみました。

そのために、3つの行を引数にして条件を調べる chk_block(a,b,c) という関数を作ってみます。
これは、生成AIを使わないで、単純に次のようにしました。
(Gemini は、docstring作成にのみ使いました。Raises:とExamples:は省略)

def chk_block(a,b,c):
   """
   3つのリストを9要素ずつに分割し、各ブロックに対して `check_list` 関数を適用します。
   全てのブロックで `check_list` が `True` を返す場合にのみ、`True` を返します。
   Args:
      a (list): 要素数9のリスト
      b (list): 要素数9のリスト
      c (list): 要素数9のリスト
   Returns:
      bool: 全てのブロックが `check_list` の条件を満たす場合にTrue、そうでない場合にFalse
   """


   block1 = a[0:3] + b[0:3] + c[0:3]
   block2 = a[3:6] + b[3:6] + c[3:6]
   block3 = a[6:] + b[6:] + c[6:]

   if check_list(block1) == False:
      return False
   if check_list(block2) == False:
      return False
   if check_list(block3) == False:
      return False
   return True

この2つの関数をテストします。
ファイル prog-01.py で、2つの関数を定義して、その下に以下のテストプログラムを挿入して実行します。

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 8, 9, 1, 2, 3 ]
row3 = [7, 8, 9, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 8, 9, 1, 7, 3 ]
row3 = [7, 8, 9, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 2, 9, 1, 2, 3 ]
row3 = [7, 8, 9, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

row1 = [1, 2, 3, 4, 5, 6, 7, 8, 9 ]
row2 = [4, 5, 6, 7, 8, 9, 1, 2, 3 ]
row3 = [7, 8, 1, 1, 2, 3, 4, 5, 6 ]

if chk_block(row1, row2, row3):
print("OK")
else:
print("NG")

$ python3 prog01.py
OK
NG
NG
NG
$

テストで問題ないことが分かりました。
長くなりましたので、続きはまた後で投稿します。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

入門者が生成AIも利用して Python プログラミングを行う。 その1

2024-10-03 21:32:06 | Python
SimplePrograms を利用して、Pythonの入門も大体終了したので、実際にプログラミングしてみます。

プログラムしようと思ったのは「数独」です。

スマートフォンを使っていると、この問題は解けるか?という宣伝広告が出てくることがあります。いくつか候補を試して進めるしかないような場合があって鬱陶しいので、それをプログラムで解いてしまおうとする試みです。本当に解が1つしかないのかなども試してみたいです。

今回は、問題を与えて解を求めるように作ってみます。最近、生成AIによるプログラミングも話題になっているので使ってみたいと思います。と言っても、全部作ってもらうと入門にならないので、手順はあらかじめ考えておいて、それを実現するような関数を生成AIに作ってもらい実現してみようと思いました。

考えた手順はこんな感じです。

初期化)
1)マスを横方向にみて、9個の要素を含むリストとみなし、上からR1、R2、...、R9 とします。
 各リストには1~9の数字が重複しないで入っている必要があります。
2)空白は0、初期の数字はそのまま(1から9の整数)R1~R9の各要素に代入して初期値にします。

数値挿入)
3)このリストrの0の要素に、残りの数値を入れたリスト(複数できる)をすべて生成するような
関数 fill_list(r)
を作ります。その返却値は、一つの要素が、要素数9個のリストである、リストです。

条件判定)
4)引数で与えたリストの要素が1)の条件を満たすリストかどうかを判定する
関数 check_list(list)
を作ります。
各行は条件をすでに条件をみたしているので、ブロックと列の判定にこれを使います。

答えを探す)
5)話を単純にするために、9重のforループを作り、条件を満たすかどうかを check_list() で判定します。こんな感じでしょうか。

for row1 in fill_list(R1):
   for row2 in fill_list(R2):
      for row3 in fill_list(R3):
         for row4 in fill_list(R4):
            for row5 in fill_list(R5):
               for row6 in fill_list(R6):
                  for row7 in fill_list(R7):
                     for row8 in fill_list(R8):
                        for row9 in fill_list(R9):

 チェックする量が膨大ですが、そのほとんどが条件を満たさないので、満たさなかったらcontinue文を使って直ぐ次に行くようにしたほうが良さそうです。答えが2つ以上ある場合もありそうですから。

列の判定)
6)列の判定は、row1 ~ row9 の各要素を先頭から取り出してマスの縦方向のカラムを表す
9個のリストに対してcheck_list()で判定して、
どれかが偽なら次のループへ、
すべてが真なら列は条件を満たしていることになります。

ブロックの判定)
7)ブロックの判定は、数独の9つのブロックをリストにして、そのリストに対してcheck_list()を実施して、
どれかが偽なら次のループへ、
すべてが真ならブロックの条件を満たしてることになります。
ループの一番内側で使うより、3つの行毎に判定するのが良さそうです。最初の3ブロックで条件を満たさなければ残りの6行を用意して判定する必要はありませんから。

6)、7)の順番は自由です。

こんな感じです。

生成AIは、各関数を作成するのに利用してみます。
上の手順の中で、check_list() と fill_list()の関数を作成してもらいたいと思います。

長くなるので、今後分割して投稿したいと思います。
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする