M.Hiroi's Home Page

Go Language Programming

Yet Another Golang Problems

[ PrevPage | Golang | NextPage ]

はじめに

このページは M.Hiroi が Go 言語の勉強で作成した簡単なプログラムをまとめたものです。元ネタは P-99: Ninety-Nine Prolog Problems で、拙作のページ Yet Another Prolog ProblemsYet Another Scheme Problems などの Go 言語バージョンになります。同じような問題が多くなると思いますが、あしからずご了承くださいませ。

●問題1

整数 n から m までの和、二乗の和、三乗の和を求める関数 sumOfInt, sumOfSquare, sumOfCube を定義してください。

func sumOfInt(n, m int) int
func sumOfSquare(n, m int) int
func sumOfCube(n, m int) int

解答

●問題2

素因数分解とは、素数でない整数 (合成数) を素数の積の形に書き表すことです。たとえば、12 は 2 * 2 * 3 と素因数分解することができます。素因数分解を行う関数 primeFactor を定義してください。結果は画面 (標準出力) へ出力するものとします。

func primeFactor(n int) 

解答

●問題3

下図に示すフィボナッチ関数 fibo を定義してください。

fibo(0) = 0
fibo(1) = 1
fibo(n) = fibo(n - 1) + fibo(n - 2)

0, 1, 1, 2, 3, 5, 8, 13 .... という直前の 2 項を足していく数列


    図 : フィボナッチ関数の定義
func fibo(n int) int

解答

●問題4

n 個の中から r 個を選ぶ組み合わせの数 nr を求める関数 combNum(n, r) を定義してください。

func combNum(n, r int) int

解答

●問題5

次の公式を使って平方根の整数部分を求める関数 sqrtInt を定義してください。

(1) 1 + 3 + 5 + ... + (2n - 1) = n2
(2) 1 + 3 + 5 + ... + (2n - 1) = n2 < m < 1 + 3 + ... (2n - 1) + (2n + 1) = (n + 1)2

式 (1) は、奇数 1 から 2n - 1 の総和は n2 になることを表しています。式 (2) のように、整数 m の値が n2 より大きくて (n + 1)2 より小さいのであれば、m の平方根の整数部分は n であることがわかります。これは m から奇数 1, 3, 5 ... (2n - 1), (2n + 1) を順番に引き算していき、引き算できなくなった時点の (2n + 1) / 2 = n が m の平方根になります。参考文献 [1] によると、この方法を「めのこ平方」と呼ぶそうです。

func sqrtInt(n int) int
sqrtInt(10) => 3
sqrtInt(100) => 10
sqrtInt(1000) => 31
sqrtInt(10000) => 100
sqrtInt(123456789) => 11111

解答

-- 参考文献 --------
[1] 仙波一郎のページ, 『平方根計算法 (PDF)』

●問題6

点を多角形の形に並べたとき、その総数を「多角数 (polygonal number)」といいます。三角形に配置したものを三角数、四角形に配置したものを四角数、五角形に配置したものを五角数といいます。三角数、四角数、五角数を下図に示します。

n 番目の p 角数を求める関数 polygonalNum を定義してください。

func polygonalNum(p, n int) int
polygonalNum(3, 1) => 1
polygonalNum(3, 2) => 3
polygonalNum(3, 3) => 6
polygonalNum(4, 1) => 1
polygonalNum(4, 2) => 4
polygonalNum(4, 3) => 9
polygonalNum(5, 1) => 1
polygonalNum(5, 2) => 5
polygonalNum(5, 3) => 12

解答

●問題7

配列 (スライス) に格納されている実数 (float64) の平均値と標準偏差を求める関数 meanSD を定義してください。

データを x1, x2, ... , xN とすると、総計量 (合計値) と平均値は次式で求めることができます。

総計量 T = x1 + x2 + ... + xN

            N
         = Σ xi
           i=1

平均値 M = (x1 + x2 + ... + xN) / N

                    N
         = (1/N) * Σ xi
                   i=1

平均値が同じ場合でも、データの特徴が異なる場合があります。たとえば、A = {4, 4, 5, 5, 5, 6, 6, 6, 7, 7} と B = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} の平均値は 5.5 になります。A のデータは平均値の近くに集まっていてますが、B のデータはバラバラになっていますね。統計学では、ばらつきの大きさを表すために「分散 (variance)」という値を使います。分散の定義を次に示します。

分散 S2 = ((x1 - M)2 + (x2 - M)2 + ... + (xN - M)2) / N

                   N
        = (1/N) * Σ (xi - M)2
                  i=1

標準偏差 S = √(S2)

