2022年4月6日水曜日

今年は豊作になりますように

ロシアとウクライナが交戦中です。


ウクライナと言えば、世界有数の穀倉地帯です。小麦の供給が減ることになるでしょう。

今年はお米の生産量を増やした方が良いのかもしれません。


空腹はつらいことです。


2021年12月5日日曜日

4つの数字で10を作るアレ その1

 先日、久しぶりに電車に乗っていたら、"3,4,7,8から10を作れ"と書いてある広告を見かけました。

 昔は切符に4つの数字が書いてあったのでよくやったものです。ICカードになってからは全然やらないですが。


 さておき。パズルを解く気分で少し解いてみました。プログラミングの勉強には比較的良い題材だと思います。


やることは4つの数字から2つ選んで四則演算。それを繰り返して10を作るだけです。

- 1回目の計算:順番を考慮して4つから2つ選ぶ方法は12通り

- 2回目の計算:1回目の計算結果と残りの2つから2つを選ぶ方法は6通り

- 3回目の計算:残っている数字を選ぶ方法は2通り

- 四則演算:4通り

よって、すべての組み合わせは、12 * 4 * 6 * 4 * 2 * 4 = 9216通りです。たかだか1万通りくらいです。Excel VBAでリストアップしてみましょう。


 ソースコードはご自由にご利用ください。ただし、趣味のプログラムなので保証はありません。

------------------------------

Option Explicit


Dim row As Long

Dim col As Long


Const epsilon As Double = 0.000001


Public Sub Calc10()

    Sheet1.Cells.Clear

    

    row = 1

    col = 1

    

    Call Calc10A(1, 1, 9, 9)

End Sub


'演算子の表示

Public Function OperationText(op As Long) As String

    OperationText = ""

    

    If op = 0 Then

        OperationText = "'+"

    ElseIf op = 1 Then

        OperationText = "'-"

    ElseIf op = 2 Then

        OperationText = "'*"

    ElseIf op = 3 Then

        OperationText = "'/"

    End If

End Function


'演算

Public Function OperationCalc(op As Long, a0 As Double, a1 As Double) As Double

    OperationCalc = a0

    

    If op = 0 Then

        OperationCalc = a0 + a1

    ElseIf op = 1 Then

        OperationCalc = a0 - a1

    ElseIf op = 2 Then

        OperationCalc = a0 * a1

    ElseIf op = 3 Then

        If Math.Abs(a1) < epsilon Then

            OperationCalc = a0

        Else

            OperationCalc = a0 / a1

        End If

    End If

End Function


'a(0) - a(3)    :   0 - 9の数字4つ

'a(4)   :   1回目の演算結果

'a(5)   :   2回目の演算結果

'a(6)   :   3回目の演算結果

Public Sub Calc10A(a0 As Long, a1 As Long, a2 As Long, a3 As Long)

    '---------- 演算子選択のIndex ----------

    Dim i0 As Long

    Dim i1 As Long

    Dim i2 As Long

    

    '---------- 数字選択のIndex ----------

    Dim j0 As Long

    Dim j1 As Long

    Dim j2 As Long

    Dim j3 As Long

    Dim j4 As Long

    Dim j5 As Long

    

    '------------------------------

    Dim a(6) As Double

    

    a(0) = a0

    a(1) = a1

    a(2) = a2

    a(3) = a3

    a(4) = 0

    a(5) = 0

    a(6) = 0

    

    '---------- 1回目の演算 ----------

    For i0 = 0 To 3

        For j0 = 0 To 3

            For j1 = 0 To 3

                If j1 <> j0 Then

                

                    a(4) = OperationCalc(i0, a(j0), a(j1))

                                      

                    '---------- 2回目の演算 ----------

                    For i1 = 0 To 3

                        For j2 = 0 To 4

                            If j2 <> j0 And j2 <> j1 Then

                                For j3 = 0 To 4

                                    If j3 <> j0 And j3 <> j1 And j3 <> j2 Then

                                    

                                        a(5) = OperationCalc(i1, a(j2), a(j3))

                                                            

                                        '---------- 3回目の演算 ----------

                                        For i2 = 0 To 3

                                            For j4 = 0 To 5

                                                If j4 <> j0 And j4 <> j1 And j4 <> j2 And j4 <> j3 Then

                                                    For j5 = 0 To 5

                                                        If j5 <> j0 And j5 <> j1 And j5 <> j2 And j5 <> j3 And j5 <> j4 Then

                                                        

                                                            a(6) = OperationCalc(i2, a(j4), a(j5))

                                                            

                                                            '---------- 1回目の演算の表示 ----------

                                                            Sheet1.Cells(row, col + 0) = OperationText(i0)

                                                            Sheet1.Cells(row, col + 1) = a(j0)

                                                            Sheet1.Cells(row, col + 2) = a(j1)

                                                            col = col + 3

                                                            

                                                            If i0 = 3 And Math.Abs(a(j1)) < epsilon Then

                                                                Sheet1.Cells(row, col) = "Divided by Zero"

                                                                GoTo label

                                                            End If

                                                                

                                                            '---------- 2回目の演算の表示 ----------

                                                            Sheet1.Cells(row, col + 0) = OperationText(i1)

                                                            Sheet1.Cells(row, col + 1) = a(j2)

                                                            Sheet1.Cells(row, col + 2) = a(j3)

                                                            col = col + 3

                                                            

                                                            If i1 = 3 And Math.Abs(a(j3)) < epsilon Then

                                                                Sheet1.Cells(row, col) = "Divided by Zero"

                                                                GoTo label

                                                            End If

                                                            

                                                            '---------- 3回目の演算の表示 ----------

                                                            Sheet1.Cells(row, col + 0) = OperationText(i2)

                                                            Sheet1.Cells(row, col + 1) = a(j4)

                                                            Sheet1.Cells(row, col + 2) = a(j5)

                                                            col = col + 3

                                                            

                                                            If i2 = 3 And Math.Abs(a(j5)) < epsilon Then

                                                                Sheet1.Cells(row, col) = "Divided by Zero"

                                                                GoTo label

                                                            End If

                                                            

                                                            '------------------------------

                                                            Sheet1.Cells(row, 10) = a(6)

