M.Hiroi's Home Page

Lightweight Language

お気楽 Python3 プログラミング超入門

[ Home | Light | Python3 ]

●多重継承

----- 参考文献 -----
Patrick Henry Winston, Berthold Klaus Paul Horn, 『LISP 原書第 3 版 (1) (2)』, 培風館, 1992

●例外処理

●二分木

単純な二分木です。詳しい説明は拙作のページ Algorithms with Python 二分木とヒープ をお読みくださいませ。

リスト : 二分木 (tree.py)

# 節
class Node:
    def __init__(self, item, left = None, right = None):
        self.item = item
        self.left = left
        self.right = right

#
# 操作関数
#

# 探索
def search(node, x):
    while node is not None:
        if node.item == x: return True
        elif x < node.item: node = node.left
        else: node = node.right
    return False 

def search_min(node):
    while node.left is not None: node = node.left
    return node.item

def search_max(node):
    while node.right is not None: node = node.right
    return node.item

# 挿入
def insert(node, x):
    if node is None:
        return Node(x)
    elif x < node.item:
        node.left = insert(node.left, x)
    elif x > node.item:
        node.right = insert(node.right, x)
    return node

# 削除
def delete_min(node):
    if node.left is None: return node.right
    node.left = delete_min(node.left)
    return node

def delete_max(node):
    if node.right is None: return node.left
    node.right = delete_max(node.right)
    return node

def delete(node, x):
    if node is None: return node
    if node.item == x:
        if node.left is None: return node.right
        if node.right is None: return node.left
        node.item = search_min(node.right)
        node.right = delete_min(node.right)
    elif x < node.item:
        node.left = delete(node.left, x)
    else:
        node.right = delete(node.right, x)
    return node

# 巡回
def each(node):
    if node is not None:
        yield from each(node.left)
        yield node.item
        yield from each(node.right)

# 二分木
class Tree:
    def __init__(self):
        self.root = None

    # 挿入
    def insert(self, x):
        self.root = insert(self.root, x)

    # 探索
    def search(self, x):
        return search(self.root, x)

    def max(self):
        if self.root is None: return None
        return search_max(self.root)

    def min(self):
        if self.root is None: return None
        return search_min(self.root)

    # 削除
    def delete(self, x):
        self.root = delete(self.root, x)

    def delete_max(self):
        if self.root is not None: delete_max(self.root)
    
    def delete_min(self):
        if self.root is not None: delete_min(self.root)

    # 巡回
    def each(self): return each(self.root)

    # 空か?
    def is_empty(self): return self.root is None

    # 表示
    def __repr__(self):
        if self.root is None: return 'Tree()'
        s = 'Tree('
        for x in each(self.root):
            s += '{}, '.format(x)
        s = s[:-2]
        s += ')'
        return s

# テスト
if __name__ == '__main__':
    import random
    tree = Tree()
    data = [random.randint(0, 100) for x in range(10)]
    print(data)
    print(tree)
    print(tree.is_empty())
    for x in data: tree.insert(x)
    print(tree)
    print(tree.is_empty())
    print('max =', tree.max())
    print('min =', tree.min())
    print("delete max")
    tree.delete_max()
    print(tree)
    print("delete min")
    tree.delete_min()
    print(tree)
    for x in data:
        print('search', x, tree.search(x))
        print('delete', x)
        tree.delete(x)
        print('search', x, tree.search(x))
        print(tree)
[82, 37, 15, 7, 93, 75, 21, 71, 47, 25]
Tree()
True
Tree(7, 15, 21, 25, 37, 47, 71, 75, 82, 93)
False
max = 93
min = 7
delete max
Tree(7, 15, 21, 25, 37, 47, 71, 75, 82)
delete min
Tree(15, 21, 25, 37, 47, 71, 75, 82)
search 82 True
delete 82
search 82 False
Tree(15, 21, 25, 37, 47, 71, 75)
search 37 True
delete 37
search 37 False
Tree(15, 21, 25, 47, 71, 75)
search 15 True
delete 15
search 15 False
Tree(21, 25, 47, 71, 75)
search 7 False
delete 7
search 7 False
Tree(21, 25, 47, 71, 75)
search 93 False
delete 93
search 93 False
Tree(21, 25, 47, 71, 75)
search 75 True
delete 75
search 75 False
Tree(21, 25, 47, 71)
search 21 True
delete 21
search 21 False
Tree(25, 47, 71)
search 71 True
delete 71
search 71 False
Tree(25, 47)
search 47 True
delete 47
search 47 False
Tree(25)
search 25 True
delete 25
search 25 False
Tree()