分散の定義からわかるように、平均値から離れたデータが多いほど、分散の値は大きくなります。逆に、平均値に近いデータが多くなると分散は小さな値になります。そして、分散の平方根が「標準偏差 (SD : standard deviation)」になります。

リスト : テストデータ (乱数で作成した身長のデータ)

var height []float64 = []float64{
    148.7, 149.5, 133.7, 157.9, 154.2, 147.8, 154.6, 159.1, 148.2, 153.1,
    138.2, 138.7, 143.5, 153.2, 150.2, 157.3, 145.1, 157.2, 152.3, 148.3,
    152.0, 146.0, 151.5, 139.4, 158.8, 147.6, 144.0, 145.8, 155.4, 155.5,
    153.6, 138.5, 147.1, 149.6, 160.9, 148.9, 157.5, 155.1, 138.9, 153.0,
    153.9, 150.9, 144.4, 160.3, 153.4, 163.0, 150.9, 153.3, 146.6, 153.3,
    152.3, 153.3, 142.8, 149.0, 149.4, 156.5, 141.7, 146.2, 151.0, 156.5,
    150.8, 141.0, 149.0, 163.2, 144.1, 147.1, 167.9, 155.3, 142.9, 148.7,
    164.8, 154.1, 150.4, 154.2, 161.4, 155.0, 146.8, 154.2, 152.7, 149.7,
    151.5, 154.5, 156.8, 150.3, 143.2, 149.5, 145.6, 140.4, 136.5, 146.9,
    158.9, 144.4, 148.1, 155.5, 152.4, 153.3, 142.3, 155.3, 153.1, 152.3,
}
func meanSD(data []float64) (float64, float64)
meadSD(height) => 150.62699999999998 6.433472701426501

解答

●問題8

配列 (スライス) の部分列の中から最大値を求める関数 maximum と 最小値を求める関数 minimum を定義してください。maximum と minimum は値とその位置を返すものとします。また、部分列の指定はスライスと同じで、省略することができるものとします。

maximum(buff)        // buff[0:len(buff)] の中から最大値を求める
maximum(buff, s)     // buff[s:len(buff)] の中から最大値を求める
maximum(buff, s, e)  // buff[s:e] の中から最大値を求める
func maximum(buff []int, args ... int) (int, int)
func minimum(buff []int, args ... int) (int, int)

解答

●問題9

選択ソート (selection sort) は、ソートしていないデータの中から最小値(または最大値)を見つけ、それを先頭のデータと交換する、という手順を繰り返すことでソートを行います。最初は、すべてのデータの中から最小値を探し、それを配列の先頭 buff[0] と交換します。次は、buff[1] 以降のデータの中から最小値を探し、それを buff[1] と交換します。これを繰り返すことでソートすることができます。

 [9 5 3 7 6 4 8]   3 と 9 を交換する
  +   +

 3 [5 9 7 6 4 8]   5 と 4 を交換する
    +       +

 3 4 [9 7 6 5 8]   9 と 5 を交換する
      +     +

 3 4 5 [7 6 9 8]   7 と 6 を交換する
        + +

 3 4 5 6 [7 9 8]   7 と 7 を交換する
          +

 3 4 5 6 7 [9 8]   9 と 8 を交換してソート終了
            + +


        図 : 選択ソート

配列 (スライス) を選択ソートする関数 selectSort を定義してください。

func selectSort(buff []int) []int

解答

●問題10

バブルソート (buble sort) は泡がぶくぶくと浮いてくるように、いちばん小さいデータが後ろから前に浮かび上がってくるアルゴリズムです。隣接する 2 つのデータを比較して、順序が逆であれば入れ換えます。これを順番に後ろから前に行っていけば、いちばん小さなデータは頂上に浮かび上がっているというわけです。先頭が決まったならば、残りのデータに対して同じことを行えば、2 番目には残りのデータの中でいちばん小さいものが浮かび上がります。これをデータ数だけ繰り返せばソートが完了します。

 9 5 3 7 6 4 8   交換しない
           ~~~
 9 5 3 7 6 4 8   交換する
         ~~~
 9 5 3 7 4 6 8   交換する
       ~~~
 9 5 3 4 7 6 8   交換しない
     ~~~
 9 5 3 4 7 6 8   交換する
   ~~~
 9 3 5 4 7 6 8   交換する
 ~~~
 3 9 5 4 7 6 8   いちばん小さいデータが決定する
 +               残りのデータに対して同様な操作を行う


        図 : バブルソート

スライスをバブルソートする関数 bubleSort を定義してください。

func bubleSort(buff []int) []int

解答


●解答1