label:

                                                            row = row + 1

                                                            col = 1

                                                        End If

                                                    Next j5

                                                End If

                                            Next j4

                                        Next i2

                                        '------------------------------

                                    End If

                                Next j3

                            End If

                        Next j2

                    Next i1

                    '------------------------------

                End If

            Next j1

        Next j0

    Next i0

    '------------------------------

End Sub


4つの数字で10を作るアレ その2

  前回作成したマクロは、ループの書き方がカッコ悪い感じです。再帰関数を使って、少し見やすくしてみます。

 ついでに無駄な計算を少し省略します。以下の2つは省略可能です。

- 四則演算のうち、足し算と掛け算は可換なので半分は不要

- 1回目の計算と2回目の計算が交換できる場合も半分は不要


 ソースコードはご自由にご利用ください。ただし、趣味のプログラムなので保証はありません。

'------------------------------

Option Explicit


Dim row As Long

Dim col As Long


Const epsilon As Double = 0.000001




Public Sub Calc10()

    Sheet1.Cells.Clear

    

    row = 1

    col = 1

    

    Call Calc10B(3, 4, 7, 8)

End Sub


'演算子の表示
Public Function OperationText(op As Long) As String
    OperationText = ""
    
    If op = 0 Then
        OperationText = "'+"
    ElseIf op = 1 Then
        OperationText = "'-"
    ElseIf op = 2 Then
        OperationText = "'*"
    ElseIf op = 3 Then
        OperationText = "'/"
    End If
End Function

'演算
Public Function OperationCalc(op As Long, a0 As Double, a1 As Double) As Double
    OperationCalc = a0
    
    If op = 0 Then
        OperationCalc = a0 + a1
    ElseIf op = 1 Then
        OperationCalc = a0 - a1
    ElseIf op = 2 Then
        OperationCalc = a0 * a1
    ElseIf op = 3 Then
        If Math.Abs(a1) < epsilon Then
            OperationCalc = a0
        Else
            OperationCalc = a0 / a1
        End If
    End If
End Function


'再帰関数を使う場合

Public Sub Calc10B(a0 As Long, a1 As Long, a2 As Long, a3 As Long)

    Dim a(6) As Double

    Dim op(2) As Long   '演算子のIndex

    Dim nm(5) As Long   '数字のIndex

    

    a(0) = a0

    a(1) = a1

    a(2) = a2

    a(3) = a3

    a(4) = 0

    a(5) = 0

    a(6) = 0

    

    Call Calc10B_iterate(a, op, nm, 0)

End Sub