●経路の探索

スタートからゴールまでの経路を求めるプログラムです。詳しい説明は拙作のページ Algorithms with Python 集合、グラフ、経路の探索 をお読みくださいませ。

リスト : 経路の探索 (keiro.py)

# 隣接リスト
adjacent = ((1, 2),    # 0
            (0, 2, 3), # 1
            (0, 1, 4), # 2
            (1, 4, 5), # 3
            (2, 3, 6), # 4
            (3,),      # 5
            (1,))      # 6

# 深さ優先探索
def depth_first_search(goal, path):
    p = path[-1]
    if p == goal:
        print(path)
    else:
        for x in adjacent[p]:
            if x in path: continue
            path.append(x)
            depth_first_search(goal, path)
            path.pop()

print("----- depth first search -----")
depth_first_search(6, [0])

# 幅優先探索
def breadth_first_search(start, goal):
    que = [[start]]
    while len(que) > 0:
        path = que.pop(0)
        p = path[-1]
        if p == goal:
            print(path)
        else:
            for x in adjacent[p]:
                if x in path: continue
                que.append(path + [x])

print("----- breadth first search -----")
breadth_first_search(0, 6)

# 反復深化
def iterative_deepening_search(start, goal):
    def dfs(goal, path, limit):
        if len(path) == limit:
            if path[-1] == goal: print(path)
        else:
            for x in adjacent[path[-1]]:
                if x in path: continue
                path.append(x)
                dfs(goal, path, limit)
                path.pop()
    for limit in range(1, 7):
        print("-----", limit, "-----")
        dfs(goal, [start], limit)

print("----- iterative deepening search -----")
iterative_deepening_search(0, 6)
----- depth first search -----
[0, 1, 2, 4, 6]
[0, 1, 3, 4, 6]
[0, 2, 1, 3, 4, 6]
[0, 2, 4, 6]
----- breadth first search -----
[0, 2, 4, 6]
[0, 1, 2, 4, 6]
[0, 1, 3, 4, 6]
[0, 2, 1, 3, 4, 6]
----- iterative deepening search -----
----- 1 -----
----- 2 -----
----- 3 -----
----- 4 -----
[0, 2, 4, 6]
----- 5 -----
[0, 1, 2, 4, 6]
[0, 1, 3, 4, 6]
----- 6 -----
[0, 2, 1, 3, 4, 6]

●簡単なプログラム (3)

簡単なパズルの解法プログラムです。パズルの説明は以下のページをお読みくださいませ。

#
# sample03.py : 簡単なプログラム (3)
#
#               Copyright (C) 2018 Makoto Hiroi
#

#
# 小町算
#
def calc_expr(expr):
    n = expr[0]
    for i in range(1, len(expr), 2):
        if expr[i] == '+':
            n += expr[i + 1]
        else:
            n -= expr[i + 1]
    return n

def print_expr(expr):
    for x in expr:
        print('{} '.format(x), end='')
    print('= 100')

def komachi(n, expr):
    if n == 10:
        if calc_expr(expr) == 100: print_expr(expr)
    else:
        komachi(n + 1, expr + ['+', n])
        komachi(n + 1, expr + ['-', n])
        komachi(n + 1, expr[:-1] + [expr[-1] * 10 + n])

print("----- komachi -----")
komachi(2, [1])

#
# N Queens Problem
#

def nqueens(n):
    def attack(q, qs):
        d = 1
        for i in range(len(qs), 0, -1):
            if q + d == qs[i - 1] or q - d == qs[i - 1]: return True
            d += 1
        return False           

    def nqueens_sub(qs):
        if len(qs) == n:
            print(qs)
        else:
            for x in range(1, n + 1):
                if x in qs or attack(x, qs): continue
                qs.append(x)
                nqueens_sub(qs)
                qs.pop()
    nqueens_sub([])