まず最初に、単純なループでプログラムを作ってみましょう。

リスト : 整数の和、二乗の和、三乗の和

func sumOfInt(n, m int) int {
    a := 0
    for ; n <= m; n++ {
        a += n
    }
    return a
}

func sumOfSquare(n, m int) int {
    a := 0
    for ; n <= m; n++ {
        a += n * n
    }
    return a
}

func sumOfCube(n, m int) int {
    a := 0
    for ; n <= m; n++ {
        a += n * n * n
    }
    return a
}

簡単なプログラムなので説明は不要ですね。ところで、数列の和を求める公式を使うと、プログラムはもっと簡単になります。

リスト : 別解

func sumOfInt0(n, m int) int {
    sum := func(x int) int { return x * (x + 1) / 2}
    return sum(m) - sum(n - 1)
}

func sumOfSquare0(n, m int) int {
    sum := func(x int) int {
        return x * (x + 1) * (2 * x + 1) / 6
    }
    return sum(m) - sum(n - 1)
}

func sumOfCube0(n, m int) int {
    sum := func(x int) int {
        return x * x * (x + 1) * (x + 1) / 4
    }
    return sum(m) - sum(n - 1)
}

公式を匿名関数でプログラムして、それを変数 sum にセットします。あとは sum(m) から sum(n - 1) を引き算すれば解を求めることができます。

●解答2

リスト : 素因数分解

func primeFactor(n int) {
    // 2 で割り算する
    for ; n % 2 == 0; n /= 2 {
        fmt.Print(2, " ")
    }
    // 奇数で割り算する
    for i := 3; i * i <= n; i += 2 {
        for ; n % i == 0; n /= i {
            fmt.Print(i, " ")
        }
    }
    if n > 1 {
        fmt.Println(n)
    }
}

最初に 2 で割り算します。それから、奇数で割り算していきます。割り算するときは、その数で割り切れるあいだは割り算を続けることに注意してください。たとえば、27 を素因数分解すると 3 * 3 * 3 になりますが、3 を一回だけしか割り算しないと、結果は 3 * 9 のように素数ではない数が含まれてしまいます。

簡単な実行例を示します。

primtFactor(12345678) => 2 3 3 47 14593
primtFactor(123456789) => 3 3 3607 3803
primtFactor(1234567890) => 2 3 3 5 3607 3803
primtFactor(1111111111) => 11 41 271 9091

なお、これはとても単純なアルゴリズムなので、大きな整数の素因数分解には適していません。巨大な合成数の素因数分解はとても難しい問題です。興味のある方は素因数分解について調べてみてください。

●解答3

フィボナッチ関数は再帰呼び出しを使えば簡単にプログラムできます。

リスト : フィボナッチ関数

func fibo(n int) int {
    if n < 2 {
        return n
    } else {
        return fibo(n - 1) + fibo(n - 2)
    }
}

関数 fibo は自分自身を 2 回呼び出しています。これを「二重再帰」といいます。fibo の呼び出しをトレースすると下図のようになります。


            図 : 関数 fibo のトレース

同じ値を何回も求めているため、関数 fibo の効率はとても悪いのです。この場合、二重再帰を「末尾再帰」に変換すると高速化することができます。そこで累算変数を使って、二重再帰を末尾再帰へ変換してみましょう。プログラムは次のようになります。

リスト : フィボナッチ関数(末尾再帰)

func fiboSub(n, a1, a2 int) int {
    if n < 1 {
        return a1
    } else {
        return fiboSub(n - 1, a2, a1 + a2)
    }
}

func fiboi(n int) int {
    return fiboSub(n, 0, 1)
}

関数 fiboSub の累算変数 a1 と a2 の使い方がポイントです。現在のフィボナッチ数を変数 a1 に、ひとつ先の値を変数 a2 に格納しておきます。あとは a1 と a2 を足し算して、新しいフィボナッチ数を計算すればいいわけです。fiboSub の呼び出しを下図に示します。

fiboSub(5, 0, 1)
  fiboSub(4, 1, 1)
    fiboSub(3, 1, 2)
      fiboSub(2, 2, 3)
        fiboSub(1, 3, 5)
          fiboSub(0, 5, 8)
          => a1 の値 5 を返す
        => 5
      => 5
    => 5
  => 5
=> 5


  図 : 関数 fiboSub の呼び出し

二重再帰では、同じ値を何回も求めていたため効率がとても悪かったのですが、このプログラムでは無駄な計算を行っていないので、値を高速に求めることができます。もちろん、末尾再帰になっているので、末尾再帰最適化を行う処理系では、プログラムをより高速に実行することができます。