Public Sub Calc10B_iterate(ByRef a() As Double, ByRef op() As Long, ByRef nm() As Long, cnt As Long)

    Dim i As Long

    Dim j As Long

    Dim k As Long

    Dim l As Long

    

    If cnt < 3 Then

        '----- Operator -----

        For i = 0 To 3

            op(cnt) = i

            

            '----- 1st number -----

            For j = 0 To 3 + cnt

                For l = 0 To 2 * cnt - 1

                    If j = nm(l) Then

                        Exit For

                    End If

                Next l

            

                If l <> 2 * cnt Then

                    GoTo label0

                End If

                

                nm(l) = j

                    

                '----- 2nd number -----

                For k = 0 To 3 + cnt

                    For l = 0 To 2 * cnt

                        If k = nm(l) Then

                            Exit For

                        End If

                    Next l

                    

                    If l <> 2 * cnt + 1 Then

                        GoTo label1

                    End If

                    

                    nm(l) = k

                    

                    '演算子が交換可能なとき

                    If (i = 0 Or i = 2) And k < j Then

                        GoTo label1

                    End If

                    

                    '1回目の演算と2回目の演算が交換可能なとき

                    '((a0,a1),(a2,a3)), ((a0,a2),(a1,a3)), ((a0,a3),(a1,a2)) の組み合わせは計算する

                    '((a1,a2),(a0+a3)), ((a1,a3),(a0,a2)), ((a2,a3),(a0,a1)) の組み合わせは計算しない

                    If cnt = 1 Then

                        If (j = 0 And (k = 1 Or k = 2 Or k = 3)) _

                        Or ((j = 1 Or j = 2 Or j = 3) And k = 0) Then

                            GoTo label1

                        End If

                    End If

                    

                    '----- Operation -----

                    a(4 + cnt) = OperationCalc(i, a(j), a(k))

                    

                    '----------

                    Call Calc10B_iterate(a, op, nm, cnt + 1)

label1:

                Next k

                '----------

label0:

            Next j

            '----------

        Next i

        '----------

    ElseIf cnt = 3 Then

        Call Calc10B_result(a, op, nm)

    End If

End Sub


Public Sub Calc10B_result(ByRef a() As Double, ByRef op() As Long, ByRef nm() As Long)

        '---------- 1回目の演算 ----------

        Sheet1.Cells(row, col + 0) = OperationText(op(0))

        Sheet1.Cells(row, col + 1) = a(nm(0))

        Sheet1.Cells(row, col + 2) = a(nm(1))

        col = col + 3

        

        If op(0) = 3 And Math.Abs(a(nm(1))) < epsilon Then

            Sheet1.Cells(row, col) = "Divided by Zero"

            GoTo label

        End If

        

        '---------- 2回目の演算 ----------

        Sheet1.Cells(row, col + 0) = OperationText(op(1))

        Sheet1.Cells(row, col + 1) = a(nm(2))

        Sheet1.Cells(row, col + 2) = a(nm(3))

        col = col + 3

        

        If op(1) = 3 And Math.Abs(a(nm(3))) < epsilon Then

            Sheet1.Cells(row, col) = "Divided by Zero"

            GoTo label

        End If

        

        '---------- 3回目の演算 ----------

        Sheet1.Cells(row, col + 0) = OperationText(op(2))

        Sheet1.Cells(row, col + 1) = a(nm(4))

        Sheet1.Cells(row, col + 2) = a(nm(5))

        col = col + 3

        

        If op(2) = 3 And Math.Abs(a(nm(5))) < epsilon Then

            Sheet1.Cells(row, col) = "Divided by Zero"

            GoTo label

        End If

        

        '------------------------------

        Sheet1.Cells(row, col) = a(6)

label:

        row = row + 1

        col = 1

End Sub


4つの数字で10を作るアレ その3

 今回は、4つの数字の組み合わせ全てで計算してみます。

 0から9までの数字を4つ選ぶ方法は10000通りですが、順序の入れ替えを考慮すると715通りです(重複ありの組み合わせです)。715通りで10になるパターンを探すだけなので、計算は意外に重たくないです。

 今回は、再帰関数を使いませんでした。(ちなみに、再帰関数を使った場合、途中でループから抜けるのが、少し面倒です。)


 計算途中に小数が出てくる(3,4,7,8)(1,1,9,9)(1,3,3,7)のようなパターンは少し難しい感じです。


 ソースコードはご自由にご利用ください。ただし、趣味のプログラムなので保証はありません。


'------------------------------

 Option Explicit


Dim row As Long

Dim col As Long


Const epsilon As Double = 0.000001


Public Sub Calc10All()

    Dim a0 As Long

    Dim a1 As Long

    Dim a2 As Long

    Dim a3 As Long

    

    Sheet1.Cells.Clear

    

    row = 1

    col = 1


    For a0 = 0 To 9

        For a1 = a0 To 9

            For a2 = a1 To 9

                For a3 = a2 To 9

                    Sheet1.Cells(row, col + 0) = a0

                    Sheet1.Cells(row, col + 1) = a1

                    Sheet1.Cells(row, col + 2) = a2

                    Sheet1.Cells(row, col + 3) = a3

                    col = col + 4

                    

                    Call Calc10All_iterate(a0, a1, a2, a3)

                    row = row + 1

                    col = 1

                Next a3

            Next a2

        Next a1

    Next a0