print("----- N Queens Problem -----")
for x in range(4, 9): nqueens(x)

#
# ナンバープレース
#

# 盤面の表示
def print_board(board):
    for xs in board:
        for n in xs: print(n, end = ' ')
        print('') 

# 条件のチェック
def check_number(x, y, n, board):
    for i in range(0, 9):
        if board[i][y] == n or board[x][i] == n: return False
    x1 = (x // 3) * 3
    y1 = (y // 3) * 3
    for i in range(0, 3):
        for j in range(0, 3):
            if board[x1 + i][y1 + j] == n: return False
    return True

# ナンバープレースの解法
def numplace(x, y, board):
    if y >= 9:
        print_board(board)
    elif x >= 9:
        numplace(0, y + 1, board)
    elif board[x][y]:
        numplace(x + 1, y, board)
    else:
        for n in range(1, 10):
            if not check_number(x, y, n, board): continue
            board[x][y] = n
            numplace(x + 1, y, board)
            board[x][y] = 0

# 問題 (出典: 数独 - Wikipedia の問題例)
sudoku_board = [
    [5, 3, 0,  0, 7, 0,  0, 0, 0],
    [6, 0, 0,  1, 9, 5,  0, 0, 0],
    [0, 9, 8,  0, 0, 0,  0, 6, 0],

    [8, 0, 0,  0, 6, 0,  0, 0, 3],
    [4, 0, 0,  8, 0, 3,  0, 0, 1],
    [7, 0, 0,  0, 2, 0,  0, 0, 6],

    [0, 6, 0,  0, 0, 0,  2, 8, 0],
    [0, 0, 0,  4, 1, 9,  0, 0, 5],
    [0, 0, 0,  0, 8, 0,  0, 7, 9]]

print('----- number place -----')
print_board(sudoku_board)
print('--------------------')
numplace(0, 0, sudoku_board)

#
# マスターマインド
#
import itertools

# bulls を数える
def count_bulls(xs, ys):
    c = 0
    for i in range(0, 4):
        if xs[i] == ys[i]: c += 1
    return c

# 同じ数の個数を数える
def count_same_numbers(xs, ys):
    c = 0
    for x in xs:
        if x in ys: c += 1
    return c

# 今までの質問と矛盾がないか
def check_query(code, qs):
    for q in qs:
        b = count_bulls(code, q[0])
        c = count_same_numbers(code, q[0]) - b
        if b != q[1] or c != q[2]: return False
    return True

# マスターマインドの解法
def master_mind(answer):
    query_table = []
    for code in itertools.permutations(range(0, 10), 4):
        if check_query(code, query_table):
            b = count_bulls(code, answer)
            c = count_same_numbers(code, answer) - b
            query_table.append((code, b, c))
            print('{}: {}, bulls = {}, cows = {}'.format(len(query_table), code, b, c))
            if b == 4:
                print('Good Job!!!')
                break

print('----- master mind -----')
master_mind([9,8,7,6])
master_mind([9,4,3,1])

#
# 水差し問題
#
MAX_A = 8
MAX_B = 5

# A を空にする
def move1(a, b): return 0, b

# A を満たす
def move2(a, b): return MAX_A, b

# A -> B
def move3(a, b):
    c = MAX_B - b
    if c >= a:
        return 0, a + b
    else:
        return a - c, b + c

# B を空にする
def move4(a, b): return a, 0

# B を満杯にする
def move5(a, b): return a, MAX_B

# B -> A
def move6(a, b):
    c = MAX_A - a
    if c >= b:
        return a + b, 0
    else:
        return a + c, b - c

def water_jug(g):
    que = [[(0, 0)]]
    while len(que) > 0:
        moves = que.pop(0)
        a, b = moves[-1]
        if a == g or b == g:
            print(moves)
            break
        for move_func in [move1, move2, move3, move4, move5, move6]:
            new_state = move_func(a, b)
            if new_state in moves: continue
            que.append(moves + [new_state])

print('----- water jug -----')
water_jug(4)
water_jug(1)

#
# 農夫と山羊と狼とキャベツの問題
#
def check_side(side):
    if 'wolf' in side and 'goat' in side: return False
    if 'goat' in side and 'cabbage' in side: return False
    return True

def move_boat(src, dst):
    src1 = src.difference({'farmer'})
    if check_side(src1):
        yield src1, dst.union({'farmer'})
    for x in ['goat', 'wolf', 'cabbage']:
        if x not in src: continue
        xs = {'farmer', x}
        src1 = src.difference(xs)
        if check_side(src1):
            yield src1, dst.union(xs)

def farmer():
    all = {'farmer', 'goat', 'wolf', 'cabbage'}
    que = [[(all, set())]]
    while len(que) > 0:
        move = que.pop(0)
        left, right = move[-1]
        if right == all:
            for xs in move: print(xs)
            break
        if 'farmer' in left:
            # left -> right
            for state in move_boat(left, right):
                if state not in move:
                    que.append(move + [state])
        else:
            # right -> right
            for r, l in move_boat(right, left):
                state = l, r
                if state not in move:
                    que.append(move + [state])
        
print('----- farmer -----')
farmer()

#
# ペグ・ソリティア (Hoppers)
#

# 定数
SIZE = 13
HOLE = 6
MAX_JUMP = 11

# 跳び先表 (del, to)
jump_table = (
    ((1, 2), (3, 6), (5, 10)),
    ((3, 5), (6, 11), (4, 7)),
    ((1, 0), (4, 6), (7, 12)),
    ((6, 9),),
    ((6, 8),),
    ((3, 1), (6, 7), (8, 11)),
    ((3, 0), (4, 2), (8, 10), (9, 12)),
    ((4, 1), (6, 5), (9, 11)),
    ((6, 4),),
    ((6, 3),),
    ((5, 0), (8, 6), (11, 12)),
    ((8, 5), (6, 1), (9, 7)),
    ((11, 10), (9, 6), (7, 2))
)

def print_move(moves):
    print('[{}, '.format(moves[0][0]), end='')
    p = moves[0][1]
    for a, b in moves[1:]:
        if a == p:
            print('{}, '.format(p), end='')
        else:
            print('{}][{}, '.format(p, a), end='')
        p = b
    print('{}]'.format(p))

def move_peg(board, from_, del_, to):
    board[from_] = 0
    board[del_] = 0
    board[to] = 1

def restore_peg(board, from_, del_, to):
    board[from_] = 1
    board[del_] = 1
    board[to] = 0

def hoppers():
    cnt = 0
    def ids(board, jc, limit, moves):
        nonlocal cnt
        if jc > limit: return
        if len(moves) == MAX_JUMP:
            if board[HOLE]:
                print_move(moves)
                cnt += 1
        else:
            for from_ in range(0, SIZE):
                if not board[from_]: continue
                for del_, to in jump_table[from_]:
                    if not board[del_] or board[to]: continue
                    if moves[-1][1] == from_:
                        new_jc = jc
                    else:
                        new_jc = jc + 1
                    moves.append((from_, to))
                    move_peg(board, from_, del_, to)
                    ids(board, new_jc, limit, moves)
                    moves.pop()
                    restore_peg(board, from_, del_, to)
    # 初手を 0 -> 6 に限定
    board = [1] * SIZE
    board[0] = 0
    board[3] = 0
    for limit in range(2, MAX_JUMP + 1):
        print("-----", limit, "-----")
        ids(board, 1, limit, [(0, 6)])
        if cnt > 0:
            print(cnt)
            break

print('----- Hoppers -----')
hoppers()

#
# 三目並べ
#

# 定数
MARU = 1
BATU = -1
FREE = 0 
MARU_WIN = 1
BATU_WIN = -1
DRAW = 0
MAX_VALUE = 2
MIN_VALUE = -2

# 直線
line = (
    (0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6),
    (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)
)

# 勝敗の判定
def check_winner(board):
    for a, b, c in line:
        piece = board[a]
        if piece != FREE and piece == board[b] == board[c]:
            if piece == MARU:
                return MARU_WIN
            else:
                return BATU_WIN
    return DRAW    

# 先手の指し手
def think_maru(board, n):
    value = MIN_VALUE
    for i in range(len(board)):
        if board[i] != FREE: continue
        board[i] = MARU
        v = check_winner(board)
        if v == DRAW and n < len(board) - 1:
            v = think_batu(board, n + 1)
        if v > value: value = v
        board[i] = FREE
    return value

# 後手の指し手
def think_batu(board, n):
    value = MAX_VALUE
    for i in range(len(board)):
        if board[i] != FREE: continue
        board[i] = BATU
        v = check_winner(board)
        if v == DRAW and n < len(board) - 1:
            v = think_maru(board, n + 1)
        if v < value: value = v
        board[i] = FREE
    return value

def tictactoe():
    board = [FREE] * 9
    for i in range(9):
        board[i] = MARU
        v = think_batu(board, 1)
        print(i, 'value = ', v)
        board[i] = FREE

print('----- tic tac toe -----')
tictactoe()
----- komachi -----
1 + 2 + 3 - 4 + 5 + 6 + 78 + 9 = 100
1 + 2 + 34 - 5 + 67 - 8 + 9 = 100
1 + 23 - 4 + 5 + 6 + 78 - 9 = 100
1 + 23 - 4 + 56 + 7 + 8 + 9 = 100
12 + 3 + 4 + 5 - 6 - 7 + 89 = 100
12 + 3 - 4 + 5 + 67 + 8 + 9 = 100
12 - 3 - 4 + 5 - 6 + 7 + 89 = 100
123 + 4 - 5 + 67 - 89 = 100
123 + 45 - 67 + 8 - 9 = 100
123 - 4 - 5 - 6 - 7 + 8 - 9 = 100
123 - 45 - 67 + 89 = 100
----- N Queens Problem -----
[2, 4, 1, 3]
[3, 1, 4, 2]
[1, 3, 5, 2, 4]
[1, 4, 2, 5, 3]
[2, 4, 1, 3, 5]
[2, 5, 3, 1, 4]
[3, 1, 4, 2, 5]
[3, 5, 2, 4, 1]
[4, 1, 3, 5, 2]
[4, 2, 5, 3, 1]
[5, 2, 4, 1, 3]
[5, 3, 1, 4, 2]
[2, 4, 6, 1, 3, 5]
[3, 6, 2, 5, 1, 4]
[4, 1, 5, 2, 6, 3]
[5, 3, 1, 6, 4, 2]
[1, 3, 5, 7, 2, 4, 6]
[1, 4, 7, 3, 6, 2, 5]
[1, 5, 2, 6, 3, 7, 4]

  ・・・省略・・・

[7, 3, 6, 2, 5, 1, 4]
[7, 4, 1, 5, 2, 6, 3]
[7, 5, 3, 1, 6, 4, 2]
[1, 5, 8, 6, 3, 7, 2, 4]
[1, 6, 8, 3, 7, 4, 2, 5]
[1, 7, 4, 6, 8, 2, 5, 3]

  ・・・省略・・・

[8, 2, 5, 3, 1, 7, 4, 6]
[8, 3, 1, 6, 2, 5, 7, 4]
[8, 4, 1, 3, 6, 2, 7, 5]
----- number place -----
5 3 0 0 7 0 0 0 0 
6 0 0 1 9 5 0 0 0 
0 9 8 0 0 0 0 6 0 
8 0 0 0 6 0 0 0 3 
4 0 0 8 0 3 0 0 1 
7 0 0 0 2 0 0 0 6 
0 6 0 0 0 0 2 8 0 
0 0 0 4 1 9 0 0 5 
0 0 0 0 8 0 0 7 9 
--------------------
5 3 4 6 7 8 9 1 2 
6 7 2 1 9 5 3 4 8 
1 9 8 3 4 2 5 6 7 
8 5 9 7 6 1 4 2 3 
4 2 6 8 5 3 7 9 1 
7 1 3 9 2 4 8 5 6 
9 6 1 5 3 7 2 8 4 
2 8 7 4 1 9 6 3 5 
3 4 5 2 8 6 1 7 9 
----- master mind -----
1: (0, 1, 2, 3), bulls = 0, cows = 0
2: (4, 5, 6, 7), bulls = 0, cows = 2
3: (5, 4, 8, 9), bulls = 0, cows = 2
4: (6, 7, 9, 8), bulls = 0, cows = 4
5: (8, 9, 7, 6), bulls = 2, cows = 2
6: (9, 8, 7, 6), bulls = 4, cows = 0
Good Job!!!
1: (0, 1, 2, 3), bulls = 0, cows = 2
2: (1, 0, 4, 5), bulls = 0, cows = 2
3: (2, 3, 5, 4), bulls = 0, cows = 2
4: (3, 4, 0, 6), bulls = 1, cows = 1
5: (3, 5, 6, 1), bulls = 1, cows = 1
6: (6, 5, 0, 2), bulls = 0, cows = 0
7: (7, 4, 3, 1), bulls = 3, cows = 0
8: (8, 4, 3, 1), bulls = 3, cows = 0
9: (9, 4, 3, 1), bulls = 4, cows = 0
Good Job!!!
----- water jug -----
[(0, 0), (0, 5), (5, 0), (5, 5), (8, 2), (0, 2), (2, 0), (2, 5), (7, 0), (7, 5), (8, 4)]
[(0, 0), (8, 0), (3, 5), (3, 0), (0, 3), (8, 3), (6, 5), (6, 0), (1, 5)]
----- farmer -----
({'wolf', 'farmer', 'goat', 'cabbage'}, set())
({'wolf', 'cabbage'}, {'farmer', 'goat'})
({'wolf', 'farmer', 'cabbage'}, {'goat'})
({'cabbage'}, {'wolf', 'farmer', 'goat'})
({'farmer', 'cabbage', 'goat'}, {'wolf'})
({'goat'}, {'wolf', 'farmer', 'cabbage'})
({'farmer', 'goat'}, {'wolf', 'cabbage'})
(set(), {'wolf', 'farmer', 'cabbage', 'goat'})
----- Hoppers -----
----- 2 -----
----- 3 -----
----- 4 -----
----- 5 -----
----- 6 -----
----- 7 -----
[0, 6][9, 3][2, 0, 6][11, 1][10, 0, 2, 6][8, 4][12, 2, 6]
[0, 6][9, 3][2, 0, 6][11, 1][10, 6][4, 8][12, 2, 0, 10, 6]
[0, 6][9, 3][2, 0, 6][11, 1][12, 2, 6][8, 4][10, 0, 2, 6]
[0, 6][9, 3][2, 6][8, 4][10, 0, 2, 6][7, 5][12, 10, 0, 6]
[0, 6][9, 3][2, 6][8, 4][10, 0, 2, 6][11, 1][12, 2, 0, 6]
[0, 6][9, 3][2, 6][8, 4][10, 0, 6][7, 5][12, 10, 0, 2, 6]
[0, 6][9, 3][2, 6][8, 4][12, 2, 0, 6][5, 7][10, 12, 2, 6]
[0, 6][9, 3][2, 6][8, 4][12, 2, 0, 6][11, 1][10, 0, 2, 6]
[0, 6][9, 3][2, 6][8, 4][12, 2, 6][5, 7][10, 12, 2, 0, 6]
[0, 6][9, 3][10, 0, 6][7, 5][2, 0, 10, 6][4, 8][12, 10, 6]
[0, 6][9, 3][10, 0, 6][7, 5][2, 6][8, 4][12, 10, 0, 2, 6]
[0, 6][9, 3][10, 0, 6][7, 5][12, 10, 6][4, 8][2, 0, 10, 6]
[0, 6][9, 3][10, 6][4, 8][2, 0, 6][11, 1][12, 2, 0, 10, 6]
[0, 6][9, 3][10, 6][4, 8][2, 0, 10, 6][7, 5][12, 10, 0, 6]
[0, 6][9, 3][10, 6][4, 8][2, 0, 10, 6][11, 1][12, 2, 0, 6]
[0, 6][9, 3][10, 6][4, 8][12, 10, 0, 6][1, 11][2, 12, 10, 6]
[0, 6][9, 3][10, 6][4, 8][12, 10, 0, 6][7, 5][2, 0, 10, 6]
[0, 6][9, 3][10, 6][4, 8][12, 10, 6][1, 11][2, 12, 10, 0, 6]
18
----- tic tac toe -----
0 value =  0
1 value =  0
2 value =  0
3 value =  0
4 value =  0
5 value =  0
6 value =  0
7 value =  0
8 value =  0

Copyright (C) 2018 Makoto Hiroi
All rights reserved.

[ Home | Light | Python3 ]