Go 言語の場合、末尾再帰最適化はサポートされていませんが、末尾再帰を繰り返しに変換することは簡単です。関数 fibo を繰り返しに変換すると次のようになります。

リスト : フィボナッチ関数(繰り返し)

func fibol(n int) int {
    a1, a2 := 0, 1
    for ; n > 0; n-- {
        a1, a2 = a2, a1 + a2
    }
    return a1
}

このように、末尾再帰は簡単に繰り返しに変換することができます。

●解答4

組み合わせの数 nr を (n, r) と表記します。(n, r) を求めるには、次の公式を使えば簡単です。

(n, r) = n * (n - 1) * (n - 2) * ... * (n - r + 1) / (1 * 2 * 3 * ... * r)

皆さんお馴染みの公式ですね。ところが、整数値の範囲が限られているプログラミング言語では、この公式を使うと乗算で「桁あふれ」を起こす恐れがあります。Go 言語の整数 (int, int64 など) は多倍長整数ではないので、この公式をそのままプログラムするわけにはいきません。そこで、次の公式を使うことにします。

(n, 0) = (n, n) = 1
(n, r) = (n, r - 1) * (n - r + 1) / r

この式は (n, r) と (n, r - 1) の関係を表しています。あとは階乗やフィボナッチ関数と同じように、再帰定義を使って簡単にプログラムできます。次のリストを見てください。

リスト : 組み合わせの数を求める

func combNum(n, r int) int {
    if n == r || r == 0 {
        return 1
    } else {
        return combNum(n, r - 1) * (n - r + 1) / r
    }
}

とても簡単ですね。ところで、M.Hiroi が使用している Go 言語の int は 64 bit なので、このプログラムでも桁あふれする場合があります。どこまで計算できるか試してみましょう。

combNum(32, 16) => 601080390
combNum(34, 17) => 2333606220
combNum(36, 18) => 9075135300
combNum(38, 19) => 35345263800
combNum(40, 20) => 137846528820
combNum(42, 21) => 538257874440
combNum(44, 22) => 2104098963720
combNum(46, 23) => 8233430727600
combNum(48, 24) => 32247603683100
combNum(50, 25) => 126410606437752
combNum(52, 26) => 495918532948104
combNum(54, 27) => 1946939425648112
combNum(56, 28) => 7648690600760440
combNum(58, 29) => 30067266499541040
combNum(60, 30) => 118264581564861424
combNum(62, 31) => -284401161134521734

Go 言語の場合、桁あふれが発生してもランタイムエラーは発生しません。もっと大きな値を計算したい場合はパッケージ math/big を使ってみてください。big には多倍長整数を扱う関数が多数用意されています。

●解答5

リスト : めのこ平方

func sqrtInt(n int) int {
    m := 1
    for ; n >= m; m += 2 {
        n -= m
    }
    return m / 2
}

アルゴリズムをそのままプログラムしただけなので、とくに難しいところは無いと思います。この方法はとても簡単ですが、数が大きくなると時間がかかるようになります。そこで、整数を 2 桁ずつ分けて計算していくことにします。次の図を見てください。

整数 6789 を 67 と 89 に分ける

1 + 3 + ... + 15 = 82 < 67

両辺を 100 倍すると 802 < 6700 < 6789

802 = 1 + 3 + ... + 159 (= 2 * 80 - 1)

161 + 163 < (6789 - 6400 = 389) < 161 + 163 + 165

整数 6789 を 67 と 89 に分けます。最初に 67 の平方根を求めます。この場合は 8 になり、82 < 67 を満たします。次に、この式を 100 倍します。すると、802 < 6700 になり、6700 に 89 を足した 6789 も 802 より大きくなります。802 は 1 から 159 までの奇数の総和であることはすぐにわかるので、6789 - 6400 = 389 から奇数 161, 163, ... を順番に引き算していけば 6789 の平方根を求めることができます。

プログラムは次のようになります。

リスト : めのこ平方 (改良版)

func sqrtIntSub(n, m int) int {
    for ; n >= m; m += 2 {
        n -= m
    }
    return m / 2
}

func sqrtInt1(n int) int {
    if n < 100 {
        return sqrtIntSub(n, 1)
    } else {
        m := 10 * sqrtInt1(n / 100)
        return sqrtIntSub(n - m * m, 2 * m + 1)
    }
}

sqrtInt1 は n の平方根の整数部分を求めます。n が 100 未満の場合は sqrtIntSub で平方根を求めます。これが再帰呼び出しの停止条件になります。n が 100 以上の場合は、n の下位 2 桁を取り除いた値 (n / 100) の平方根を sqrtInt1 で求め、その値を 10 倍して変数 m にセットします。そして、sqrIntSub で n - m * m から奇数 2 * m + 1, 2 * m + 3 ... を順番に引き算していって n の平方根を求めます。