End Sub


'演算子の表示

Public Function OperationText(op As Long) As String

    OperationText = ""

    

    If op = 0 Then

        OperationText = "'+"

    ElseIf op = 1 Then

        OperationText = "'-"

    ElseIf op = 2 Then

        OperationText = "'*"

    ElseIf op = 3 Then

        OperationText = "'/"

    End If

End Function




Public Sub Calc10All_iterate(a0 As Long, a1 As Long, a2 As Long, a3 As Long)

    '---------- 演算子選択のIndex ----------

    Dim i0 As Long

    Dim i1 As Long

    Dim i2 As Long

    

    '---------- 数字選択のIndex ----------

    Dim j0 As Long

    Dim j1 As Long

    Dim j2 As Long

    Dim j3 As Long

    Dim j4 As Long

    Dim j5 As Long

    

    '------------------------------

    Dim a(6) As Double

    

    a(0) = a0

    a(1) = a1

    a(2) = a2

    a(3) = a3

    a(4) = 0

    a(5) = 0

    a(6) = 0

    

    '---------- 1回目の演算 ----------

    For i0 = 0 To 3

        For j0 = 0 To 3

            For j1 = 0 To 3

                If j1 = j0 Then

                    GoTo label0

                End If

                

                a(4) = OperationCalc(i0, a(j0), a(j1))

                                  

                '---------- 2回目の演算 ----------

                For i1 = 0 To 3

                    For j2 = 0 To 4

                        If j2 = j0 Or j2 = j1 Then

                            GoTo label1

                        End If

                            

                        For j3 = 0 To 4

                            If j3 = j0 Or j3 = j1 Or j3 = j2 Then

                                GoTo label2

                            End If

                            

                            a(5) = OperationCalc(i1, a(j2), a(j3))

                                                

                            '---------- 3回目の演算 ----------

                            For i2 = 0 To 3

                                For j4 = 0 To 5

                                    If j4 = j0 Or j4 = j1 Or j4 = j2 Or j4 = j3 Then

                                        GoTo label3

                                    End If

                                    

                                    For j5 = 0 To 5

                                        If j5 = j0 Or j5 = j1 Or j5 = j2 Or j5 = j3 Or j5 = j4 Then

                                            GoTo label4

                                        End If

                                        

                                        a(6) = OperationCalc(i2, a(j4), a(j5))

                                        

                                        '------------------------------

                                        If Math.Abs(a(6) - 10) < epsilon Then

                                            Sheet1.Cells(row, col + 0) = OperationText(i0)

                                            Sheet1.Cells(row, col + 1) = a(j0)

                                            Sheet1.Cells(row, col + 2) = a(j1)

                                            col = col + 3

                                            

                                            Sheet1.Cells(row, col + 0) = OperationText(i1)

                                            Sheet1.Cells(row, col + 1) = a(j2)

                                            Sheet1.Cells(row, col + 2) = a(j3)

                                            col = col + 3

                                            

                                            Sheet1.Cells(row, col + 0) = OperationText(i2)

                                            Sheet1.Cells(row, col + 1) = a(j4)

                                            Sheet1.Cells(row, col + 2) = a(j5)

                                            col = col + 3

                                            

                                            Sheet1.Cells(row, col) = a(6)

                                            

                                            Exit Sub

                                        End If

                                        '------------------------------

label4:

                                    Next j5

label3:

                                Next j4

                            Next i2

                            '------------------------------

label2:

                        Next j3

label1:

                    Next j2

                Next i1

                '------------------------------

label0:

            Next j1

        Next j0

    Next i0

    '------------------------------

    Sheet1.Cells(row, 14) = "Not 10"

End Sub



2021年9月5日日曜日

理解の仕方

 「孫子の兵法」の話の続きです。

 私は、守屋先生の孫子の兵法を読んで以来、それが「孫子の兵法」だと思っていました。


 最近、ふと別の方の「孫子の兵法」に関する著作を読んで、人によって理解の仕方は違うものだなぁと思いました。


 まぁ、当たり前の話ですが。何が重要と考えるかは、人によって違って当然です。


 というわけで、私も自分が理解した内容をまとめてみました。


 かの曹操も、自ら「孫子の兵法」の注釈書を記したらしいです。読んでみたら、また別の発見があって面白いかもしれません。