●解答6

多角数の点の増分を表に示すと、次のようになります。

 n   三角数            四角数             五角数
---+-----------------------------------------------------------
 1 |  1                 1                  1
 2 |  3 = 1+2           4 = 1+3            5 = 1+4
 3 |  6 = 1+2+3         9 = 1+3+5         12 = 1+4+7
 4 | 10 = 1+2+3+4      16 = 1+3+5+7       22 = 1+4+7+10
 5 | 15 = 1+2+3+4+5    25 = 1+3+5+7+9     35 = 1+4+7+10+13
 6 | 21 = 1+2+3+4+5+6  36 = 1+3+5+7+9+11  51 = 1+4+7+10+13+16

      ・・・・・・      ・・・・・・・     ・・・・・

 n | n(n + 1) / 2      n^2                n(3n - 1) / 2

表を見ればお分かりのように、三角数は公差 1、四角数は公差 2、五角数は公差 3、p 角数は公差 p - 2 の等差数列の和になります。初項を a, 公差を d とすると、等差数列の和 Sn は次式で求めることができます。

Sn = n(2a + (n - 1)d) / 2

a = 1, d = p - 2 を代入して計算すると、多角数 Pp,n は次式で求めることができます。

Pp,n = ((p - 2)n^2 - (p - 4)n) / 2

この式を Go 言語でプログラムすると、次のようになります。

リスト : 多角数 (polygon.go)

func polygonalNum(p, n int) int {
    return ((p - 2) * n * n - (p - 4) * n) / 2
}
リスト : 簡単なテスト

    for p := 3; p <= 8; p++ {
        for i := 1; i <= 20; i++ {
            fmt.Print(polygonalNum(p, i), " ")
        }
        fmt.Println("")
    }

それでは実行してみましょう。

1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 210
1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400
1 5 12 22 35 51 70 92 117 145 176 210 247 287 330 376 425 477 532 590
1 6 15 28 45 66 91 120 153 190 231 276 325 378 435 496 561 630 703 780
1 7 18 34 55 81 112 148 189 235 286 342 403 469 540 616 697 783 874 970
1 8 21 40 65 96 133 176 225 280 341 408 481 560 645 736 833 936 1045 1160

p 角数の初項は 1 で、第 2 項は p になります。多角数 - Wikipedia によると、多角数にはいろいろな面白い性質があるようです。

●解答7

平均値と標準偏差を求めるプログラムは簡単です。次のリストを見てください。

リスト : 平均値と標準偏差

// 合計値を求める
func sum(buff []float64) float64 {
    a := 0.0
    for _, v := range buff {
        a += v
    }
    return a
}

// 平均値と標準偏差を求める
func meanSD(buff []float64) (float64, float64) {
    n := float64(len(buff))
    m := sum(buff) / n
    s := 0.0
    for _, x := range buff {
        s += (x - m) * (x - m)
    }
    return m, math.Sqrt(s / n)
}

プログラムは簡単なので、説明は不要でしょう。

参考文献 [2] によると、データを 1 回通読するだけで平均値と標準偏差 (分散) を求めることができるそうです。参考文献 [2] のプログラムを Go 言語で書き直すと、次のようになります。

リスト : 平均値と標準偏差 (2)

func meanSD2(buff []float64) (float64, float64) {
    n := float64(len(buff))
    m, s := 0.0, 0.0
    for i, x := range buff {
        x -= m
        m += x / float64(i + 1)
        s += float64(i) * x * x / float64(i + 1)
    }
    s = math.Sqrt(s / n)
    return m, s
}

関数 meanSD2 でデータ height の平均値と標準偏差を求めると、次のようになります。

150.62699999999998 6.433472701426506

平均値は 150.63 cm で、標準偏差は 6.43 cm になりました。

-- 参考文献 --------
[2] 奥村晴彦,『C言語による最新アルゴリズム事典』, 技術評論社, 1991

●解答8

リスト : 最大値と最小値

// 引数の取得
func getRange(buff, args []int) (int, int) {
    var s, e int
    switch len(args) {
    case 0:  s, e = 0, len(buff)
    case 1:  s, e = args[0], len(buff)
    default: s, e = args[0], args[1]
    }
    return s, e
}

// 最小値を求める
func minimum(buff []int, args ... int) (int, int) {
    s, e := getRange(buff, args)
    m := s
    for s++ ; s < e; s++ {
        if buff[s] < buff[m] {
            m = s
        }
    }
    return buff[m], m
}

// 最大値を求める
func maximum(buff []int, args ... int) (int, int) {
    s, e := getRange(buff, args)
    m := s
    for s++ ; s < e; s++ {
        if buff[s] > buff[m] {
            m = s
        }
    }
    return buff[m], m
}

部分列の範囲指定は minimum, maximum の可変長引数 args で受け取ります。そして、部分列の範囲は関数 getRange で求めます。args の長さが 0 ならば、始点が 0 で終点が len(buff) になり、args の長さが 1 ならば、始点が args[0] で終点が len(buff) になります。それ以外の場合は始点が args[0] で終点が args[1] です。

minimum と maximum は getRange を呼び出して始点と終点を変数 s, e にセットします。そして、始点 s の要素を仮の最小値 (または最大値) として、その位置を変数 m にセットします。あとは、s + 1 から e 未満の範囲で buff[m] と buff[s] を比較して、buff[s] が小さい (または大きい) 場合は m の値を s に更新します。最後に buff[m] と m を返します。

●解答9

リスト : 選択ソート

func selectSort(buff []int) []int {
    k := len(buff)
    for i := 0; i < k - 1; i++ {
        v, m := minimum(buff, i, k)
        buff[i], buff[m] = v, buff[i]
    }
    return buff
}

選択ソートは関数 minimum を使うと簡単です。データの個数を変数 k にセットします。最初のループで変数 i の値は 0 から k - 1 まで動きます。そして、minimum で buff[i] から buff[k - 1] までの中から最小値を探し、それを buff[i] と交換するだけです。

●解答10

リスト : バブルソート

func bubleSort(buff []int) []int {
    k := len(buff) - 1
    for i := 0; i < k; i++ {
        for j := k; j > i; j-- {
            if buff[j - 1] > buff[j] {
                buff[j - 1], buff[j] = buff[j], buff[j - 1]
            }
        }
    }
    return buff
}

最初のループで k 回 (データの個数 - 1) だけ繰り返します。2 番目のループで buff の後ろから前に向かって、確定していないデータを比較していき、もしも順番が逆になっていたら交換します。

選択ソートとバブルソートは簡単なアルゴリズムですが、データ数が多くなると時間がかかります。データ数を n とすると実行時間は n の 2 乗に比例します。選択ソートとバブルソートは遅いアルゴリズムなのです。


●プログラムリスト

//
// yagp01.go : Yet Another Golang Problems
//
//             Copyright (C) 2014-2021 Makoto Hiroi
//
package main

import (
    "fmt"
    "math"
)

// P01
func sumOfInt(n, m int) int {
    a := 0
    for ; n <= m; n++ {
        a += n
    }
    return a
}

func sumOfSquare(n, m int) int {
    a := 0
    for ; n <= m; n++ {
        a += n * n
    }
    return a
}

func sumOfCube(n, m int) int {
    a := 0
    for ; n <= m; n++ {
        a += n * n * n
    }
    return a
}

func sumOfInt0(n, m int) int {
    sum := func(x int) int { return x * (x + 1) / 2}
    return sum(m) - sum(n - 1)
}

func sumOfSquare0(n, m int) int {
    sum := func(x int) int {
        return x * (x + 1) * (2 * x + 1) / 6
    }
    return sum(m) - sum(n - 1)
}

func sumOfCube0(n, m int) int {
    sum := func(x int) int {
        return x * x * (x + 1) * (x + 1) / 4
    }
    return sum(m) - sum(n - 1)
}

// P02
func primeFactor(n int) {
    // 2 で割り算する
    for ; n % 2 == 0; n /= 2 {
        fmt.Print(2, " ")
    }
    // 奇数で割り算する
    for i := 3; i * i <= n; i += 2 {
        for ; n % i == 0; n /= i {
            fmt.Print(i, " ")
        }
    }
    if n > 1 {
        fmt.Println(n)
    }
}

// P03
func fibo(n int) int {
    if n < 2 {
        return n
    } else {
        return fibo(n - 1) + fibo(n - 2)
    }
}

func fiboSub(n, a1, a2 int) int {
    if n < 1 {
        return a1
    } else {
        return fiboSub(n - 1, a2, a1 + a2)
    }
}

func fiboi(n int) int {
    return fiboSub(n, 0, 1)
}

func fibol(n int) int {
    a1, a2 := 0, 1
    for ; n > 0; n-- {
        a1, a2 = a2, a1 + a2
    }
    return a1
}

// P04
func combNum(n, r int) int {
    if n == r || r == 0 {
        return 1
    } else {
        return combNum(n, r - 1) * (n - r + 1) / r
    }
}