兵法に学ぶ 4 --- 終わり方 ---

------------------------------

 一般に、物事の終結というのは難しいものです。名君が後継の育成に失敗するという逸話はよくあります。株なんかも、勢いがあって儲かっているときはいいですが、そこからの引き際は非常に難しいと聞きます。


 「孫子の兵法」においては、国家・社会が存続するなら、一つの戦いの終わりは、次への始まりです。つまり準備の段階に戻るだけです。

 きちんとした情報収集と情勢判断ができれば、適切な終結も見えてくるはずです。


------------------------------

「窮寇には迫ることなかれ」「囲師には必ずかき」

 追い込まれた敵は必死の抵抗をしてきます。「窮鼠猫を噛む」という言葉の通りです。死にものぐるいの反撃を受けるくらいなら、あえて敵の逃げ道を作った方が得策でしょう。敵を誘導しているなら、主導権はこちらにあります。


------------------------------

 近頃、正論で相手を論破するというのが流行している気がします。

 相手を論破するというのは派手な勝利に見えますが、追い込まれた相手が無茶苦茶な反撃をすることも考えられます。相手の逃げ道を用意して誘導するのが、兵法に則った戦略だと思います。

 敢えて敵を作る必要はありません。


------------------------------

 古の名将・名君は、一つの勝利に浮かれることなく、先を見越して、次の戦いの準備を進めていたと思います。

 「勝って兜の緒を締めよ」ということです。


兵法に学ぶ 3 --- 行動について ---

------------------------------

「疾きこと風のごとし」

 戦いに勝つための行動の基本は、素早く、勢いよくです。

 第2次大戦時のドイツの電撃作戦しかり、古今東西の戦争では、素早く、勢いのある戦法が勝利しています。


------------------------------

「兵は拙速を聞くも、いまだ巧の久しきをみざるなり」

 拙速というのは、速くても拙い(まずい)ということです。分かり易くパターンを書き出してみると、

 1. 速くて、まずい

 2. 速くて、うまい

 3. 遅くて、まずい

 4. 遅くて、うまい

の4パターンが考えられます (ラーメン屋ではありませんが)。このうち、4.のパターンは基本的にはあり得ません。遅い=時間がかかる、というのは、それだけで消耗しているからです。2.が最良、1が次善であれば、とにかく速く行動することは、望ましいことです。


 もちろん、速ければ何でもいいわけではありません。「急いてはことを仕損じる」という言葉もあります。どんなに行動が速くても、内容が良くなければ、やっぱり失敗です。

 目指すべきは速くて巧いです。


 言い換えるなら、夏休みの宿題は早々に手を付けましょう、ということです。


------------------------------

「人を致して人に致されず」

 後手後手の対策には、スピードもなく、勢いもありません。主導権を取って、先手で行動することが大事です。

 事前の準備が整っていれば、必ず先手の行動がとれるはずです。


------------------------------

「死地に陥れて然る後に生く」

 "背水の陣"というのがあります。自軍をあえて危機的な状況に追い込み、兵を死にものぐるいで戦わせる、という戦術です。死にものぐるいなのだから、勢いはあるでしょう。


 社員に危機感を煽ろうとする経営者がいたりします。背水の陣のやり方でしょう。がむしゃらに働けば、活路が見いだされるかもしれません。


 ただし、常に危機感を煽ってばかりでは、社員が疲弊するだけです。社員が疲弊していたら、勢いが出ません。


 また、危機感だけ煽っても、取り組むべき課題が見えていなければ、迷走するだけです。やはり勢いが出ません。これは状況分析が不足しています。


------------------------------

 話はそれますが、兵法書の呉子には、"将軍は手続きを簡素化すべき"、といったことが書かれているそうです。

 行動を起こすときに、煩雑な手続きがあると、勢いがそがれてしまいます。「孫子の兵法」と同じ思考だと思います。


 ・・・お役所の手続きも簡素化して欲しいものです。


------------------------------

「人をして慮ることを得ざらしむ」

 一度行動を起こしたら、その後は深く考えず、勢いに任せて突き進むのがうまいやり方です。


 「孫子の兵法」には、"作戦は将軍が考え、兵隊は何も考えないでいい"、とあります。少し悩ましいです。


 私が思うに、勢いのある行動は大事なんですが、兵隊は何も考えないでいいわけではないです。将軍が間違っていたら、何も考えないうちに全滅してしまいます。


 複雑化した現代社会において、必ずしもリーダーの選択が正しいとは限りません。リーダーの間違いに気づくためには、部下もしっかり考えた方がいいでしょう。