// P05
func sqrtInt(n int) int {
    m := 1
    for ; n >= m; m += 2 {
        n -= m
    }
    return m / 2
}

func sqrtIntSub(n, m int) int {
    for ; n >= m; m += 2 {
        n -= m
    }
    return m / 2
}

func sqrtInt1(n int) int {
    if n < 100 {
        return sqrtIntSub(n, 1)
    } else {
        m := 10 * sqrtInt1(n / 100)
        return sqrtIntSub(n - m * m, 2 * m + 1)
    }
}

// P06
func polygonalNum(p, n int) int {
    return ((p - 2) * n * n - (p - 4) * n) / 2
}

// P07

// 身長のデータ
var height []float64 = []float64{
    148.7, 149.5, 133.7, 157.9, 154.2, 147.8, 154.6, 159.1, 148.2, 153.1,
    138.2, 138.7, 143.5, 153.2, 150.2, 157.3, 145.1, 157.2, 152.3, 148.3,
    152.0, 146.0, 151.5, 139.4, 158.8, 147.6, 144.0, 145.8, 155.4, 155.5,
    153.6, 138.5, 147.1, 149.6, 160.9, 148.9, 157.5, 155.1, 138.9, 153.0,
    153.9, 150.9, 144.4, 160.3, 153.4, 163.0, 150.9, 153.3, 146.6, 153.3,
    152.3, 153.3, 142.8, 149.0, 149.4, 156.5, 141.7, 146.2, 151.0, 156.5,
    150.8, 141.0, 149.0, 163.2, 144.1, 147.1, 167.9, 155.3, 142.9, 148.7,
    164.8, 154.1, 150.4, 154.2, 161.4, 155.0, 146.8, 154.2, 152.7, 149.7,
    151.5, 154.5, 156.8, 150.3, 143.2, 149.5, 145.6, 140.4, 136.5, 146.9,
    158.9, 144.4, 148.1, 155.5, 152.4, 153.3, 142.3, 155.3, 153.1, 152.3,
}

// 合計値を求める
func sum(buff []float64) float64 {
    a := 0.0
    for _, v := range buff {
        a += v
    }
    return a
}

func meanSD(buff []float64) (float64, float64) {
    n := float64(len(buff))
    m := sum(buff) / n
    s := 0.0
    for _, x := range buff {
        s += (x - m) * (x - m)
    }
    return m, math.Sqrt(s / n)
}

func meanSD2(buff []float64) (float64, float64) {
    n := float64(len(buff))
    m, s := 0.0, 0.0
    for i, x := range buff {
        x -= m
        m += x / float64(i + 1)
        s += float64(i) * x * x / float64(i + 1)
    }
    s = math.Sqrt(s / n)
    return m, s
}

// P08

// 引数の取得
func getRange(buff, args []int) (int, int) {
    var s, e int
    switch len(args) {
    case 0:  s, e = 0, len(buff)
    case 1:  s, e = args[0], len(buff)
    default: s, e = args[0], args[1]
    }
    return s, e
}

// 最小値を求める
func minimum(buff []int, args ... int) (int, int) {
    s, e := getRange(buff, args)
    m := s
    for s++ ; s < e; s++ {
        if buff[s] < buff[m] {
            m = s
        }
    }
    return buff[m], m
}

// 最大値を求める
func maximum(buff []int, args ... int) (int, int) {
    s, e := getRange(buff, args)
    m := s
    for s++ ; s < e; s++ {
        if buff[s] > buff[m] {
            m = s
        }
    }
    return buff[m], m
}

// P09
func selectSort(buff []int) []int {
    k := len(buff)
    for i := 0; i < k - 1; i++ {
        v, m := minimum(buff, i, k)
        buff[i], buff[m] = v, buff[i]
    }
    return buff
}

// P10
func bubleSort(buff []int) []int {
    k := len(buff) - 1
    for i := 0; i < k; i++ {
        for j := k; j > i; j-- {
            if buff[j - 1] > buff[j] {
                buff[j - 1], buff[j] = buff[j], buff[j - 1]
            }
        }
    }
    return buff
}

// 簡単なテスト
func main() {
    fmt.Println("----- P01 -----")
    fmt.Println(sumOfInt(1, 1000))
    fmt.Println(sumOfSquare(1, 1000))
    fmt.Println(sumOfCube(1, 1000))
    fmt.Println(sumOfInt0(1, 1000))
    fmt.Println(sumOfSquare0(1, 1000))
    fmt.Println(sumOfCube0(1, 1000))

    fmt.Println("----- P02 -----")
    primeFactor(12345678)
    primeFactor(123456789)
    primeFactor(1234567890)
    primeFactor(1111111111)

    fmt.Println("----- P03 -----")
    for i := 0; i <= 10; i++ {
        fmt.Println(i, ": ", fibo(i), fiboi(i), fibol(i))
    }
    for i := 31; i <= 40; i++ {
        fmt.Println(i, ": ",  fiboi(i), fibol(i))
    }

    fmt.Println("----- P04 -----")
    for i := 16; i <= 31; i++ {
        fmt.Println(i * 2, i, ": ", combNum(i * 2, i))
    }

    fmt.Println("----- P05 -----")
    fmt.Println("10", sqrtInt(10), sqrtInt1(10))
    fmt.Println("100", sqrtInt(100), sqrtInt1(100))
    fmt.Println("1000", sqrtInt(1000), sqrtInt1(1000))
    fmt.Println("123456789", sqrtInt(123456789), sqrtInt1(123456789))

    fmt.Println("----- P06 -----")
    for p := 3; p <= 8; p++ {
        for i := 1; i <= 20; i++ {
            fmt.Print(polygonalNum(p, i), " ")
        }
        fmt.Println("")
    }

    fmt.Println("----- P07 -----")
    fmt.Println(meanSD(height))
    fmt.Println(meanSD2(height))

    fmt.Println("----- P08 -----")
    a := []int{5,9,4,7,3,8,2,6,1,0}
    fmt.Println(minimum(a))
    fmt.Println(maximum(a))
    fmt.Println(minimum(a, 2))
    fmt.Println(maximum(a, 2))
    fmt.Println(minimum(a, 2, 7))
    fmt.Println(maximum(a, 2, 7))

    fmt.Println("----- P09 -----")
    b := []int{5,6,4,7,3,8,2,9,1,0}
    fmt.Println(selectSort(b))

    fmt.Println("----- P10 -----")
    c := []int{5,6,4,7,3,8,2,9,1,0}
    fmt.Println(bubleSort(c))

}

●実行結果

$ go run yagp01.go
----- P01 -----
500500
333833500
250500250000
500500
333833500
250500250000
----- P02 -----
2 3 3 47 14593
3 3 3607 3803
2 3 3 5 3607 3803
11 41 271 9091
----- P03 -----
0 :  0 0 0
1 :  1 1 1
2 :  1 1 1
3 :  2 2 2
4 :  3 3 3
5 :  5 5 5
6 :  8 8 8
7 :  13 13 13
8 :  21 21 21
9 :  34 34 34
10 :  55 55 55
31 :  1346269 1346269
32 :  2178309 2178309
33 :  3524578 3524578
34 :  5702887 5702887
35 :  9227465 9227465
36 :  14930352 14930352
37 :  24157817 24157817
38 :  39088169 39088169
39 :  63245986 63245986
40 :  102334155 102334155
----- P04 -----
32 16 :  601080390
34 17 :  2333606220
36 18 :  9075135300
38 19 :  35345263800
40 20 :  137846528820
42 21 :  538257874440
44 22 :  2104098963720
46 23 :  8233430727600
48 24 :  32247603683100
50 25 :  126410606437752
52 26 :  495918532948104
54 27 :  1946939425648112
56 28 :  7648690600760440
58 29 :  30067266499541040
60 30 :  118264581564861424
62 31 :  -284401161134521734
----- P05 -----
10 3 3
100 10 10
1000 31 31
123456789 11111 11111
----- P06 -----
1 3 6 10 15 21 28 36 45 55 66 78 91 105 120 136 153 171 190 210
1 4 9 16 25 36 49 64 81 100 121 144 169 196 225 256 289 324 361 400
1 5 12 22 35 51 70 92 117 145 176 210 247 287 330 376 425 477 532 590
1 6 15 28 45 66 91 120 153 190 231 276 325 378 435 496 561 630 703 780
1 7 18 34 55 81 112 148 189 235 286 342 403 469 540 616 697 783 874 970
1 8 21 40 65 96 133 176 225 280 341 408 481 560 645 736 833 936 1045 1160
----- P07 -----
150.62699999999998 6.433472701426501
150.62699999999998 6.433472701426506
----- P08 -----
0 9
9 1
0 9
8 5
2 6
8 5
----- P09 -----
[0 1 2 3 4 5 6 7 8 9]
----- P10 -----
[0 1 2 3 4 5 6 7 8 9]

初版 2014 年 2 月 23 日
改訂 2021 年 12 月 12 日

Copyright (C) 2014-2021 Makoto Hiroi
All rights reserved.

[ PrevPage | Golang | NextPage ]